How to add some context to each entry of a django QuerySet

无人久伴 提交于 2019-12-13 14:22:32

问题


I use Django 1.8.4 with Python 3.4

I have a model for tournaments that defines a method which returns a string if a subscription is forbidden.

class Tournament(models.Model):
    name = models.CharField(max_length=200, null=True, blank=True)
    subscriptions = models.ManyToManyField('ap_users.Profile')
    is_subscription_open = models.BooleanField(default=True)
    # ...

    def why_subscription_impossible(self, request):
        if not request.user.profile.is_profile_complete():
            return 'Your profile is not complete'
        elif not self.is_subscription_open:
            return 'Subscriptions are closed'
        elif <another_condition>:
            return 'Another error message'

        return None

I want to display the list of tournaments, using a generic ListView, and I want to use the result of the method to modify the way it is displayed:

<table class="table">
    <thead>
        <td>Tournament</td>
        <td>Subscription</td>
    </thead>
    {% for tournament in tournament_list %}
        <tr>
            <td>{{ tournament.name }}</td>
            <td>
                {% if tournament.why_subscription_impossible %}
                    {{ tournament.why_subscription_impossible }}
                {% else %}
                    <a href="{% url 'ap_tournament:subscribe' tournament.id %}">Subscribe</a>
                {% endif %}
            </td>
        </tr>
    {% endfor %}
</table>

The view is a class based generic view inherited from generic.ListView.

class IndexView(generic.ListView):
    template_name = 'ap_tournament/index.html'

    def get_queryset(self):
        return Tournament.objects.all()

The shown solution doesn't work, because I need to pass the current request, to get information about logged user. So I tried to add the result of the method to a context in the view

class IndexView(generic.ListView):
    template_name = 'ap_tournament/index.html'

    def get_queryset(self):
        return Tournament.objects.all()

    def get_context_data(self, **kwargs):
        context = super(IndexView, self).get_context_data(**kwargs)

        additional_ctx_info = []
        for tournament in self.get_queryset():
            additional_ctx_info.append({
                'reason_to_not_subscribe': tournament.why_subscription_impossible(self.request)
            })

        context['subscr_info'] = additional_ctx_info
        return context

Obviously, this doesn't work too. I don't know how to access to the subscr_info[n] with n the current index in the tournament_list. I know the forloop.counter0 to get the index, but I can't use it in the template (or I don't know how). I tried :

  • {{ subscr_info.forloop.counter0.reason_to_not_subscribe }}
  • {{ subscr_info.{{forloop.counter0}}.reason_to_not_subscribe }}

I also tried to annotate the QuerySet in get_queryset() view method and read about aggregate(), but I feel that works only with operations supported by the database (AVG, COUNT, MAX, etc.).

I also feels that using a filter or a template tag will not work in my case since I need to use the result of the method in a if tag.

Is there a better solution or a completely diffferent method to achieve what I want ?


回答1:


In your view, you could also do:

tournaments = self.get_queryset()
for tournament in tournaments:
    tournament.reason_to_not_subscribe = tournament.why_subscription_impossible(self.request)

Then add tournaments to the context.




回答2:


You have to create tournament_list in your views.py file, and set it depending on whether the user is logged and has the corresponding permissions.

If you need to count something, you can create the following Counter class :

class Counter:
    count = 0

    def increment(self):
        self.count += 1
        return ''

    def decrement(self):
        self.count -= 1
        return ''

You can then use it in your templates by calling {{ counter.increment }} and {{ counter.count }}. ({{ subscr_info.{{counter.count}}.reason_to_not_subscribe }} and don't forget to place {{ counter.increment }} in your loop.

However, a nicer workaround I used was to create a dictionnary containing both the main element and the additional information, i.e.

ctx_info = []
for tournament in self.get_queryset():
    ctx_info.append({
        'tournament': tournament
        'reason_to_not_subscribe': tournament.why_subscription_impossible(self.request)
    })

and then loop on ctx_info. It's cleaner, but I however do not know how this can be implemented within a ListView (which I never used)

By the way, your template contains {{ why_subscription_impossible }} instead of {{ tournament.why_subscription_impossible }}, I don't know if it was intended...



来源:https://stackoverflow.com/questions/32683954/how-to-add-some-context-to-each-entry-of-a-django-queryset

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!