2016-05-05 8 views
1

オブジェクトがまだ存在しない場合のデフォルト値であるリバースリレーションシップを照会する簡単な方法はありますか?関連オブジェクトまたはデフォルト

例をあげて詳しく説明します。

私は、次の基本的なモデルのモデルを持っている:

class Delegation(models.Model): 
    name = models.CharField(unique=True,max_length=4) 
    country = models.CharField(unique=True,max_length=100) 
    members = models.ManyToManyField(User, blank=True) 

class Exam(models.Model): 
    name = models.CharField(max_length=100, unique=True) 
    active = models.BooleanField(default=True) 

は、今私は(提出)アクションはまだ進行中であるか確定基本的にあれば、いくつかのアクションに関する情報を格納する必要があります。このため、私は両方のモデルにリンクするモデルを使用します。

class ExamAction(models.Model): 
    OPEN = 'O' 
    SUBMITTED = 'S' 
    STATUS_CHOICES = ((OPEN, 'In progress'), (SUBMITTED, 'Submitted')) 
    TRANSLATION = 'T' 
    POINTS = 'P' 
    ACTION_CHOICES = ((TRANSLATION, 'Translation submission'), (POINTS, 'Points submission')) 
    exam  = models.ForeignKey(Exam) 
    delegation = models.ForeignKey(Delegation) 
    action  = models.CharField(max_length=2, choices=ACTION_CHOICES) 
    status  = models.CharField(max_length=1, choices=STATUS_CHOICES, default=OPEN) 
    timestamp = models.DateTimeField(auto_now=True) 
    class Meta: 
     unique_together = (('exam', 'delegation', 'action'),) 

私の問題は、(一瞬)私はExamAction.OPENのデフォルト値を引き受けるそのため、アクションはまだDBに記載されていることができなかったことを前提としているため、クエリが、今はかなり複雑で取得することです。

具体的な例では、これは私がまだ代表団のために開かれている試験の一覧を照会するために、現時点で使用するものです。

exams_open = Exam.objects.filter((Q(examaction__delegation=delegation) & Q(examaction__action=ExamAction.TRANSLATION) & Q(examaction__status=ExamAction.OPEN)) 
    | Q(examaction__isnull=True), active=True,) 

私は上記のクエリでも間違っていると信じて、それは必要があるのでExamActionaction=TRANSLATIONdelegation=delegationが含まれていない場合はExamオブジェクトを返しますが、別のアクションが既に格納されていても(値は何でも)保存されません。

私はこの設計上の問題に遭遇した最初の人ではないと確信しています。もっと簡単な実装が存在しなければならないと思います。しかし何?

私は現在、検討していることである:

  1. ExamAction.objects.get_or_create()
    • 問題1:それはある:GETクエリが
    • が問題2一意である必要があります。すなわち、これは、複数の一致では動作しません。 には役に立ちません。オブジェクトをGETリクエストで作成してください。
  2. 新しいExamオブジェクトを作成すると、可能な限りExamActionが生成されます。これは、すべての代表団を通じてループすることを意味する。
    • 試験が生成された後に委任が追加されるとどうなりますか?
  3. のManyToMany関係は、この状況で役立ちますか?私はまだ、希望の(デフォルト)値を持つExamActionかまだ存在していないExamActionをクエリする必要があるので、そうは思わない。
  4. 完全に再設計されていますか?私は可能なアイデアを公開しています!

ありがとうございます。

答えて

0

私が正しくあなたの目的のクエリを理解していれば、私は(ここではManager上のカスタムメソッドなど)このようにそれを置く:

# UPDATE: Bugfix thanks to Mic D. 
class ExamManager(models.Manager): 
    def open_exams(delegation, action): 
     return Exam.objects.exclude(
      examaction__in=ExamAction.objects.filter(
       delegation=delegation, 
       action=action, 
       status=ExamAction.SUBMITTED, 
      ) 
     ).distinct() 

は、だからそれはあまりにも悪くはありません。あなたの提案されたソリューションで簡単に見て

  1. あなたはこの点に注意してくださいとは、一度に1つの行で動作します。

  2. ほとんどのこのような質問の場合、これが私の提案です。しかし、あなたのケースでは、それはデータベースのサイズを二次的に増加させ、新しい委任を追加するとより複雑になります。

  3. ManyToManyFieldは文法的な砂糖ですが、ここでは何もしません。

  4. これは良いアイデアかもしれませんが、実際には何か提案をするには十分な情報がありません。

あなたのアプローチは実際にはかなり妥当だと思うので、適切なクエリを見つけ出す必要があります。私が使用して終了

+0

フィードバックありがとうございます。マネージャでのクエリは、実際にははるかにエレガントであり、私は最初から維持するのがとても醜いわけではありません。 –

+0

私はビットポイント2を追求しようとしました。結局、大きな二次データベースを持つことになりました。私はちょうど始めにすべてのエントリを作成し、GETリクエストでの作成を避けるために、私はdelegationと試験。 –

+0

提案したクエリに問題が見つかりました。私はどのように修正し、いくつかの詳細なデバッグを投稿しました。 –

0

クエリは次のとおりです。

exams_open = Exam.objects.exclude(
    examaction__in=ExamAction.objects.filter(
     delegation=delegation, 
     action=ExamAction.TRANSLATION, 
     status=ExamAction.SUBMITTED) 
).distinct() 

これはKevin Christopher Henryに触発されたが、しかし、私はそれでバグを発見しました。それをデバッグするために、私は実際にprint QuerySet.queryで生成されたクエリを出力しなければなりませんでした。違いはかなり微妙ですが、私の場合は大きな違いがありました。

間違ったクエリ:

print Exam.objects.exclude(
      examaction__delegation=delegation, 
      examaction__action=action, 
      examaction__status=ExamAction.SUBMITTED, 
     ).query 

出力:ここ

SELECT "app_exam"."id", 
     "app_exam"."name" 
FROM "app_exam" 
WHERE NOT ("app_exam"."id" IN (SELECT U1."exam_id" AS Col1 
           FROM "app_examaction" U1 
           WHERE U1."action" = t) 
      AND "app_exam"."id" IN (SELECT U1."exam_id" AS Col1 
            FROM "app_examaction" U1 
            WHERE U1."delegation_id" = 1) 
      AND "app_exam"."id" IN (SELECT U1."exam_id" AS Col1 
            FROM "app_examaction" U1 
            WHERE U1."status" = s)) 

exclude()ステートメントのいずれかを満たす任意の試験が削除されます。ここでは正確に一度だけの試験を除外

SELECT "app_exam"."id", 
     "app_exam"."name" 
FROM "app_exam" 
WHERE NOT ("app_exam"."id" IN (SELECT V1."exam_id" AS Col1 
           FROM "app_examaction" V1 
           WHERE V1."id" IN (SELECT U0."id" 
                FROM "app_examaction" U0 
                WHERE (U0."delegation_id" 
                  = 1 
                  AND U0."action" = t 
                  AND U0."status" = s 
                  )))) 

print Exam.objects.exclude(
    examaction__in=ExamAction.objects.filter(
     delegation=delegation, 
     action=ExamAction.TRANSLATION, 
     status=ExamAction.SUBMITTED) 
).query 

出力:正しいクエリについては

関連する問題