Django - 多过滤查询集 return 空查询集

Django - Multi filtering queryset return empty queryset

我在 Django 2.0 中遇到了查询集的问题,经过一些研究,我没有发现任何问题看起来像我的。

我认为这是因为我不认识的人创建了我非常古老的遗留数据库。

所以,我有一个如下所示的 sqlite 数据库:

你看到了吗,Table 属性没有 primary_key,所以我用 django inspectdb 命令做了一个 models,看起来像这样:

from django.db import models

class Record(models.Model):
    id = models.IntegerField(db_column='ID', primary_key=True)

    class Meta:
        db_table = 'Records'

    def __str__(self):
        return "%s" % self.id


class Propertie(models.Model):
    id = models.ForeignKey(Record, models.DO_NOTHING, db_column='ID', primary_key=True)
    item = models.CharField(db_column='Item', max_length=500)
    value = models.CharField(db_column='Value', max_length=500)

    class Meta:
        db_table = 'Properties'

    def __str__(self):
        return '[%s]- %s -> %s' % (self.item, self.value, self.id)

我将 Properties.id 设置为 primary_key 但它是一个 ForeignKey 并且 Django 说将此字段设置为 OneToOneField 这是正常和合乎逻辑的,但是 1 Record 链接到 9 Properties 所以 Porpertie.id 不可能 unique 这是我的第一个问题,因为我无法更改数据库。

我的第二个真正的问题是当我运行这个查询时:

def my_view(request):

   epoch = datetime.date(1970, 1, 1)
   period_from = stat_form.cleaned_data.get("period_from")
   period_to = stat_form.cleaned_data.get("period_to")
   product = stat_form.cleaned_data.get("kit")

   timestamp_from = period_from - epoch
   timestamp_to = period_to - epoch

   records = Record.objects.using("statool").filter(
        propertie__item="product",
        propertie__value=product,
    ).filter(
        propertie__item="stamp",
        propertie__value__gt=str(int(timestamp_from.total_seconds())),
        propertie__value__lt=str(int(timestamp_to.total_seconds())),
    ).count()

这个 QuerySet 是空的,但它应该 return 大约 16XXX Record 我不知道会发生什么?

因为如果我执行此查询:

  records = Record.objects.using("statool").filter(
        propertie__item="product",
        propertie__value=product,
  )

它 return 是一个结果,但第二个过滤器不起作用...

这些请求的目标是让 Record 出来,并附上具体的日期和产品名称。

Propertiesitem字段的9种可能是:

在通过 productsite 获取 version 之后,将应用具有相同逻辑的未来查询.

感谢您的帮助! 抱歉我的英语不好:)

回答我的问题,

首先我已经停止尝试用户 multi .filter 因为当我 运行:

records = Record.objects.using("statool").filter(
    propertie__item="product",
    propertie__value=product,
).filter(
    propertie__item="stamp",
    propertie__value__gt=str(int(timestamp_from.total_seconds())),
    propertie__value__lt=str(int(timestamp_to.total_seconds())),
).count()

在第一个 .filterRecord 对象丢失对 propertie_set 的引用之后,我无法按属性进行过滤。

如@ukemi 和@Ralf 所说,使用:

.filter(
    propertie__item="stamp",
    propertie__value__gt=str(int(timestamp_from.total_seconds())),
    propertie__value__lt=str(int(timestamp_to.total_seconds())),
)

进行精确查询是一个非常糟糕的主意。

所以这是我的解决方案:

def select_stats(request):
    epoch = datetime.date(1970, 1, 1)
    period_from = stat_form.cleaned_data.get("period_from")
    period_to = stat_form.cleaned_data.get("period_to")
    product = stat_form.cleaned_data.get("kit")

    timestamp_from = period_from - epoch
    timestamp_to = period_to - epoch
    timestamp_from = int(timestamp_from.total_seconds())
    timestamp_to = int(timestamp_to.total_seconds())

    all_product = Propertie.objects.using("statool").filter(
        item="product",
        value=product
    ).values_list("id", flat=True)

    all_stamp = Propertie.objects.using("statool").annotate(
        date=Cast("value", IntegerField())
    ).filter(
        date__gte=timestamp_from,
        date__lt=timestamp_to
    ).values_list("id", flat=True)

    all_records = Record.objects.using("statool").filter(
        id__in=all_product.intersection(all_stamp)
    )

    all_recorded_propertie = Propertie.objects.using("statool").filter(id__in=all_records)

    all_version = all_recorded_propertie.filter(
        id__in=all_records,
        item="version"
    ).values_list("value", flat=True).distinct()

    all_site = all_recorded_propertie.filter(
        id__in=all_records,
        item="site"
    ).values_list("value", flat=True).distinct()

    stats_site = {}
    for version in all_version:
        stats_site[version] = {}
        id_version = all_recorded_propertie.filter(
            item="version",
            value=version
        ).values_list("id", flat=True)
        for site in all_site:
            id_site = all_recorded_propertie.filter(
                item="site", 
                value=site
            ).values_list("id", flat=True)
            stats_site[version][site] = id_version.intersection(id_site).count()

通过这种方式解决时间戳问题:

all_stamp = Propertie.objects.using("statool").annotate(
    date=Cast("value", IntegerField())
).filter(
    date__gte=timestamp_from,
    date__lt=timestamp_to
).values_list("id", flat=True)

感谢来自此线程的@erikreed:

顺便说一句,这是我发现的最有效的工作方式。
但是如果我们 运行 这个视图我们有这个 运行 时间: view query runtime

正如你所见,每个QuerySet都非常快,但是version.idsite.id之间的交集很长(超过2分钟)。

如果有人知道进行这些查询的更好方法,请告诉我们:)
希望我能帮到别人。