-
-
Notifications
You must be signed in to change notification settings - Fork 32.7k
Fixed #35957 -- Allowed auto fields to be used in composite primary keys. #19585
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
django/db/models/fields/__init__.py
Outdated
@@ -2792,14 +2792,24 @@ def __init__(self, *args, **kwargs): | |||
def check(self, **kwargs): | |||
return [ | |||
*super().check(**kwargs), | |||
*self._check_primary_key(), | |||
*self._check_primary_key(kwargs.get("databases", ["default"])), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One of the tests I saw was passing databases
kwarg into check()
but in another test it was absent. Is this a correct assumption here that databases
may be passed in?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should default to an empty list, see how CharField.check
does it
django/django/db/models/fields/__init__.py
Lines 1219 to 1225 in 3f59711
def check(self, **kwargs): | |
databases = kwargs.get("databases") or [] | |
return [ | |
*super().check(**kwargs), | |
*self._check_db_collation(databases), | |
*self._check_max_length_attribute(**kwargs), | |
] |
django/db/models/fields/__init__.py
Outdated
all( | ||
connections[db].features.supports_autofields_in_composite_pk | ||
for db in databases | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simon had recently mentioned that only checking the default connection's features was harmful in a multi-engine setup. Is this what we want to do moving forward or just continue to use the default connection? If so we may need to abstract this as it will catch folks out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose what I should instead be doing here is iterating over connections
in full 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also just wonder whether we may want to add something like this to Field
:
@cached_property
def part_of_pk(self):
return self.primary_key or self in getattr(self.model._meta.pk, "pk", [])
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this what we want to do moving forward or just continue to use the default connection? If so we may need to abstract this as it will catch folks out.
We should most definitely not use the default connection. If not specified the check should not be performed.
class AutoId(models.Model): | ||
pk = models.CompositePrimaryKey("id", "name") | ||
id = models.BigAutoField() | ||
name = models.CharField(max_length=255) | ||
|
||
class Meta: | ||
required_db_features = ["supports_autofields_in_composite_pk"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does someone know how to skip checks when required_db_features
is set? My tests were failing because it was still failing the check when run with SQLite.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refer to how Constraint.check
is implemented
e.g.
django/django/db/models/constraints.py
Lines 152 to 155 in 3f59711
if not ( | |
connection.features.supports_table_check_constraints | |
or "supports_table_check_constraints" in model._meta.required_db_features | |
): |
tests/composite_pk/tests.py
Outdated
@@ -220,6 +220,12 @@ def test_model_forms(self): | |||
): | |||
self.assertIsNone(modelform_factory(Comment, fields=["pk"])) | |||
|
|||
def test_autoid_in_composite_pk(self): | |||
obj = AutoId.objects.create(name="Automatically generated ID") | |||
self.assertEqual(obj.id, 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the correct way to check an auto field's initial value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assert it's not None
?
django/db/models/fields/__init__.py
Outdated
connections[db].features.supports_autofields_in_composite_pk | ||
for db in databases | ||
) | ||
and self in getattr(self.model._meta.pk, "fields", []) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and self in getattr(self.model._meta.pk, "fields", []) | |
and self in self.model._meta.pk_fields |
django/db/models/fields/__init__.py
Outdated
@@ -2792,14 +2792,24 @@ def __init__(self, *args, **kwargs): | |||
def check(self, **kwargs): | |||
return [ | |||
*super().check(**kwargs), | |||
*self._check_primary_key(), | |||
*self._check_primary_key(kwargs.get("databases", ["default"])), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should default to an empty list, see how CharField.check
does it
django/django/db/models/fields/__init__.py
Lines 1219 to 1225 in 3f59711
def check(self, **kwargs): | |
databases = kwargs.get("databases") or [] | |
return [ | |
*super().check(**kwargs), | |
*self._check_db_collation(databases), | |
*self._check_max_length_attribute(**kwargs), | |
] |
django/db/models/fields/__init__.py
Outdated
all( | ||
connections[db].features.supports_autofields_in_composite_pk | ||
for db in databases | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this what we want to do moving forward or just continue to use the default connection? If so we may need to abstract this as it will catch folks out.
We should most definitely not use the default connection. If not specified the check should not be performed.
class AutoId(models.Model): | ||
pk = models.CompositePrimaryKey("id", "name") | ||
id = models.BigAutoField() | ||
name = models.CharField(max_length=255) | ||
|
||
class Meta: | ||
required_db_features = ["supports_autofields_in_composite_pk"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refer to how Constraint.check
is implemented
e.g.
django/django/db/models/constraints.py
Lines 152 to 155 in 3f59711
if not ( | |
connection.features.supports_table_check_constraints | |
or "supports_table_check_constraints" in model._meta.required_db_features | |
): |
tests/composite_pk/tests.py
Outdated
@@ -220,6 +220,12 @@ def test_model_forms(self): | |||
): | |||
self.assertIsNone(modelform_factory(Comment, fields=["pk"])) | |||
|
|||
def test_autoid_in_composite_pk(self): | |||
obj = AutoId.objects.create(name="Automatically generated ID") | |||
self.assertEqual(obj.id, 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assert it's not None
?
Thanks Simon, have applied your suggestions 👍 |
Trac ticket number
ticket-35957
Branch description
Allow auto fields to be used as part of a composite PK:
primary_key
from the deconstruction...AUTOINCREMENT
afterPRIMARY KEY
on the column declarationChecklist
main
branch.