Bug in dumpdata dependency calculation involving ManyToManyFields
|Reported by:||aneil||Owned by:||koirikivi|
|Has patch:||yes||Needs documentation:||no|
|Needs tests:||no||Patch needs improvement:||no|
The manage.py dumpdata command incorrectly interprets ManyToMany relationships as dependencies of the model that declares them (rather than the other way around).
In the example below are 5 models - User, Tag and Posting, where both User and Posting have M2M relationships to Tag via UserTag and PostingTag, respectively. This should be serializable.
Here are the actual dependencies:
User: None Tag: None Posting: User PostingTag: Posting, Tag UserTag: User, Tag
However, dumpdata fails with this error:
Error: Can't resolve dependencies for main.Posting, main.PostingTag, main.Tag, main.User, main.UserTag in serialized app list.
from django.db.models import Model,CharField,ForeignKey,ManyToManyField,TextField,DateTimeField class User(Model): username = CharField(max_length=20) password = CharField(max_length=20) topics = ManyToManyField("Tag",through="UserTag") def natural_key(self): return (self.username,) class Posting(Model): user = ForeignKey(User) text = TextField() time = DateTimeField() def natural_key(self): return (self.user.username,self.time) natural_key.dependencies=['main.User'] class Tag(Model): name = CharField(max_length=20) postings = ManyToManyField(Posting,through="PostingTag") def natural_key(self): return (self.name,) class PostingTag(Model): tag = ForeignKey(Tag) posting = ForeignKey(Posting) def natural_key(self): return (self.tag.natural_key(),self.posting.natural_key()) class UserTag(Model): user = ForeignKey(User) tag = ForeignKey(Tag) def natural_key(self): return (self.tag.natural_key(),self.user.natural_key())
The reason this occurs is invalid logic in django/core/management/commands/dumpdata.py in lines 152-155. Here is the relevant code & context:
145 # Now add a dependency for any FK or M2M relation with 146 # a model that defines a natural key 147 for field in model._meta.fields: 148 if hasattr(field.rel, 'to'): 149 rel_model = field.rel.to 150 if hasattr(rel_model, 'natural_key'): 151 deps.append(rel_model) 152 for field in model._meta.many_to_many: 153 rel_model = field.rel.to 154 if hasattr(rel_model, 'natural_key'): 155 deps.append(rel_model) 156 model_dependencies.append((model, deps))
Lines 152-155 treat M2M relations like FK relations. This is incorrect. A Model named by an FK is a dependency, however, the model named by an M2M is not.
The fix requires adding the M2M *table* to the model_list, and processing its dependencies accordingly.
I've attached a simple test project that demonstrates the problem.
Change History (18)
Changed 4 years ago by aneil
Changed 4 years ago by aneil
comment:1 Changed 4 years ago by russellm
- Cc james@…, andrew@… added
- Has patch set
- Keywords database postgres schema added
- Needs documentation unset
- Needs tests set
- Patch needs improvement set
- Triage Stage changed from Unreviewed to Accepted
comment:2 Changed 4 years ago by russellm
- Cc james@…, andrew@… removed
- Keywords easy-pickings added; database postgres schema removed
comment:3 Changed 4 years ago by outofculture
- Owner changed from nobody to outofculture
- Status changed from new to assigned