相关对象或默认

时间:2016-05-05 12:47:54

标签: django django-models django-queryset

是否有一种简单的方法来查询对象的反向关系过滤,如果该对象尚不存在,则该值是默认值?

让我用一个例子详细说明。

我有以下基本型号型号:

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

对于一个具体的例子,我现在使用这个来查询仍然为代表团开放的考试列表:

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

我认为上述查询也是错误的,因为如果ExamExamActionaction=TRANSLATION没有delegation=delegation,它应返回ExamAction.objects.get_or_create()个对象,但它不会如果已经存储了另一个动作(具有任何值)。

我很确定我不是第一个遇到这个设计问题的人,我认为必须存在一个更简单的实现。但是什么?

我目前正在考虑的是:

  1. ExamAction
    • 问题1:这对多个匹配不起作用,即get查询应该是唯一的
    • 问题2:在GET请求中创建对象并不好。
  2. 在创建新的Exam对象时生成所有可能的through=ExamAction。这意味着所有代表团都要循环。
    • 如果在生成考试后添加了委托会怎么样?
  3. <html> <head> <title>DZ Prototype</title> <link rel="icon" type="img/ico" href="images/favicon.jpg"> </head> <body> <center> <h1>Welcome to DZ Prototype Testing Area!!</h1> </center> <p></p> <p></p> <p></p> <p style="text-align:center"><img src="http://images1.knowable.com/live/articles/2_1ec282dc7578c9356aef339b8b98bbe9.gif" alt="It works!!"></p> <p></p> <p></p> <div align="center"> <?php //$theFile = "cowrie.txt"; //$line = file($theFile); //echo $line[18]; // Simulated line array for demonstraiton purposes $line = array("ewfefe", "ewfewfewf", "54g5grthrhryt", "Incorrectly Classified Instances 1 100 %"); if (trim($line[3]) == "Incorrectly Classified Instances 1 100 %") { echo "<h2><font color=red>Possible Malicious Login Attempt</h2>"; } else { echo "<h2><font color=green>Status Green</h2>"; } ?> </div> </body> </html> 的ManyToMany关系是否有助于这种情况?我不这么认为,因为我仍然应该查询具有所需(默认)值或尚不存在的ExamAction。
  4. 完全重新设计?我对可能的想法持开放态度!
  5. 非常感谢。

2 个答案:

答案 0 :(得分: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. 如您所知,这一次只能在一行上运行。

  2. 对于大多数这样的问题,这就是我的建议。但是在你的情况下,它会以二次方式增加数据库的大小,当你添加一个新的委托时,它会使生活变得更加复杂。

  3. ManyToManyField只是一些语法糖,在这里对你没有任何帮助。

  4. 这可能是一个好主意,但这里没有足够的信息可以提出任何建议。

  5. 所以我认为你的方法实际上非常合理,你只需要弄清楚正确的查询。

答案 1 :(得分:0)

我最终使用的查询是:

Find

这是受Kevin Christopher Henry的启发,但我发现它有一个错误。为了调试它,我实际上必须使用exams_open = Exam.objects.exclude( examaction__in=ExamAction.objects.filter( delegation=delegation, action=ExamAction.TRANSLATION, status=ExamAction.SUBMITTED) ).distinct() 打印生成的查询。差异非常微妙,但就我而言,它产生了巨大的差异。

查询错误:

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

输出:

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

这里我们只正确地排除了一次考试。