0

I'm trying to get annotations showing in my templates. I have two models (model1 and model2) and I want to show the number of model2's related to model1.

Here is my views.py:

def model2_count(request, pk):
    model2count = models.Model1.objects.filter(pk=model1.pk).annotate(title_count=Count(‘model2__title'))
    return render(request, 'model1/_model1.html', {‘m2c’: model2count})

Here is the template (model1/_model1.html):

I tried this:

{% for object in m2c %}</h3>
    {{ object.title }}
    {{ object.title_count }}
{% endfor %}

And tried this:

{% if m2c.title_count %}
    {{ m2c.title_count }}
{% endif %}

I've been pulling my hair out over this for a couple days and can't figure it out. The following has been largely unhelpful:

What's frustrating is that I can't even say why applying these solutions hasn't worked.

Any input is appreciated.

Also, here are my models with all the BS taken out.

class Publication(models.Model):
    title = models.CharField(max_length=150, unique=False, blank=False)
    contributors_note = models.TextField(max_length=300, blank=False)
    website = models.URLField()
    publisher = models.CharField(max_length=250, unique=False)
    publication_date = models.DateField(default=datetime.date.today)
    slug = models.SlugField(allow_unicode=True, unique=False)

    content_type = models.CharField(max_length=100, unique=False)# In this field user's define the type of content (blog, newspaper article, publication etc)
    research_type = models.CharField(max_length=100, unique=False)# In this field user's define whether the research is based on primary or secondary research
    user = models.ForeignKey(Current_user, related_name="publication")
    created_at = models.DateTimeField(auto_now_add=True)
    last_updated = models.DateTimeField(auto_now=True)
    category = models.ForeignKey(Category, related_name="publication",null=True, blank=False)

    comment = models.TextField()


    def __str__(self):
        return self.title

    def save(self, *args, **kwargs):
        self.slug = slugify(self.title)
        super().save(*args, **kwargs)

    def get_absolute_url(self):
        return reverse(
            "publication:single",
            kwargs={
                "username": self.user.username,
                "pk": self.pk
            }
        )

    class Meta:
        ordering = ["-created_at"]


class Assessment(models.Model):
    title = models.CharField(max_length=150, unique=False, blank=False)
    publication = models.ForeignKey('publication.Publication', on_delete=models.CASCADE, related_name='assessment')
    analyst = models.ForeignKey(Current_user, null=True, blank=True, related_name="assessment")
    created_at = models.DateTimeField(auto_now_add=True)
    approved_comment = models.BooleanField(default=False)


    key_finding1 = models.TextField(max_length=300)
    key_finding2 = models.TextField(max_length=300)
    key_finding3 = models.TextField(max_length=300)

    ratings_range = (
    ('1', 'Very Weak'),
    ('2', 'Weak'),
    ('3', 'Moderate'),
    ('4', 'Strong'),
    ('5', 'Very Strong'),
    )

    content_rating_1 = models.CharField(max_length=1, choices=ratings_range)
    content_rating_1_comment = models.TextField(max_length=300)
    content_rating_2 = models.CharField(max_length=1, choices=ratings_range)
    content_rating_2_comment = models.TextField(max_length=300)
    content_rating_3 = models.CharField(max_length=1, choices=ratings_range)
    content_rating_3_comment = models.TextField(max_length=300)
    content_rating_4 = models.CharField(max_length=1, choices=ratings_range)
    content_rating_4_comment = models.TextField(max_length=300)
    content_rating_5 = models.CharField(max_length=1, choices=ratings_range)
    content_rating_5_comment = models.TextField(max_length=300)

    source_rating_1 = models.CharField(max_length=1, choices=ratings_range)
    source_rating_1_comment = models.TextField(max_length=300)
    source_rating_2 = models.CharField(max_length=1, choices=ratings_range)
    source_rating_2_comment = models.TextField(max_length=300)
    source_rating_3 = models.CharField(max_length=1, choices=ratings_range)
    source_rating_3_comment = models.TextField(max_length=300)
    source_rating_4 = models.CharField(max_length=1, choices=ratings_range)
    source_rating_4_comment = models.TextField(max_length=300)
    source_rating_5 = models.CharField(max_length=1, choices=ratings_range)
    source_rating_5_comment = models.TextField(max_length=300)


    def approve(self):
        self.approved_comment = True
        self.save()

    def __str__(self):
        return self.title

    class Meta:
        ordering = ["-created_at"]
Dt23
  • 103
  • 1
  • 11
  • What *is* being shown? Is `object.title` being shown? – dirkgroten Sep 15 '18 at 11:08
  • Show us how your models are related, I don't understand `Count('model2__title')` as aggregation. I'd expect just `Count('model2')` if model2 has a ForeignKey to `model1`. – dirkgroten Sep 15 '18 at 11:12
  • How to debug such an issue: Open your Django shell (`django-admin shell`) and actually fetch a `model1` object, then create your `model2count` variable as in your code and check the result. – dirkgroten Sep 15 '18 at 11:14
  • @dirkgroten I went through the debugging process and it works - I get the correct count. – Dt23 Sep 16 '18 at 08:18
  • Model2 is related to model1 with foreignkey. Any other tips on how to debug it? – Dt23 Sep 16 '18 at 08:21
  • Another note on why this is super confusing: I have another section in the code where I use a for to display other model2 data that's related to model1: {% for object in model1.model2.all %}. It only works when the .all is included and it works for displaying things like { object.title } but annotations don't work. – Dt23 Sep 16 '18 at 08:29
  • Can you show us how your models are defined? And please use your original code, working code (not 'model1' and 'model2' but the real names of your models). The code above has obvious syntax errors and cannot be your working code. Also as mentioned, 'model2__title' doesn't work for your count aggregate, so please edit your question to show the code as you have it right now. – dirkgroten Sep 16 '18 at 10:28
  • Yeah sure. I thought I'd spare you my drivel. I'm adding it above. – Dt23 Sep 16 '18 at 15:17
  • So you’re doing `Assessment.objects.filter(pk=pk).annotate(title_count=Count('publication')`? – dirkgroten Sep 16 '18 at 16:51
  • The other way around: Publication.objects.filter(pk=pk).annotate(title_count=Count('assessment'). As in, each publication has many assessments, and I want to count how many assessments have been done for this particular publication. – Dt23 Sep 17 '18 at 11:08

2 Answers2

1

Bad mistake on my part. The solution's given above worked. Here is my final code:

views.py

class PublicationDetail(SelectRelatedMixin, generic.DetailView):
model = models.Publication
select_related = ("category", "user")

def get_queryset(self):
    queryset = super().get_queryset()
    return queryset.filter(user__username__iexact=self.kwargs.get("username")).annotate(assessment_count=Count('assessment'))

Fellow noobs that follow: - It's easier for the people that want to help you (and faster for you) if you just post your original code instead of trying to get smart. Don't be embarrassed. I would've saved us all time if I had just done that.

Dt23
  • 103
  • 1
  • 11
0

First aggregations are done only using the field name i.e model2 not model2__title

Next getting counts of annotated columns recommended to use .values or values_list but not required.

https://docs.djangoproject.com/es/2.1/topics/db/aggregation/#cheat-sheet

model1 = Model1.objects.get(pk=model1.pk)
model2count = (
    Model1.objects.annotate(count=Count('model2'))
       .filter(pk=model1.pk)
       .values_list('title', 'count', named=True)
)

template.html

{% for object in model2count %}
    {{ object.title }}
    {{ object.count }}
{% endfor %}
jackotonye
  • 3,537
  • 23
  • 31
  • Thanks for the response. I gave it a try and there's no change - nothing showing in the template. – Dt23 Sep 16 '18 at 08:16
  • This has to work. Please show us more of the template. Or check the HTML source in your browser. – dirkgroten Sep 16 '18 at 10:30
  • I completely get that this has to work. I have no clue why it's not. Especially considering that when I use the django shell it works fine – Dt23 Sep 17 '18 at 11:09
  • @dirkgroten thanks for trying to help a noob. I figured it out - dirkgroten's "This has to work" really helped. The problem (as usual) was me - I overlooked the generic view I had used initially. – Dt23 Sep 17 '18 at 12:42