【问题标题】:Exclude models based on a value from another model with no foreign key基于来自另一个没有外键的模型的值排除模型
【发布时间】:2020-01-17 06:46:08
【问题描述】:

我有以下架构

class Review(models.Model):
    comment_id = models.CharField(max_length=32)

class Comment(models.Model):
    comment_id = models.CharField(max_length=32)
    uploaded = models.DateTimeField()

注意 1 除了 ReviewComment 模型具有完全相同的 comment_id 之外,没有外键

注意 2 存在一对一/唯一关系(每个表中只会出现一次 comment_id

我想进行选择查询以获取所有 Review 模型,它们的相对 Comment 模型不旧超过 10 天。

【问题讨论】:

  • 一开始你为什么不使用ForeignKey
  • 这是遗留代码,数据库架构的更改被阻止...
  • 但是将其声明为 Django 模型中的 OneToOneField 需要对架构进行任何更改。
  • @DanielRoseman:FK 将创建一个 FOREIGN KEY 约束,对于 OneToOne 它可能还会添加一个 UNIQUE 约束。但我同意,就列/...而言,您可以使用to_field,它不会使用不同的字段类型。
  • CI/CD 不会自动运行数据库迁移。这意味着如果我对 Schema 进行了更改,我必须报告它并且有人运行这些迁移两次(暂存/生产环境)

标签: python django python-3.x postgresql django-queryset


【解决方案1】:

选项 1:添加关系(强烈建议

我强烈建议在这里使用OneToOneField,因为这将保证参照完整性,并检查唯一性。您可以使用以下方法构建此类模型:

class Review(models.Model):
    comment = models.OneToOneField(
        'Comment',
        to_attr='comment_id',
        on_delete=models.CASCADE
    )

class Comment(models.Model):
    comment_id = models.CharField(max_length=32, unique=True)
    uploaded = models.DateTimeField(auto_now_add=True)

这不仅加强了完整性,而且使查询更加方便,我们可以简单地查询:

from django.utils import timezone
from datetime import timedelta

Review.objects.filter(
    comment__uploaded__gt=(timezone.now()-timedelta(days=10)).date()
).values('comment_id')

选项 2:使用嵌套查询进行查询

如果这完全不可能,你可以使用:

from django.utils import timezone
from datetime import timedelta

comments = Comment.objects.filter(
    uploaded__gt=(timezone.now()-timedelta(days=10)).date()
).values('comment_id')

然后我们可以通过以下方式获取相应的评论:

Review.objects.filter(
    comment_id__in=comments
)

上面将生成一个如下所示的查询:

SELECT review.*
FROM review
WHERE review.comment_id IN (
    SELECT U0.comment_id
    FROM comment U0
    WHERE U0.uploaded > 2019-09-06 00:00:00
)

【讨论】:

  • 我想将这两个查询合并为一个,但我不确定这是否可行。选项 2 有效,因此我将其标记为已接受的答案。谢谢
【解决方案2】:

根据您的模型,您可以执行以下操作:

comments = Comment.objects.filter(uploaded > (the_days_specified)).get(comment_id)
reviews = Review.objects.filter(comment_id__in=comments)

【讨论】:

  • (我假设你会写 values_list 而不是 get)。这个解决方案的问题是Comment 模型的数量大约是 800.000+。而且您的第一个查询会很慢。我想要一个解决方案,将这个过滤变成一个查询
  • 我认为 cmets = Comment.objects.filter(uploaded > (the_days_specified)).get(comment_id) 是错误的查询,可能是 cmets = Comment.objects.filter(uploaded__gt =(the_days_specified))。 get(comment_id=comment_id) 是当您需要“大于”过滤并需要“key = value”对时的正确过滤方式
  • @dios231:一个查询集,.values() 没有被评估,所以你永远不会真正将comment_ids 拉到内存中。
【解决方案3】:

你可以像这样编写原始查询:

mydate = timezone.now()-timedelta(days=10) ##you need import timezone.now and timedelta
query = '''SELECT * FROM app_review as r 
                      INNER JOIN app_comment as c 
                      ON r.comment_id = c.comment_id
                      WHERE c.uploaded>{date}'''
Review.objects.raw(query.format(date=mydate))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-01
    • 1970-01-01
    • 2018-04-15
    • 2019-08-15
    • 2020-06-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多