mirror of
https://github.com/django/django.git
synced 2025-09-25 07:59:11 +00:00
Fixed #35453 -- Made ManyToManyField.concrete False.
ManyToManyField was already excluded from fields, concrete_fields, and local_concrete_fields in Options.
This commit is contained in:
parent
dce1b9c2de
commit
f9a44cc0fa
@ -1660,7 +1660,9 @@ class BaseDatabaseSchemaEditor:
|
|||||||
return output
|
return output
|
||||||
|
|
||||||
def _field_should_be_altered(self, old_field, new_field, ignore=None):
|
def _field_should_be_altered(self, old_field, new_field, ignore=None):
|
||||||
if not old_field.concrete and not new_field.concrete:
|
if (not (old_field.concrete or old_field.many_to_many)) and (
|
||||||
|
not (new_field.concrete or new_field.many_to_many)
|
||||||
|
):
|
||||||
return False
|
return False
|
||||||
ignore = ignore or set()
|
ignore = ignore or set()
|
||||||
_, old_path, old_args, old_kwargs = old_field.deconstruct()
|
_, old_path, old_args, old_kwargs = old_field.deconstruct()
|
||||||
@ -1692,8 +1694,10 @@ class BaseDatabaseSchemaEditor:
|
|||||||
):
|
):
|
||||||
old_kwargs.pop("db_default")
|
old_kwargs.pop("db_default")
|
||||||
new_kwargs.pop("db_default")
|
new_kwargs.pop("db_default")
|
||||||
return self.quote_name(old_field.column) != self.quote_name(
|
return (
|
||||||
new_field.column
|
old_field.concrete
|
||||||
|
and new_field.concrete
|
||||||
|
and (self.quote_name(old_field.column) != self.quote_name(new_field.column))
|
||||||
) or (old_path, old_args, old_kwargs) != (new_path, new_args, new_kwargs)
|
) or (old_path, old_args, old_kwargs) != (new_path, new_args, new_kwargs)
|
||||||
|
|
||||||
def _field_should_be_indexed(self, model, field):
|
def _field_should_be_indexed(self, model, field):
|
||||||
|
@ -144,7 +144,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|||||||
# Choose a default and insert it into the copy map
|
# Choose a default and insert it into the copy map
|
||||||
if (
|
if (
|
||||||
not create_field.has_db_default()
|
not create_field.has_db_default()
|
||||||
and not (create_field.many_to_many or create_field.generated)
|
and not create_field.generated
|
||||||
and create_field.concrete
|
and create_field.concrete
|
||||||
):
|
):
|
||||||
mapping[create_field.column] = self.prepare_default(
|
mapping[create_field.column] = self.prepare_default(
|
||||||
|
@ -1849,6 +1849,10 @@ class ManyToManyField(RelatedField):
|
|||||||
)
|
)
|
||||||
return name, path, args, kwargs
|
return name, path, args, kwargs
|
||||||
|
|
||||||
|
def get_attname_column(self):
|
||||||
|
attname, _ = super().get_attname_column()
|
||||||
|
return attname, None
|
||||||
|
|
||||||
def _get_path_info(self, direct=False, filtered_relation=None):
|
def _get_path_info(self, direct=False, filtered_relation=None):
|
||||||
"""Called by both direct and indirect m2m traversal."""
|
"""Called by both direct and indirect m2m traversal."""
|
||||||
int_model = self.remote_field.through
|
int_model = self.remote_field.through
|
||||||
|
@ -725,7 +725,7 @@ class QuerySet(AltersData):
|
|||||||
"Unique fields that can trigger the upsert must be provided."
|
"Unique fields that can trigger the upsert must be provided."
|
||||||
)
|
)
|
||||||
# Updating primary keys and non-concrete fields is forbidden.
|
# Updating primary keys and non-concrete fields is forbidden.
|
||||||
if any(not f.concrete or f.many_to_many for f in update_fields):
|
if any(not f.concrete for f in update_fields):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"bulk_create() can only be used with concrete fields in "
|
"bulk_create() can only be used with concrete fields in "
|
||||||
"update_fields."
|
"update_fields."
|
||||||
@ -736,7 +736,7 @@ class QuerySet(AltersData):
|
|||||||
"update_fields."
|
"update_fields."
|
||||||
)
|
)
|
||||||
if unique_fields:
|
if unique_fields:
|
||||||
if any(not f.concrete or f.many_to_many for f in unique_fields):
|
if any(not f.concrete for f in unique_fields):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"bulk_create() can only be used with concrete fields "
|
"bulk_create() can only be used with concrete fields "
|
||||||
"in unique_fields."
|
"in unique_fields."
|
||||||
@ -916,7 +916,7 @@ class QuerySet(AltersData):
|
|||||||
raise ValueError("All bulk_update() objects must have a primary key set.")
|
raise ValueError("All bulk_update() objects must have a primary key set.")
|
||||||
opts = self.model._meta
|
opts = self.model._meta
|
||||||
fields = [opts.get_field(name) for name in fields]
|
fields = [opts.get_field(name) for name in fields]
|
||||||
if any(not f.concrete or f.many_to_many for f in fields):
|
if any(not f.concrete for f in fields):
|
||||||
raise ValueError("bulk_update() can only be used with concrete fields.")
|
raise ValueError("bulk_update() can only be used with concrete fields.")
|
||||||
all_pk_fields = set(opts.pk_fields)
|
all_pk_fields = set(opts.pk_fields)
|
||||||
for parent in opts.all_parents:
|
for parent in opts.all_parents:
|
||||||
|
@ -92,10 +92,10 @@ class UpdateQuery(Query):
|
|||||||
raise FieldError(
|
raise FieldError(
|
||||||
"Composite primary key fields must be updated individually."
|
"Composite primary key fields must be updated individually."
|
||||||
)
|
)
|
||||||
if not field.concrete or (field.is_relation and field.many_to_many):
|
if not field.concrete:
|
||||||
raise FieldError(
|
raise FieldError(
|
||||||
"Cannot update model field %r (only non-relations and "
|
"Cannot update model field %r (only concrete fields are permitted)."
|
||||||
"foreign keys permitted)." % field
|
% field
|
||||||
)
|
)
|
||||||
if model is not self.get_meta().concrete_model:
|
if model is not self.get_meta().concrete_model:
|
||||||
self.add_related_update(model, field, val)
|
self.add_related_update(model, field, val)
|
||||||
|
@ -172,7 +172,7 @@ class CompositePKUpdateTests(TestCase):
|
|||||||
def test_cant_update_relation(self):
|
def test_cant_update_relation(self):
|
||||||
msg = (
|
msg = (
|
||||||
"Cannot update model field <django.db.models.fields.related.ForeignObject: "
|
"Cannot update model field <django.db.models.fields.related.ForeignObject: "
|
||||||
"user> (only non-relations and foreign keys permitted)"
|
"user> (only concrete fields are permitted)"
|
||||||
)
|
)
|
||||||
|
|
||||||
with self.assertRaisesMessage(FieldError, msg):
|
with self.assertRaisesMessage(FieldError, msg):
|
||||||
|
@ -6,6 +6,7 @@ from .models import AllFieldsModel
|
|||||||
|
|
||||||
NON_CONCRETE_FIELDS = (
|
NON_CONCRETE_FIELDS = (
|
||||||
models.ForeignObject,
|
models.ForeignObject,
|
||||||
|
models.ManyToManyField,
|
||||||
GenericForeignKey,
|
GenericForeignKey,
|
||||||
GenericRelation,
|
GenericRelation,
|
||||||
)
|
)
|
||||||
@ -209,7 +210,7 @@ class FieldFlagsTests(test.SimpleTestCase):
|
|||||||
def test_model_and_reverse_model_should_equal_on_relations(self):
|
def test_model_and_reverse_model_should_equal_on_relations(self):
|
||||||
for field in AllFieldsModel._meta.get_fields():
|
for field in AllFieldsModel._meta.get_fields():
|
||||||
is_concrete_forward_field = field.concrete and field.related_model
|
is_concrete_forward_field = field.concrete and field.related_model
|
||||||
if is_concrete_forward_field:
|
if is_concrete_forward_field or field.many_to_many:
|
||||||
reverse_field = field.remote_field
|
reverse_field = field.remote_field
|
||||||
self.assertEqual(field.model, reverse_field.related_model)
|
self.assertEqual(field.model, reverse_field.related_model)
|
||||||
self.assertEqual(field.related_model, reverse_field.model)
|
self.assertEqual(field.related_model, reverse_field.model)
|
||||||
|
@ -157,43 +157,32 @@ class AdvancedTests(TestCase):
|
|||||||
self.assertEqual(bar_qs[0].foo_id, b_foo.target)
|
self.assertEqual(bar_qs[0].foo_id, b_foo.target)
|
||||||
|
|
||||||
def test_update_m2m_field(self):
|
def test_update_m2m_field(self):
|
||||||
msg = (
|
rel = "<django.db.models.fields.related.ManyToManyField: m2m_foo>"
|
||||||
"Cannot update model field "
|
msg = f"Cannot update model field {rel} (only concrete fields are permitted)."
|
||||||
"<django.db.models.fields.related.ManyToManyField: m2m_foo> "
|
|
||||||
"(only non-relations and foreign keys permitted)."
|
|
||||||
)
|
|
||||||
with self.assertRaisesMessage(FieldError, msg):
|
with self.assertRaisesMessage(FieldError, msg):
|
||||||
Bar.objects.update(m2m_foo="whatever")
|
Bar.objects.update(m2m_foo="whatever")
|
||||||
|
|
||||||
def test_update_reverse_m2m_descriptor(self):
|
def test_update_reverse_m2m_descriptor(self):
|
||||||
msg = (
|
rel = "<ManyToManyRel: update.bar>"
|
||||||
"Cannot update model field <ManyToManyRel: update.bar> "
|
msg = f"Cannot update model field {rel} (only concrete fields are permitted)."
|
||||||
"(only non-relations and foreign keys permitted)."
|
|
||||||
)
|
|
||||||
with self.assertRaisesMessage(FieldError, msg):
|
with self.assertRaisesMessage(FieldError, msg):
|
||||||
Foo.objects.update(m2m_foo="whatever")
|
Foo.objects.update(m2m_foo="whatever")
|
||||||
|
|
||||||
def test_update_reverse_fk_descriptor(self):
|
def test_update_reverse_fk_descriptor(self):
|
||||||
msg = (
|
rel = "<ManyToOneRel: update.bar>"
|
||||||
"Cannot update model field <ManyToOneRel: update.bar> "
|
msg = f"Cannot update model field {rel} (only concrete fields are permitted)."
|
||||||
"(only non-relations and foreign keys permitted)."
|
|
||||||
)
|
|
||||||
with self.assertRaisesMessage(FieldError, msg):
|
with self.assertRaisesMessage(FieldError, msg):
|
||||||
Foo.objects.update(bar="whatever")
|
Foo.objects.update(bar="whatever")
|
||||||
|
|
||||||
def test_update_reverse_o2o_descriptor(self):
|
def test_update_reverse_o2o_descriptor(self):
|
||||||
msg = (
|
rel = "<OneToOneRel: update.bar>"
|
||||||
"Cannot update model field <OneToOneRel: update.bar> "
|
msg = f"Cannot update model field {rel} (only concrete fields are permitted)."
|
||||||
"(only non-relations and foreign keys permitted)."
|
|
||||||
)
|
|
||||||
with self.assertRaisesMessage(FieldError, msg):
|
with self.assertRaisesMessage(FieldError, msg):
|
||||||
Foo.objects.update(o2o_bar="whatever")
|
Foo.objects.update(o2o_bar="whatever")
|
||||||
|
|
||||||
def test_update_reverse_mti_parent_link_descriptor(self):
|
def test_update_reverse_mti_parent_link_descriptor(self):
|
||||||
msg = (
|
rel = "<OneToOneRel: update.uniquenumberchild>"
|
||||||
"Cannot update model field <OneToOneRel: update.uniquenumberchild> "
|
msg = f"Cannot update model field {rel} (only concrete fields are permitted)."
|
||||||
"(only non-relations and foreign keys permitted)."
|
|
||||||
)
|
|
||||||
with self.assertRaisesMessage(FieldError, msg):
|
with self.assertRaisesMessage(FieldError, msg):
|
||||||
UniqueNumber.objects.update(uniquenumberchild="whatever")
|
UniqueNumber.objects.update(uniquenumberchild="whatever")
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user