class Food_Tag(models.Model): name = models.CharField(max_length=200) related_tags = models.ManyToManyField('self', blank=True, symmetrical=False, through='Tag_Relation') def __unicode__(self): return self.name class Tag_Relation(models.Model): source = models.ForeignKey(Food_Tag, related_name='source_set') target = models.ForeignKey(Food_Tag, related_name='target_set') is_a = models.BooleanField(default=False); # True if source is a target has_a = models.BooleanField(default=False); # True if source has a target
I want to be able to get the relations between Food_Tags like:
>>> steak = Food_Tag.objects.create(name="steak") >>> meat = Food_Tag.objects.create(name="meat") >>> r = Tag_Relation(source=steak, target=meat, is_a=True) >>> r.save() >>> steak.related_tags.all() [<Food_Tag: meat>] >>> meat.related_tags.all() []
but related_tags is empty for meat. I realize this has to do with the 'symmetrical=False' argument, but how can I set up the model such that 'meat.related_tags.all()' returns all related Food_Tags?
score:6
As mentioned in the docs:
- When defining a many-to-many relationship from a model to itself, using an intermediary model, you must use
symmetrical=False
(see the model field reference).
Thus, it is not (yet?) possible to have a symmetrical, recursive many-to-many relationship with extra fields, in Django. It's a "pick two" sorta deal.
Similar question
- How to make recursive ManyToManyField relationships that have extra fields symmetrical in Django?
- In Django, how do you retrieve data from extra fields on many-to-many relationships without an explicit query for it?
- How to make it such that if comments have been edited before, an "edited" permanent message will be displayed beside the comment?
- How would I make Django form fields have children?
- How to create relation on in django User model and other model if other model have two fields to make relation
- In a Django form, how do I make a field readonly (or disabled) so that it cannot be edited?
- Django admin: how to sort by one of the custom list_display fields that has no database field
- Django -- How to have a project wide templatetags shared among all my apps in that project
- django-rest-framework how to make model serializer fields required
- How to make sure that my AJAX requests are originating from the same server in Python
score:0
To create a symmetrical relationship, you have two options:
1) Create two Tag_Relation
objects - one with steak
as the source, and another with steak
as the target:
>>> steak = Food_Tag.objects.create(name="steak")
>>> meat = Food_Tag.objects.create(name="meat")
>>> r1 = Tag_Relation(source=steak, target=meat, is_a=True)
>>> r1.save()
>>> r2 = Tag_Relation(source=meat, target=steak, has_a=True)
>>> r2.save()
>>> steak.related_tags.all()
[<Food_Tag: meat>]
>>> meat.related_tags.all()
[<Food_Tag: steak]
2) Add another ManyToManyField to the Food_Tag
model:
class Food_Tag(models.Model):
name = models.CharField(max_length=200)
related_source_tags = models.ManyToManyField('self', blank=True, symmetrical=False, through='Tag_Relation', through_fields=('source', 'target'))
related_target_tags = models.ManyToManyField('self', blank=True, symmetrical=False, through='Tag_Relation', through_fields=('target', 'source'))
class Tag_Relation(models.Model):
source = models.ForeignKey(Food_Tag, related_name='source_set')
target = models.ForeignKey(Food_Tag, related_name='target_set')
As a note, I'd try to use something more descriptive than source
and target
for your through model fields.
score:0
The best solution of this problem (after many investigations) was to manually create symmetrical db record on save()
call. This results in DB data redundancy, of course, because you create 2 records instead of one. In your example, after saving Tag_Relation(source=source, target=target, ...)
you should save reverse relation Tag_Relation(source=target, target=source, ...)
like this:
class Tag_Relation(models.Model):
source = models.ForeignKey(Food_Tag, related_name='source_set')
target = models.ForeignKey(Food_Tag, related_name='target_set')
is_a = models.BooleanField(default=False);
has_a = models.BooleanField(default=False);
class Meta:
unique_together = ('source', 'target')
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
# create/update reverse relation using pure DB-level functions
# we cannot just save() reverse relation because there will be a recursion
reverse = Tag_Relation.objects.filter(source=self.target, target=self.source)
if reverse.exists():
reverse.update(is_a=self.is_a, has_a=self.has_a)
else:
Tag_Relation.objects.bulk_create([
Tag_Relation(source=self.target, target=self.source, is_a=self.is_a, has_a=self.has_a)
])
The only disadvantage of this implementation is duplicating Tag_Relation
entry, but except this everything works fine, you can even use Tag_Relation in InlineAdmin.
UPDATE
Do not forget to define delete
method as well which will remove reverse relation.
score:1
Since you didn't explicitly say that they need to be asymmetrical, the first thing I'll suggest is setting As eternicode pointed out, you can't do this when you're using a symmetrical=True
. This will cause the relation to work both ways as you described.through
model for the M2M relationship. If you can afford to go without the through
model, you can set symmetrical=True
to get exactly the behavior you describe.
If they need to remain asymmetrical however, you can add the keyword argument related_name="sources"
to the related_tags
field (which you might want to consider renaming to targets
to make things more clear) and then access the related tags using meat.sources.all()
.
score:2
I found this approach made by Charles Leifer which seems to be a good approach to overcome this Django limitation.
Credit To: stackoverflow.com
Related Query
- How can I make my model fields optional in Django?
- Django-filter, how to make multiple fields search? (with django-filter!)
- How to add some extra fields to the page in django-cms? (in django admin panel)
- How to force select_related() to select ForeignKeys that have null=True attribute?
- How can I get all the objects in a Django model that have a specific value for a ForeignKey field?
- Django how to make form fields optional
- How to to make a file private by securing the url that only authenticated users can see
- How to save extra fields on registration using custom user model in DRF + django-rest-auth
- How can I serve my django website from multiple machines, that is how can I make it distributed?
- How do I create a custom UserChangeForm that does not allow certain fields to be edited?
- How to make Django Queryset that selects records with max value within a group
- How to make a query that filters rows in which one column equals another one of the same table?
- Django REST framework: how to wrap the response with extra fields .... and supply the current response in data field
- How to tell which fields have been deferred/only'd in a Django queryset
- Django. How to add an extra field in User model and have it displayed in the admin interface
- Can a django formset that dynamically adds fields have persistent data?
- Django 1.8: How can I ensure that of Two Fields in a Model, At Least One or Only One must meet a condition?
- Django: How do I make fields non-editable by default in an inline model formset?
- How can I make Django search in multiple fields using QuerySets and MySql "Full Text Search"?
- How to make test case fail if a django template has a rendering error that would silently fail in production
More Query from same tag
- How to connect flutter Web_socket_channel package with Django Channels?
- Mysql query in django view
- Django Rest Framework remove csrf
- Why django-rest-auth SocialLoginView is not working?
- Django annotate a field value to queryset
- copy contents of one table to another table in django
- Lazy object returned unexpected type. error while authentication to admin page
- KeyError: 'collectstatic' When deploying the Django application to pythonanywhere
- Django CSRF verification failed. Webhook payment system
- Login Method in Django works fine with POSTMAN but with front end everything works except Login Method
- Apache mod_wsgi Django setup - Forbidden You don't have permission to access /mysite on this server
- sudo pip install django
- How to POST a django form with AJAX & jQuery
- Using relative vs absolute URL for STATIC_URL in Django
- Where do stderr logs go, for gunicorn applications run by supervisor?