Search code examples
pythondjangodjango-modelsmany-to-many

Strange: why Django related many-to-many lookup works this way (but not like in docs)?


The Django docs describe how to query reverse m2m fields: https://docs.djangoproject.com/en/1.8/topics/db/queries/#many-to-many-relationships

According to this answer we should use related_name as the first argument in the query.

But I'm having trouble making this work. (I'm using Django 1.8.5). Here are my example models:

class Nlpneutralfeature(models.Model):   
    neutral_feature = models.CharField(u'found word', max_length=255, default='')   

class Userproject(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="project", verbose_name=("owner"))
    monitoring_words = models.ManyToManyField(Nlpneutralfeature, null=True, blank=True, related_name="monitoringwords")        
    name = models.CharField(u'Название проекта', unique=True, max_length=255)

So, to get all Nlpneutralfeature rows that belong to Userproject where Userproject.name == '48', I need to do:

Nlpneutralfeature.objects.filter(userproject__monitoringwords__name='48')

But this does not work. The error I get is: Cannot resolve keyword *'userproject'* into field.

So, Django cannot understand that 'userproject' is lowercased model name Userproject.

Now, this is working:

Nlpneutralfeature.objects.filter(monitoringwords__name='48')

How does Django know that monitoringwords is related_name? No error is strange to me, sicne there is no monitoringwords field in the Nlpneutralfeature model!


Solution

  • Note the phrasing in the Django docs:

    The name to user for the relation from the related object back to this one. It's also the default value for related_query_name (the name to use for the reverse filter name from the target model).

    In your example, the "target model" is Nlpneutralfeature. When you set related_name = 'monitoringwords' on the ManyToManyField Userproject.monitoringwords, it tells Django to use that related_name to refer from Nlpneutralfeature model objects back to the corresponding Userproject model objects.

    Correspondingly, if you had declared Userproject.monitoringwords with related_name = 'foo', the way to query for all Nlpneautralfeatures belonging to project 48 would be: Nlpneautralfeature.objects.filter(foo__name='48').