2017-10-12 107 views
2

最大列值我有3種相關機型:Django的ORM過濾器由兩個相關模型

Program(Model): 
    ... # which aggregates ProgramVersions 

ProgramVersion(Model): 
    program = ForeignKey(Program) 
    index = IntegerField() 

UserProgramVersion(Model): 
    user = ForeignKey(User) 
    version = ForeignKey(ProgramVersion) 
    index = IntegerField() 

PROGRAMVERSION和UserProgramVersion基於index場訂購的型號 - 對象與表中的最高index被認爲是最新/最新對象(這是由一些自定義邏輯處理的,不相關)。

我想選擇所有最新的UserProgramVersion,即最新的UPV的指向同一個程序。

這可以通過這個UserProgramVersion查詢集進行處理:

def latest_user_program_versions(self): 
    latest = self\ 
     .order_by('version__program_id', '-version__index', '-index')\ 
     .distinct('version__program_id') 

    return self.filter(id__in=latest) 

這工作正常但即時尋找不使用.distinct溶液() 我想是這樣的:

def latest_user_program_versions(self): 
    latest = self\ 
     .annotate(
      'max_version_index'=Max('version__index'), 
      'max_index'=Max('index'))\ 
     .filter(
      'version__index'=F('max_version_index'), 
      'index'=F('max_index')) 

    return self.filter(id__in=latest) 

但是這不起作用

+0

我不認爲它「工作正常」沒有按用戶或用戶組過濾。如果你現在忽略它,你可能會得到一個答案,以後無法添加它。 – hynekcer

回答

2

使用Subquery() expressions在Django 1.11。文檔中的示例類似,目的也是爲所需的父記錄獲取最新的項目。

如果你大大改善它,寫在溶液(你也許可以用你的對象的例子開始,但我也寫了一個完整的更復雜的建議,以避免可能出現的性能缺陷。)

from django.db.models import OuterRef, Subquery 

... 
def latest_user_program_versions(self, *args, **kwargs): 
    # You should filter users by args or kwargs here, for performance reasons. 
    # If you do it here it is applied also to subquery - much faster on a big db. 
    qs = self.filter(*args, **kwargs) 
    parent = Program.objects.filter(pk__in=qs.values('version__program')) 
    newest = (
     qs.filter(version__program=OuterRef('pk')) 
     .order_by('-version__index', '-index') 
    ) 
    pks = (
     parent.annotate(newest_id=Subquery(newest.values('pk')[:1])) 
     .values_list('newest_id', flat=True) 
    ) 
    # Maybe you prefer to uncomment this to be it compiled by two shorter SQLs. 
    # pks = list(pks) 
    return self.filter(pk__in=pks) 

您回答。


編輯你在你的第二個解決問題
沒有人能切分支下他,無論是在SQL,但我可以在一個子查詢坐在它的臨時副本,以能夠生存下去: - )這也是爲什麼我在開始時要求過濾器的原因。第二個問題是Max('version__index')和Max('index')可能來自兩個不同的對象,並且找不到有效的交集。


EDIT2驗證:從我的查詢內部SQL是複雜的,但似乎是正確的。

SELECT app_userprogramversion.id,... 
FROM app_userprogramversion 
WHERE app_userprogramversion.id IN 
    (SELECT 
     (SELECT U0.id 
     FROM app_userprogramversion U0 
     INNER JOIN app_programversion U2 ON (U0.version_id = U2.id) 
     WHERE (U0.user_id = 123 AND U2.program_id = (V0.id)) 
     ORDER BY U2.index DESC, U0.index DESC LIMIT 1 
     ) AS newest_id 
    FROM app_program V0 WHERE V0.id IN 
     (SELECT U2.program_id AS Col1 
     FROM app_userprogramversion U0 
     INNER JOIN app_programversion U2 ON (U0.version_id = U2.id) 
     WHERE U0.user_id = 123 
     ) 
    )