Using custom manager in template

Hi every one
i have 3 models as follow

good:

class Good(models.Model):
    mainbranch = models.ForeignKey(
        MainBranch, on_delete=models.CASCADE,
        verbose_name='کد دسته اصلی', default=0
    )
    subbranch1 = models.ForeignKey(
        SubBranch1, on_delete=models.CASCADE,
        verbose_name='کد دسته فرعی 1', default=0
    )
    subbranch2 = models.ForeignKey(
        SubBranch2, on_delete=models.CASCADE,
        verbose_name='کد کالا', default=0
    )
    goodproperties = models.OneToOneField(
        GoodProperties, on_delete=models.CASCADE,
        verbose_name='مشخصات کالا', null=True, blank=True
    )
    goodspecifications = models.OneToOneField(
        GoodSpecification, on_delete=models.CASCADE,
        verbose_name='معرفی کالا', default=0, null=True, blank=True
    )
    name = models.CharField(verbose_name='نام کالا')
    image = models.ImageField(upload_to='photos/%Y/%m/%d', null=True, blank=True, verbose_name='تصویر کالا')
    quantity = models.PositiveIntegerField(default=0, verbose_name='تعداد موجود')
    price = models.DecimalField(
        decimal_places=0, max_digits=12, default=0,
        verbose_name='قیمت کالا'
    )
    discount = models.SmallIntegerField(null=True, blank=True, verbose_name='میزان تخفیف')
    seller = models.ManyToManyField('Seller', verbose_name='کد فروشنده')

    @property  # getting all comments related to a specific good
    def get_comments(self):
        goods_comment = self.comment.all()
        return goods_comment

    def __str__(self):
        return self.name

comment:

class Comment(models.Model):
    good = models.ForeignKey(Good, on_delete=models.CASCADE, verbose_name='کد کالا', related_name='comments')
    customer = models.ForeignKey(CustomUser, on_delete=models.CASCADE, verbose_name='کد مشتری')
    text = models.TextField(verbose_name='متن نظر', help_text='متن نظر را وارد کنید')
    date = models.DateTimeField(auto_now=True, verbose_name='تاریخ ثبت رای')

    def __str__(self):
        return self.text

and vote:

class Vote(models.Model):
    comment = models.ForeignKey(Comment, on_delete=models.CASCADE, null=True, blank=True,
                                related_name='comment_vote', verbose_name='نظر مربوطه')
    customer = models.ForeignKey(CustomUser, on_delete=models.CASCADE, null=True, blank=True,
                                 related_name='comment_user', verbose_name='کاربر نظر داده')
    type = models.BooleanField(verbose_name='نوع نظر')

    objects = models.Manager()
    like_dislike_counts = LikeDislikeManager()

    def __str__(self):
        return '{0}--{1}'.format(self.comment, self.type)

i defined a custom manager to count all like and dislikes of each comment leaved for a certain product

class LikeDislikeManager(models.Manager):
    def get_query_set(self):
        comment_counts = Comment.objects.annotate(
            like_count=Count('comment_vote', filter=Q(comment_vote__type=True)),
            dislike_count=Count('comment_vote', filter=Q(comment_vote__type=False))
        )
        queryset = self.get_queryset()
        queryset.prefetch_related(
            Prefetch('comments', queryset=comment_counts)
        )
        return queryset

but in my template i wrote this piece of code and it won’t work
could anyone tell me where the problem is.
my template:

<p dir="ltr">
        {% for obj in object.comments.comment_vote.like_dislike_count %}
               {{obj.like_count}}رای
         {% endfor %}
</p>

You are annotating like_count and dislike_count directly on the Comment objects. Those annotated values are then available like any other field in the model.

Therefore, if you have an instance of Comment named comment, then you would reference those fields as comment.like_count and comment.dislike_count.

Without seeing the view that is rendering this template, we would not be able to offer a specific suggestion as to how to fix your template.

Thanks for your answer
here is the view

class GoodsListView(DetailView):
    model = Good
    template_name = 'g_list.html' 

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['comment_form'] = CommentForm        
        return context

    def post(self, request, *args, **kwargs):
        view = CommentPost.as_view()
        return view(request, *args, **kwargs)

Since the object in your template is an instance of Good, then you would iterate over the object.comments collection, and for each comment, render like_count and dislike_count.

so i changed template like below but nothing is displaying as like and dislike

<div class = "card-footer">
            {% for comment in good.comments.all %}
             <p>
                 <span class = "font-weight-bold">
                     {{comment.customer.first_name}} {{comment.customer.last_name}} --
                 </span>
                {{comment.text}}
                <a href="{% url 'edit_comment' pk=good.pk cpk=comment.pk %}">ویرایش</a> |
                <a href="{% url 'delete_comment' pk=good.pk cpk=comment.pk %}">حذف</a>
                <p dir="ltr">
                <label>آیا این نظر مفید بود؟</label>
                <form action="{% url 'comment_votes' pk=good.pk cpk=comment.pk %}" dir="ltr" method="post">
                    {% csrf_token %}
                    <button dir="ltr" class="btn btn-primary btn-sm" type="submit" name="up"
                            value="{{comment.id}}">موافقم
                    </button>
                    <button dir="ltr" class="btn btn-primary btn-sm" type="submit" name="down"
                            value="{{comment.id}}">مخالفم
                    </button>
                </form>
                <p dir="ltr">
                    {{ comment.like_count }}like
                    {{ comment.dislike_count }}dislike
                </p>
                <hr>
                </p>
            </p>
            {% endfor %}

and i have another question
is it right to define manager in Comment class and use that custom manager in Vote class as i done that in first post?

Ok, I wasn’t fully reading all the code you posted.

The manager you show appears to be attached to the Vote model, which isn’t involved here. (Your query is based on Good, since that’s what your DetailView is showing.)

Probably the easiest way to do this is to override the get_object method in your detail view and put your query in there.

You’ll probably end up with something like:

get_object(self, queryset):
    pk = self.kwargs.get(self.pk_url_kwarg)
    comment_qs = Comment.objects.annotate(
        like_count=Count('comment_vote', filter=Q(comment_vote__type=True)),
        dislike_count=Count('comment_vote', filter=Q(comment_vote__type=False))
    )
    obj = Good.objects.prefetch_related(
        Prefetch('comments', queryset=comment_qs)
    ).get(pk=pk)
    return obj
    
Regarding your other question, a Manager should work primarily with the Model to which it is assigned. While that's not a physical requirement, doing otherwise will create confusion.

But for something like this, unless you're going to have multiple views using this same query, I don't think I'd create a manager for it. If it's really associated with this one view, then that's probably where this code belongs.

so i am really sorry for this question
how am i going to use the returned obj by get_object method in my template?

You already do.

The get method in the DetailView looks like this:

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        context = self.get_context_data(object=self.object)
        return self.render_to_response(context)

The first thing that get does is call get_object and populates self.object with it.

Then, it calls get_context_data, passing that new object.

The get_context_data includes the line:
context["object"] = self.object
which makes self.object available to the template by the name object - which is what you’re using in the template.

There are two resources I’ve been recommending to people to help understand how the Django-provided generic CBVs work - the Classy Class-Based Views site and the CBV diagrams page.

They help a lot with understanding what’s going on here.

1 Like

Thanks a lot for you answer it workd
i always learn from you
God bless you