简体   繁体   English

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

[英]Django - Multi filtering queryset return empty queryset

I have a problem with queryset in Django 2.0, after some research, I don't find any problem looks like mine. 我在Django 2.0中的queryset有问题,经过一些研究,我发现没有任何问题像我的一样。

I think it's because of my very old legacy database create by someone I didn't know. 我认为这是因为我不认识的人创建了非常旧的旧数据库。

So, I have a sqlite database who looks like this: 因此,我有一个如下所示的sqlite数据库:

Has you can see, the Table Properties don't have primary_key , so i made a models with django inspectdb command who looks like this: 您是否看到,表属性没有primary_key ,所以我使用django inspectdb命令制作了一个models ,该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)

I set Properties.id as primary_key but it's a ForeignKey and Django say to set this field as OneToOneField and it's normal and logic, but 1 Record is linked to 9 Properties so Porpertie.id can't be unique this is my first problem because I can't alter the database. 我将Properties.id设置为primary_key但是这是一个ForeignKey并且Django说要将此字段设置为OneToOneField ,这是正常现象和逻辑,但是1 Record链接到9个Properties因此Porpertie.id不能unique这是我的第一个问题,因为我无法更改数据库。

My second and real problem is when I run this query: 我的第二个也是真正的问题是当我运行此查询时:

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

this QuerySet is empty but it should return approximately 16XXX Record I don't know what happens? QuerySet为空,但应返回大约16XXX Record我不知道会发生什么?

Because if I do this query: 因为如果执行此查询:

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

It returns a result but the second filter doesn't work ... 它返回结果,但是第二个过滤器不起作用...

The goal of those request is to get the Record out with the specific date and product name. 这些要求的目的是让Record了与特定日期和产品名称。

the 9 possibilities of item field in Properties can be: Propertiesitem字段的9种可能性可以是:

  • product 产品
  • version
  • tool 工具
  • stamp 邮票
  • user 用户
  • host 主办
  • site 现场
  • project 项目
  • args ARGS

A future query with the same logic will be applied just after to get version by product and by site . 具有相同逻辑的将来查询将在之后应用,以按产品站点获取版本

Thank you for your help! 谢谢您的帮助! And sorry for my bad English :) 对不起,我的英语不好:)

To answer my problem, 为了回答我的问题,

first i have stoped to try user multi .filter because when i run: 首先,我已.filter尝试使用用户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()

After the first .filter Record objects lost reference to propertie_set so i can't filter by propertie. 在第一个.filter Record对象之后,丢失了对propertie_set引用,因此我无法按属性进行过滤。

As say @ukemi and @Ralf, using: 如@ukemi和@Ralf,使用:

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

is a really bad idea to have exact query. 进行精确查询是一个非常糟糕的主意。

So this is my solution: 所以这是我的解决方案:

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

To solve timestamp problem by this way: 通过以下方式解决时间戳问题:

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)

Thank's to @erikreed from this thread: Django QuerySet Cast 感谢@erikreed这个线程: Django QuerySet Cast

By the way, this is the most efficient way i've found to do my job. 顺便说一句,这是我找到的最有效的工作方式。
But if we run this view we have this runtime: view query runtime 但是,如果运行此视图,则将具有以下运行时: 视图查询运行时

As you can see, every QuerySet are very fast, but intersections between version.id and site.id are very long (more than 2 minutes). 如您所见,每个QuerySet都非常快,但是version.idsite.id之间的交集非常长(超过2分钟)。

If someone know a better way to do those query, just let us know :) 如果有人知道做这些查询的更好方法,请告诉我们:)
Hope I help someone. 希望我能帮助别人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM