Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django remove m2m instance when there are no more relations

In case we had the model:

class Publication(models.Model):
    title = models.CharField(max_length=30)

class Article(models.Model):
    publications = models.ManyToManyField(Publication)

According to: https://docs.djangoproject.com/en/4.0/topics/db/examples/many_to_many/, to create an object we must have both objects saved before we can create the relation:

p1 = Publication(title='The Python Journal')
p1.save()
a1 = Article(headline='Django lets you build web apps easily')
a1.save()
a1.publications.add(p1)

Now, if we called delete in either of those objects the object would be removed from the DB along with the relation between both objects. Up until this point I understand.

But is there any way of doing that, if an Article is removed, then, all the Publications that are not related to any Article will be deleted from the DB too? Or the only way to achieve that is to query first all the Articles and then iterate through them like:

to_delete = []
qset = a1.publications.all()
for publication in qset:
    if publication.article_set.count() == 1:
        to_delete(publication.id)
a1.delete()
Publications.filter(id__in=to_delete).delete()

But this has lots of problems, specially a concurrency one, since it might be that a publication gets used by another article between the call to .count() and publication.delete().

Is there any way of doing this automatically, like doing a "conditional" on_delete=models.CASCADE when creating the model or something?

Thanks!

like image 224
lpares12 Avatar asked Oct 15 '25 04:10

lpares12


1 Answers

I tried with @Ersain answer:

a1.publications.annotate(article_count=Count('article_set')).filter(article_count=1).delete()

Couldn't make it work. First of all, I couldn't find the article_set variable in the relationship.

django.core.exceptions.FieldError: Cannot resolve keyword 'article_set' into field. Choices are: article, id, title

And then, running the count filter on the QuerySet after filtering by article returned ALL the tags from the article, instead of just the ones with article_count=1. So finally this is the code that I managed to make it work with:

Publication.objects.annotate(article_count=Count('article')).filter(article_count=1).filter(article=a1).delete()

Definetly I'm not an expert, not sure if this is the best approach nor if it is really time expensive, so I'm open to suggestions. But as of now it's the only solution I found to perform this operation atomically.

like image 152
lpares12 Avatar answered Oct 16 '25 21:10

lpares12



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!