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
出来,并附上具体的日期和产品名称。
Properties
中item
字段的9种可能是:
- 产品
- 版本
- 工具
- 戳
- 用户
- 主机
- 站点
- 项目
- 参数
在通过 product 和 site 获取 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()
在第一个 .filter
Record 对象丢失对 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.id
和site.id
之间的交集很长(超过2分钟)。
如果有人知道进行这些查询的更好方法,请告诉我们:)
希望我能帮到别人。
我在 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
出来,并附上具体的日期和产品名称。
Properties
中item
字段的9种可能是:
- 产品
- 版本
- 工具
- 戳
- 用户
- 主机
- 站点
- 项目
- 参数
在通过 product 和 site 获取 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()
在第一个 .filter
Record 对象丢失对 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.id
和site.id
之间的交集很长(超过2分钟)。
如果有人知道进行这些查询的更好方法,请告诉我们:)
希望我能帮到别人。