m2m 'through' field in django models throwing this error: 'M2M field' object has no attribute '_m2m_reverse_name_cache'

Viewed 358

Hey guys I am trying to add a m2m through field to have assistants to my 'Department' model to call like department.assistants.all(), but while doing so, I am getting this error AttributeError: 'ManyToManyField' object has no attribute '_m2m_reverse_name_cache'.

This is my model:

class Department(models.Model):
    id                  = models.BigAutoField(primary_key=True)
    user                = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    assistants          = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Assistants', related_name='dep_assistants', 
                            symmetrical=False)

class Assistants(models.Model):
    id                  = models.BigAutoField(primary_key=True)
    department          = models.ForeignKey(Department, related_name='of_department', on_delete=models.CASCADE)
    assistant           = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='dt_assistant', 
                            verbose_name="Department Assistant", on_delete=models.CASCADE)
    added               = models.DateTimeField(auto_now_add=True)

I am pretty new to this concept. Can someone tell me what I did wrong here?

Thanks

2 Answers

If one assistant relates to only one department - this is relation one-to-many. (One department has many assistants) In code would be:

class Assistant(models.Model):
    ...
    department = models.ForeignKey(Department)

No need for a special reference on Department. To get all assistants:

assistants = models.Assistant.objects.filter(department=department)

Or create a property on a class Department:

@property
def assistants(self):
    return models.Assistant.objects.filter(department=self)

If one assistant relates to many departments (and each department has many assistants), it is many-to-many relationship and there should be additional class between them:

class Assignment(models.Model):
    assistant = models.ForeignKey(Assistant)
    department = models.ForeignKey(Department)

class Department(models.Model):
    ...
    assignment= models.ForeignKey(Assignment)

class Assistant(models.Model):
    ...
    assignment = models.ForeignKey(Assignment)

So here to query assistants of the department:

assistants = models.Assistant.objects.filter(
    assignment__in=models.Assignment.objects.filter(
        department=department
    )
)

The way you have defined your models the queries seem too confusing. Try how models are defined below and then try the query.

You did not mention the through_field attribute in the many to many field definition. check the docs: https://docs.djangoproject.com/en/3.2/ref/models/fields/#django.db.models.ManyToManyField

class Department(models.Model):
    # i think this is not needed. Also id is a protected keyword in python.
    # id                  = models.BigAutoField(primary_key=True)
    user                = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    assistants          = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Assistants', 
                                                 related_name='departments', through_fields=("department", "assistant"))

# model name should never be prural. It is singluar becuase it is the name of the object.
class Assistant(models.Model):
    # i think this is not needed. Also id is a protected keyword in python.
    # id                  = models.BigAutoField(primary_key=True)
    department          = models.ForeignKey(Department, on_delete=models.CASCADE)
    assistant           = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name="Department Assistant", on_delete=models.CASCADE)
    added               = models.DateTimeField(auto_now_add=True)


# how to query assistants from departments
# you will get objects of User model
qs = department.assistants.all()


# how to query departments from assistants
# you will get objects of Department model
qs = user.departments.all()


# If you want to query the Assistant model

# from department object
qs = department.assistant_set.all()

# from assistant object
qs = user.assistant_set.all()

# in either case you will get the objects of Assistant model
for i in qs:
    print(i.added, i.department, i.assistant)

Try this and let me know if you still get the error.

My suggestion is to name the assistant field on the Assistant model as user. This way you will not need to define through_field on the many to many field.

Related