3

I've a strange problem in Django template. It is a template for show a list of the articles and for everyone of they I show a list of keyword that I've called key concepts.

The stranger thing is that instead of a list of key concepts it is shown a list of articles that use that key concept.

Below the E/R diagram and model and template of my project: enter image description here

Models.py

class KeyConceptModel(models.Model):
    concept_text = models.CharField(max_length=50)

    def __str__(self):
        return self.concept_text

    def get_absolute_url(self):
        return reverse("keyconceptManuscriptusView", kwargs={"pk": self.pk})

    class Meta: 
        verbose_name = "Concetto chiave"
        verbose_name_plural = "Concetti chiave"  

class PostModel(models.Model):
    post_title = models.CharField(max_length=70)
    post_short_description = models.TextField(max_length=200)
    post_contents = models.TextField()
    post_publishing_date = models.DateTimeField(auto_now=False, auto_now_add=True)
    post_author = models.ForeignKey(AuthorModel, on_delete=models.CASCADE)
    post_keyconcept = models.ManyToManyField(KeyConceptModel)
    slug = models.SlugField(verbose_name="Slug", unique="True")
    post_highlighted = models.BooleanField(default=False)

    def __str__(self):
        return self.post_title

    def get_absolute_url(self):
        return reverse("singlepostManuscriptusView", kwargs={"slug": self.slug})

    class Meta: 
        verbose_name = "Articolo"
        verbose_name_plural = "Articoli" 

Views.py

class ListPostGDV(ListView):
    model = PostModel
    template_name = "manuscriptus_home.html"

Template

{% for posts in object_list %}
  <div id="news" class="container">
    <div class="row">
      <img class="img-fluid" src="{% static 'manuscriptus/img/demo_img.png' %}" alt="Header image">
    </div>
    <div class="row">
      <div class="col-3">
        <div class="row">
          <small class="text-muted">Pubblicato il <strong>{{ posts.post_publishing_date|date }}</strong></small>
        </div>
        <div class="row">
          {% for keyword in object_list.all %}
              <p>{{ keyword }}</p>
          {% endfor %}
        </div>
      </div>
      <div class="col-9">
        <div class="row">
          <p class="h3"><a href="{{ posts.get_absolute_url }}">{{ posts.post_title }}</a></p>
        </div>
        <div class="row">
          <p class="h5">{{ posts.post_short_description|safe|linebreaks }}</p>
        </div>
      </div>
    </div>
  </div>
{% empty %}
  <div id="news" class="container">
    <h1>Go to the admin panel and create your first post!</h1>
  </div>
{% endfor %}

NB: I've used the generic detail views

MaxDragonheart
  • 1,117
  • 13
  • 34

1 Answers1

3

In your template you write:

<!-- this will iterate again over the same list -->
{% for keyword in object_list.all %}
    <p>{{ keyword }}</p>
{% endfor %}

But here object_list is your QuerySet of the articles. The fact that you call .all() only means that the for loop will thus again iterate over all PostModels (well the .all() is used to make it explicit that you do no filtering).

If you want to iterate over the post_keyconcept, you need to call posts.post_keyconcept.all instead:

{% for keyword in posts.post_keyconcept.all %}
    <p>{{ keyword }}</p>
{% endfor %}

Since you want to render the key_concepts of all posts, you better use .prefetch_related(..) in the ListView such that the keywords are fetched in a constant number of queries, so:

class ListPostGDV(ListView):
    model = PostModel
    queryset = PostModel.objects.prefetch_related('post_keyconcept')

    # ...

Note: normally the names of models are singular and without the Model suffix, so Post instead of PostModel, and KeyConcept instead of KeyConceptModel.

 

Note: since you iterate over object_list (in the outer loop) the item is a single post, so I advice to name it post, instead of posts, since this otherwise only introduces confusion.

 

Note: you prefix all attributes with post_ which is a bit redundant. It also prevents making use of duck typing when for example two models have a name attribute, and you want the function to be able to process both. Therefore I advise to remove the post_ prefix of the attributes.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • I've editetd the first post to add the ListView. When I use your indication is shown this message: "Cannot find 'key_concepts' on PostModel object, 'key_concepts' is an invalid parameter to prefetch_related()". What is wrong? About the notes that you have add, you are rigth but this is my first project in Django and this type of naming is important to me in development phase. After this phase I will use your indications to have a correct naming. – MaxDragonheart Sep 09 '18 at 12:37
  • 1
    @MassimilianoMoraca: ha, it was `post_keyconcept` :) as you can see the prefix thing is really confusing. Some of the above mentioned stylings are for example described in [**PEP-8**](https://www.python.org/dev/peps/pep-0008/). Although one does not have to use this (in the sense that programs work without it, it is typically harder to people used to the styling) :) – Willem Van Onsem Sep 09 '18 at 12:41
  • eh...now I don't show any error but in the template the key concepts aren't shown. I think it is better that I follow your directions and I go to correct the prefixes also because I start to confuse myself too! :S Thanks for your indication :) – MaxDragonheart Sep 09 '18 at 13:01
  • 1
    @MassimilianoMoraca: did you edit the template as well (it was the same mistake, it should be `posts.post_keyconcept.all`). – Willem Van Onsem Sep 09 '18 at 13:03
  • 1
    I've recreated all the project following your indications and now I can confirm that it run now. Thanks :) – MaxDragonheart Sep 09 '18 at 18:21