Arthur Sult
Arthur Sult

Reputation: 736

django model on_delete pass self to models.SET()

For example, I have a Book model, each book has its author and tag.

def get_authors_first_tag(book):
    try:
        tag = book.author.tags.first()
    except:
        return None
    else:
        return tag.id


class Author(models.Model):
    name = models.CharField(max_length=50)
    tags = models.ManyToManyField('Tag')

class Book(models.Model):
  name = models.CharField(max_length=50)
  author = models.ForeignKey(Author)
  tag =  models.ForeignKey('Tag', null=True, on_delete=models.SET(get_authors_first_tag))

I want, when deleting a Tag item, book's tag will be set to its author's first tag. How to do this? Thanks. Doc page - https://docs.djangoproject.com/en/1.9/ref/models/fields/#django.db.models.SET

UPDATE. signals didn't help

from django.db.models.signals import post_delete
from django.dispatch import receiver

class Book(models.Model):
  # temporaryly set to NULL on delete, 
  # becouse for my neds I cannot use default behavior (models.CASCADE)
  tag =  models.ForeignKey('Tag', null=True, on_delete=models.SET_NULL)

@receiver(post_delete, sender=Tag)
def delete_tag(instance, **kwargs):
    for book in instance.book_set.all():
        try:
            book.tag = book.author.tags.first()
            book.save()
        except:
            pass

UPDATE2 final working solution is: (dirty, but works)

@receiver(post_delete, sender=Tag)
def delete_tag(instance, **kwargs):
    books = Book.objects.all()
    for book in books:
        # after Tag item deleting, book.tag is set to None
        # so if boook's tag is null, get it's author's first tag
        if not book.tag:
            book.tag = book.author.tags.first()
            book.save()

Upvotes: 3

Views: 4159

Answers (1)

Alex Morozov
Alex Morozov

Reputation: 5993

The SET() doesn't accept any arguments, so you should use the signal receiver:

from django.db.models.signals import pre_delete
from django.dispatch import receiver

@receiver(pre_delete, sender=Tag)
def reset_tag(sender, **kwargs):
    tag = kwargs['instance']
    for book in books.filter(tag=tag):
        book.tag = book.author.tags.first()
        book.save()

Put this code into your models.py or make sure the file you put the receiver into is imported at the startup, so that the receiver is actually connected.

Upvotes: 2

Related Questions