ManyToManyField
class DatabaseModule(models.Model):
code = models.CharField(max_length=10)
description = models.CharField(max_length=100)
def __unicode__(self):
return '%s %s' % (self.code, self.description,)
class Meta:
verbose_name = 'Database Module Type'
verbose_name_plural = 'Database Module Types'
class DatabaseConfig(models.Model):
description = models.CharField(max_length=100)
database_name = models.CharField(max_length=100)
modules = models.ManyToManyField(DatabaseModule)
def __unicode__(self):
return '%s %s' % (self.database_name, self.description,)
class Meta:
ordering = ['database_name']
verbose_name = 'Database Configuration'
verbose_name_plural = 'Database Configurations'
add
To add a model instance (or primary key) to a many to many field:
config.modules.add(module)
# modules is a list of modules
config.modules.add(*modules)
Warning
I think the add
method allows you to add an object instance or
a primary key. If you try and add an invalid primary key, the
add
method will fail silently!! I can’t find this in the
official documents, but it is hinted at here: django_conduit_
through
Warning
Do not use soft deletes for the through
table if you want to
use it in a filter
e.g. department__in=[dept1, dept2]
.
The filter will not exclude the deleted rows.
Example:
# model
department = models.ManyToManyField(
Category,
related_name='contact_department',
through='ContactDepartment',
)
def add_department(self, category):
return ContactDepartment.objects.init_contact_category(
self,
category,
)
def update_department(self, categories, user):
ContactDepartment.objects.update_categories(
self, categories, user
)
In this example, ContactDepartment
is inherited from ContactCategory
:
class ContactCategoryManager(models.Manager):
def categories(self, contact):
return self.model.objects.filter(contact=contact)
def create_contact_category(self, contact, category):
obj = self.model(contact=contact, category=category)
obj.save()
return obj
def init_contact_category(self, contact, category):
try:
obj = self.model.objects.get(contact=contact, category=category)
except self.model.DoesNotExist:
obj = self.create_contact_category(contact, category)
return obj
def update_categories(self, contact, categories, user):
for category in categories:
self.init_contact_category(contact, category)
self.model.objects.filter(
contact=contact,
).exclude(
category__in=categories,
).delete()
class ContactDepartmentManager(ContactCategoryManager):
pass
class ContactCategory(TimeStampedModel):
"""Abstract model for 'contact', 'category' tables."""
contact = models.ForeignKey(Contact)
category = models.ForeignKey(Category)
class Meta:
abstract = True
def __str__(self):
return '{} {}'.format(self.contact.user.username, self.category.name)
class ContactDepartment(ContactCategory):
objects = ContactDepartmentManager()
class Meta:
ordering = ['contact__user__username', 'category__name']
unique_together = ['contact', 'category']
verbose_name = 'Contact department'
verbose_name_plural = 'Contact departments'