2017-05-04 4 views
3

Djangoのクエリセット?から最新の値をクエリセットの注釈参加フィルタ私は、単一の式では、<em>多対1の関係</em><em>への注釈</em>の<em>フィルタ</em>結果から<em>最新</em>値を取得できますか

このおもちゃスキーマを考える:

  • 派生foo_statusbar_statusそれぞれ:(Loremモデル用)ジャンゴLoremAdminでカスタムクエリセットを作るために

    from django.db import models 
    
    class Lorem(models.Model): 
        """ Lorem ipsum, dolor sit amet. """ 
    
    class LoremStatusEvent(models.Model): 
        """ A status event change on a given `ipsum` for a `lorem`. """ 
    
        created = models.DateTimeField(auto_now_add=True) 
        lorem = models.ForeignKey(Lorem) 
        ipsum = models.CharField(max_length=200) 
        status = models.CharField(max_length=10) 
    

    、私がする必要があります個別のJOIN句からLoremStatusEventモデルへ:lorem.loremstatusevent_set__status

  • 各参加をフィルタリングすると、対応するIpsum値のイベントのみが含まれます。foo_status_events=LoremStatusEvent.filter(ipsum='foo')
  • ステータスイベントのセットを、対応する各参加の最新のものに集約します(foo_status=LoremStatusEvent.objects.filter(ipsum='foo').latest('created').status)。
  • 2つの追加値、foo_statusbar_statusqueryset = queryset.annotate(foo_status=???).annotate(bar_status=???)で結果に注釈を付けます。

私はこのすべてを行うには、いくつかの機能を考案した場合 - get_fieldlatest_byfilter_byloremstatus_set_of_every_corresponding_loremを - 私は、管理者を書くことができ、このような何か:

from django.contrib import admin 

class LoremAdmin(admin.ModelAdmin): 
    """ Django admin for `Lorem` model. """ 

    class Meta: 
     model = Lorem 

    def get_queryset(request): 
     """ Get the `QuerySet` of all instances available to this admin. """ 
     queryset = super().get_queryset(request) 
     queryset.annotate(
      foo_status=(
       get_field('status')(
        latest_by('created')(
         filter_by(ipsum='foo')(
          loremstatusevent_set_of_every_corresponding_lorem)))), 
      bar_status=(
       get_field('status')(
        latest_by('created')(
         filter_by(ipsum='bar')(
          loremstatusevent_set_of_every_corresponding_lorem)))), 
     ) 
     return queryset 

これらのプレースホルダ名のそれぞれを置き換える必要があり、実際どのような機能?

  • loremstatus_set_of_every_corresponding_loremは、Lorem.loremstatusevent_setに相当します。
  • filter_by右側の結合をフィルタリングする。
  • latest_by、結果セットをフィールド順に集約して、単一の最新インスタンスを取得する。私はそのようなdocumented aggregation functionが表示されません。
  • get_field、結果インスタンスからフィールドを参照する。

Lorem.loremstatusevent_setを介して関連するインスタンスにアクセスすることは、すべてLoremのクエリセットで行う必要があります。私はその時点でLoremStatusEventインスタンスを持っていないので、私はLoremStatusEventの属性を直接使用することはできません。

実際のDjangoの機能は上記のプレースホルダに含まれていますか?

+0

も参照してください:https://stackoverflow.com/questions/31234880/annotate-with-value-of-latest-related-in-django-1-8-using-conditional-annotation – Risadinha

答えて

3

あなたはbackport to 1.8+としても利用可能である新Subquery機能、でこれを行うことができるはずです。

try: 
    from django.db.models.expressions import Subquery, OuterRef 
except ImportError: 
    from django_subquery.expressions import Subquery, OuterRef 

class LoremAdmin(admin.ModelAdmin): 
    # … 

    def get_queryset(request): 
     queryset = super().get_queryset(request) 
     status_event_per_lorem = LoremStatusEvent.objects.filter(
      lorem=OuterRef('pk')) 
     latest_status_event_per_lorem = (
      status_event_per_lorem.order_by(
       'pk', '-created').distinct('pk')) 
     latest_status_event_for_ipsum_foo = (
      latest_status_event_per_lorem.filter(ipsum='foo')) 
     latest_status_event_for_ipsum_bar = (
      latest_status_event_per_lorem.filter(ipsum='bar')) 
     queryset = queryset.annotate(
      foo_status=Subquery(
       latest_status_event_for_ipsum_foo.values('status')[:1]), 
      bar_status=Subquery(
       latest_status_event_for_ipsum_bar.values('status')[:1]), 
     ) 

     return queryset 
+0

これはエラーを発生させます。 'ValueError:このクエリーセットは外部クエリへの参照を含み、サブクエリでのみ使用できます.' 'filter(...)'結果が 'latest(...)'に使用されるからです。 – bignose

+1

私はこれを行う方法を見つけましたが、そのエラーは発生しません。私はこの答えを編集しているので、意図どおりに動作します。 – bignose

0

これは私が問題を想像する方法です(これを正しく理解すれば)。それが実際にうまくいくかどうかは分かりません。それがうまくいけば、あまり効率的ではないかもしれません。

from django.db import models 

class LoremAdmin(admin.ModelAdmin): 
    … 
    def get_queryset(request): 
     """ Get the `QuerySet` of all instances available to this admin. """ 
     queryset = super().get_queryset(request) 
     queryset.annotate(
      foo_status=models.F('loremstatusevent_set').filter(ipsum='foo').latest('created').status, 
      bar_status=models.F('loremstatusevent_set').filter(ipsum='bar').latest('created').status, 
     ) 
+0

は面白そうですね。しかし、 'AttributeError: 'F'オブジェクトには 'filter'という属性がありませんので、' F'オブジェクトは必要なものではありません。 – bignose

+0

'models.F'を使ってそのような関係を構築することはできません。 –

関連する問題