How do you ensure that you don't leave any orphan records when deleting records from Django tables that have a many-to-many relationship?
I've created three Django models that will allow users to create lists of items that represent activities (e.g. movies, concerts, and sporting events) they want to attend. Since each user can include one or more items (i.e. activities) in a list and multiple users may have the same item (activity) in one of their lists, there's a many-to-many relationship between lists and items:
class Type(models.Model):
TYPES = (
(1, 'Movie'),
(2, 'Concert'),
(3, 'Sport'),
)
type = models.CharField(_('type'), max_length=16)
class Meta:
db_table = 'type'
class List(models.Model):
user = models.ForeignKey(User)
type = models.ForeignKey(Type, help_text=_('Type of list'))
name = models.CharField(_('list'), max_length=128, help_text=_('Name of list'))
class Meta:
db_table = 'list'
unique_together = (('user', 'type', 'name'), ) # Ensure no duplicate lists
class Item(models.Model):
"""item contains the pk of a movie, concert, or sporting event record"""
lists = models.ManyToManyField(List, related_name='items'). # Results in creation of item_lists linking table
type = models.ForeignKey(Type, help_text=_('Type of item'))
item = models.IntegerField(_('item'), help_text=_('Item in list'))
class Meta:
db_table = 'item'
unique_together = ('item', type',)
Whenever I create a new list item, I'll use Django's get_or_create() QuerySet method.
To prevent orphan records, I would think that whenever I delete an entire list or one or more items from a list, I'll also need to check to see if the deleted items are contained in any other lists and delete those items from the item table if they aren't. The pseudocode might look like this:
items_to_delete = (single item or items from a list)
for item in items_to_delete:
if item is not in item_lists linking table: # Prevent orphan records
delete item from item table
delete item from item_lists linking table
Is there a "standard" Django way to perform the above record deletion? Should I override Django's object delete method so that it also deletes the item from the item table if there are no more instances in the linking table? Or would this be a situation for creating a Django signal?
Second, should I wrap the above deletion logic in a Django atomic database transaction? Isn't it possible that one process might do a get_or_create on an existing item and get it because it thinks it's in the table but then a second process deletes that item from the item table before the first process can create a record for it in the linking table?