【问题标题】:Annotations are ignored in Django/Tastypie注释在 Django/Tastypie 中被忽略
【发布时间】:2012-09-09 17:58:54
【问题描述】:

在我的 Tastypie 资源中,我正在注释我的查询集,但我没有看到该注释流向 JSON Tastypie 生成并传回。代码很简单:

class CompetitionResource(ModelResource):
    total_tickets = fields.IntegerField(readonly=True)

    class Meta:
        queryset = Competition.objects.all().annotate(total_tickets=Count('ticket__ticketownership__user__id', distinct=True))

我在查询集中生成和注释的那个计数根本不会出现在最终的 JSON 中。最终的 JSON 有一个 total_users 字段(因为我在 ModelResource 中声明了一个),但它为空。我是否遗漏了任何明显的东西来确保通过这样的注释?如果没有,有什么办法可以解决这个问题?

一种方法是在我的模型中创建一个属性,然后将我的 ModelResource 中的 total_users 字段绑定到该属性。但这可能会导致我从数据库中提取的每个竞赛的计数查询,这并不好。我想在一个注释类型的查询中做到这一点。

【问题讨论】:

  • 我之前没见过annotate()all() 一起使用。如果你只使用Competition.objects.annotate(total_tickets=Count('ticket__ticketownership__user__id', distinct=True))// 会发生什么编辑:我刚刚查看了 Django 代码,这些调用应该做同样的事情......对不起。
  • 是的,这应该没什么区别。上面的 annotate() 代码工作正常,只是它没有传递到最终的 JSON 输出。

标签: django annotations tastypie


【解决方案1】:

我认为您的问题可能与 Tastypie 文档中针对queryset 给出的警告有关:

如果您在其中放置任何可调用对象,它们只会被评估一次(当 Meta 类被实例化时)。这尤其会影响与日期/时间相关的事物。请参阅 :ref:cookbook 了解解决方法。

看着食谱中的relevant section,我认为你应该尝试这样的事情:

class CompetitionResource(ModelResource):
    total_users = fields.IntegerField(readonly=True)

    class Meta:
        queryset = Competition.objects.all()

    def get_object_list(self, request):
        return super(CompetitionResource, self).get_object_list(request).annotate(total_tickets=Count('ticket__ticketownership__user__id', distinct=True))

【讨论】:

  • 好主意 - 太好了,事实上我之前也尝试过......但它也不起作用。 total_tickets 仍然为空(顺便说一下,您的代码中有一个小错误,您的字段名称是 total_users 并且您的注释名称是 total_tickets,但它无论如何都不起作用)。我会继续寻找。不过非常感谢。
【解决方案2】:

好的,我明白了。您可以简单地使用可以添加到 ModelResource 的自定义 dehydrate_[field name] 方法。对于每个 ModelResource 字段,Tastypie 会检查您是否指定了 dehydrate_[field name] 方法,如果您指定了,它会在将对象处理成包时调用该方法(然后以 JSON 或 XML 或其他形式输出)。这个 dehydrate_[field name] 方法获取 Tastypie 在此之前为该特定对象创建的包。好消息是这个bundle中有原始对象,在bundle.obj下。并且该对象仍将具有您在 get_object_list 中提供的原始注释(如上面的答案所示)。所以你可以使用下面的代码。

class CompetitionResource(ModelResource):
    total_tickets = fields.IntegerField(readonly=True)

    class Meta:
        queryset = Competition.objects.all()

    def get_object_list(self, request):
        return super(CompetitionResource, self).get_object_list(request).annotate(total_tickets=Count('ticket__ticketownership__user__id', distinct=True))

    def dehydrate_total_tickets(self, bundle):
        return bundle.obj.total_tickets

从自定义 dehydrate_[field name] 方法返回的任何内容都将作为该字段的最终值正确存储在该对象的包中,然后正确处理为输出。

【讨论】:

    【解决方案3】:

    它没有出现在文档中,但是查看源代码有一个 attribute 参数可以传递给字段声明,该参数可用于将其绑定到模型实例。

    可选地接受一个attribute,它应该是一个字符串 实例属性或可在对象中调用 dehydrate 或在hydrate 期间将数据推送到对象上。 默认为None,表示将手动访问数据。

    因此,对于您的示例,以下内容应该可以解决问题。

    class CompetitionResource(ModelResource):
        total_tickets = fields.IntegerField(readonly=True, attribute='total_tickets')
    
        class Meta:
            queryset = Competition.objects.all().annotate(total_tickets=Count('ticket__ticketownership__user__id', distinct=True))
    

    dehydrate 解决方案适用于使用值填充发送的对象,但不允许您轻松利用 Tastypie 的一些其他功能,例如内置过滤(我相信排序)。使用带有属性参数的字段定义。

    【讨论】:

      猜你喜欢
      • 2012-05-19
      • 2015-01-09
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-05
      • 1970-01-01
      相关资源
      最近更新 更多