Django-多重过滤查询集返回空查询集

时间:2018-06-22 08:58:06

标签: python django django-queryset django-filter django-2.0

我在Django 2.0中有一个查询集问题,经过一些研究,我发现没有任何问题像我的一样。

我认为这是因为我的旧数据库是由我不认识的人创建的。

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

您已经看到,表属性没有primary_key,所以我用Django models命令制作了一个inspectdb,如下所示:

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为空,但应返回大约16XXX Record 我不知道会发生什么?

因为我执行以下查询:

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

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

这些请求的目的是使用特定的日期和产品名称来获取Record

itemProperties字段的9种可能性可以是:

        
  • 产品
  •     
  • 版本
  •     
  • 工具
  •     
  • 邮票
  •     
  • 用户
  •     
  • 主机
  •     
  • 站点
  •     
  • 项目
  •     
  • args

具有相同逻辑的将来查询将立即应用,以通过产品站点获得版本

谢谢您的帮助! 对不起,我的英语不好:)

1 个答案:

答案 0 :(得分:1)

要回答我的问题,

首先,我停止尝试使用多用户.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()

在第一个.filter记录对象丢失对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通过以下线程:Django QuerySet Cast

顺便说一句,这是我找到的最有效的方式。
但是,如果运行此视图,则将具有以下运行时: view query runtime

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

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