Home > OS >  How to show all the tags related to a publication in a list of publications filtered by a tag in Dja
How to show all the tags related to a publication in a list of publications filtered by a tag in Dja

Time:11-13

Consider following models:

# models.py

from django.db import models

class Tag(models.Model):
    name = models.CharField(max_length=40)
    number_of_publications = models.PositiveIntegerField(default=0)
    def __str__(self):
        return self.name


class Publication(models.Model):
    name = models.CharField(max_length=80)
    text = models.TextField()
    tags = models.ManyToManyField(Tag, related_name="publications")
# urls.py

from .views import publications_tagged
from django.urls import path

urlpatterns = [
    path('publications/tagged/<str:name>', publications_tagged)
]
# views.py

from .models import Tag, Publication
from django.shortcuts import get_object_or_404, render


def publications_tagged(request, name):
    tag = get_object_or_404(Tag.objects.prefetch_related('publications'), name=name)
    return render(request, 'myapp/tagged_questions.html', {'tag': tag})

Ok, so when we'd go to a template and make a for-loop that will create a representation of every publication and show appropriate tags of it, Django would make a database call for every publication to fetch all the tags the publication has, meaning:

Iteration x of tag.tags_set -> check what tags match with publication X.

Of course, we can just not show tags and everything will work like magic (utilizing prefetch_related), but that's not what we want :)

How could we do something similar in Django?

What we want is some kind of optimized way to achieve such a result, because stackoverflow renders 15 items really fast, which makes me think they do not do it the monkey way that Django does by default.

This is what html might look like

This is what html code might look like

P.S. My problem doesn't quite sound like that. But just in case anyone would say: "Oh, it's a bad design, you shouldn't do that at all" I decided to show an example.

CodePudding user response:

You can add a property to your Publication model to render tags as a list of strings, so:

class Publication(models.Model):
    text = models.TextField()
    tags = models.ManyToManyField(Tag)

    @property
    def tags_list(self):
        # renders as ["django", "python", ...]
        return list(self.tags.values_list("name", flat=True))

Make sure you use prefetch_related("tags") to optimize your query.

CodePudding user response:

The answer is really simple, all you have to do is in the prefetch_related specify publications__tags, which leaves you with

tag = get_object_or_404(Tag.objects.prefetch_related('publications', 'publications__tags'), name=name)

  • Related