【问题标题】:How to add some context to each entry of a django QuerySet如何为 django QuerySet 的每个条目添加一些上下文
【发布时间】:2023-03-20 05:55:01
【问题描述】:

我使用 Django 1.8.4 和 Python 3.4

我有一个锦标赛模型,它定义了一个在订阅被禁止时返回字符串的方法。

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

我想显示锦标赛列表,使用一个通用的 ListView,我想使用方法的结果来修改它的显示方式:

<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>

视图是一个基于类的通用视图,继承自generic.ListView

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

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

显示的解决方案不起作用,因为我需要传递当前请求,以获取有关登录用户的信息。所以我尝试将方法的结果添加到视图中的上下文中

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

显然,这也行不通。我不知道如何使用锦标赛列表中的当前索引访问subscr_info[n]。我知道forloop.counter0 来获取索引,但我不能在模板中使用它(或者我不知道如何)。我试过了:

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

我还尝试在 get_queryset() 视图方法中注释 QuerySet 并阅读了有关 aggregate() 的信息,但我觉得这只适用于数据库支持的操作(AVG、COUNT、MAX 等)。

我也觉得在我的情况下使用过滤器或模板标签不起作用,因为我需要在if标签中使用方法的结果。

是否有更好的解决方案或完全不同的方法来实现我想要的?

【问题讨论】:

  • 我认为 why_subscription_impossible 方法应该放在其他地方,但不在模型中。模型方法应该对对象的属性做一些事情(并且应该特定于该对象/行,否则对于多行将其放在模型管理器中)
  • here 表示从 django 1.8 开始,现在可以在 annotate 中使用任何类型的表达式。因此,为避免进行多个查询(在 get_context_data 中获取查询集),请将您的方法移到任何类之外(例如到您的 view.py)并在 get_queryset 中执行return Tournament.objects.annotate(reason=why_subscription_impossible(self.request))。试试看它是否有效,这将是最干净的解决方案。
  • 这个问题的例子被简化了,但是这个方法实际上执行了很多检查,包括锦标赛的字段。出于这个原因,我可以将它移动到其他地方(并向函数添加锦标赛参数),但我不能使用这个新函数作为注释(因为我不能在每次调用时传递不同的锦标赛对象)。我更新了代码 sn-ps。

标签: python django django-queryset


【解决方案1】:

你必须在你的views.py文件中创建tournament_list,并根据用户是否登录并有相应的权限来设置。

如果需要计数,可以创建如下 Counter 类:

class Counter:
    count = 0

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

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

然后您可以通过调用{{ counter.increment }}{{ counter.count }} 在模板中使用它。 ({{ subscr_info.{{counter.count}}.reason_to_not_subscribe }} 并且不要忘记将 {{ counter.increment }} 放在您的循环中。

但是,我使用的一个更好的解决方法是创建一个包含主要元素和附加信息的字典,即

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

然后在 ctx_info 上循环。它更干净,但是我不知道如何在 ListView 中实现它(我从未使用过)

顺便说一句,你的模板包含{{ why_subscription_impossible }}而不是{{ tournament.why_subscription_impossible }},我不知道是不是有意...

【讨论】:

  • 我在这里不需要你的 Counter 类,因为内置的 {{ forloop.counter }} 和 {{ forloop.counter0 }} 做同样的事情。我更新了问题以修复您在我的模板中提到的错误。谢谢!我会尽快尝试您的解决方案,看看它是否对我有帮助。
  • {{ subscr_info.{{counter.count}}.reason_to_not_subscribe }} 不是有效语法 (stackoverflow.com/questions/7569133/…)。
【解决方案2】:

在你看来,你也可以这样做:

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

然后将tournaments 添加到上下文中。

【讨论】:

  • 是的,我会在最后机会保留这个解决方案,但它对我来说似乎有点不雅。我会等几个小时看看是否有人有其他解决方案。
  • 毕竟,您的解决方案是唯一有效的解决方案。接受
猜你喜欢
  • 2018-12-14
  • 2011-02-08
  • 2010-11-01
  • 2011-03-05
  • 1970-01-01
  • 2014-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多