[Django]-Determine if an M2M field has a custom through model

2👍

Looks as though

m2m_field.related.through._meta.auto_created is False

does the job.

👤Steven

2👍

From Django 2.2 the .add(), .create(), etc. methods are able to work with a custom through Model as long as you provide the corresponding values for the required fields of the intermediate model using through_defaults:

From the documentation:

You can also use add(), create(), or set() to create relationships, as long as you specify through_defaults for any required fields:

>>> beatles.members.add(john, through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.create(name="George Harrison", through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.set([john, paul, ringo, george], through_defaults={'date_joined': date(1960, 8, 1)})

You may prefer to create instances of the intermediate model directly.

The .remove() method’s behavior needs a bit of attention:

If the custom through table defined by the intermediate model does not enforce uniqueness on the (model1, model2) pair, allowing multiple values, the remove() call will remove all intermediate model instances:

>>> Membership.objects.create(person=ringo, group=beatles,
...     date_joined=date(1968, 9, 4),
...     invite_reason="You've been gone for a month and we miss you.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]>
>>> # This deletes both of the intermediate model instances for Ringo Starr
>>> beatles.members.remove(ringo)
>>> beatles.members.all()
<QuerySet [<Person: Paul McCartney>]>

About the _meta field I haven’t been able to access it from the m2m field, but the above part of the documentation seems to allow avoiding the "gymnastics" of accessing _meta.
If I find anything interesting I will update my answer accordingly.

0👍

For django 2.2 you can directly check the whether the through model is autocreated or not

This can be checked through a direct check like the following

# check for the auto_created value
m2m_field.through._meta.auto_created == False

To test this I created some sample clases with multiple M2M fields one with a custom through field and one with default class.

from django.db import models
import uuid


class Leaf(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(null=True, max_length=25, blank=True)


# Create your models here.
class MainTree(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    new_leaves = models.ManyToManyField(Leaf, through='LeafTree')
    leaves = models.ManyToManyField(Leaf, related_name='main_branch')


class LeafTree(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    tree = models.ForeignKey(MainTree, on_delete=models.CASCADE)
    leaf = models.ForeignKey(Leaf, on_delete=models.CASCADE)

Testing our method

In [2]: from trees.models import MainTree                                                                                                                                                                

In [3]: m = MainTree()                                                                                                                                                                                   

In [4]: m.leaves.through._meta                                                                                                                                                                           
Out[4]: <Options for MainTree_leaves>

In [5]: m.leaves.through._meta.auto_created                                                                                                                                                              
Out[5]: trees.models.MainTree

In [6]: m.new_leaves.through._meta.auto_created                                                                                                                                                          
Out[6]: False

In [7]: m.new_leaves.through._meta.auto_created == False                                                                                                                                                 
Out[7]: True

In [8]: m.leaves.through._meta.auto_created == False                                                                                                                                                     
Out[8]: False

Leave a comment