函数的一部分是计算一些较低,相等且高于a_price
的最后有效商品。无法找到更好的方法所以我现在使用两个循环。问题是这会执行575个查询,这太多了。
一种产品可以有很多买家和买家有很多优惠(具有不同的日期时间)。我试图添加prefetch_related('buyers')
,但它根本没用。
编辑:在这种情况下,有32种产品,每种产品有0到30位买家。
reset_queries()
products = Product.objects.filter(user=user)
my_active_products = products.filter(active=True).prefetch_related('buyers')
for product in my_active_products:
for buyer in product.buyers.filter(valid=True):
last_valid_offer = buyer.get_last_valid_offer()
a_price = product.a_price
if a_price:
if a_price < last_valid_offer.eur_price:
cheaper += 1
elif a_price > last_valid_offer.eur_price:
more_expensive += 1
elif a_price == last_valid_offer.eur_price:
equal += 1
else:
unknown += 1
print len(connection.queries)
您知道我该怎么做才能减少查询次数吗?
EDIT Models.py:
class Product(Model):
name...
active = BooleanField(...)
class Buyer(Model):
product = ForeignKey('Product',related_name='buyers')
def get_last_valid_offer(self):
return self.offers.filter(valid=True).latest('datetime')
class Offer(Model):
buyer = ForeignKey('Buyer', related_name='offers')
valid = BooleanField(...)
datetime = DateTimeField(...)
eur_price = MoneyField(...)
答案 0 :(得分:1)
我相信你只需要一个带有一些连接和总和的查询就可以实现这一点。
如果您提供数据库的架构,我可以尝试详细说明答案,现在我将假设以下内容。
create table user (
id integer primary key,
active boolean);
create table product (
id integer primary key,
user integer non null,
price integer non null,
foreign key(user) references user(id));
create table product_buyer (
id integer primary key,
product integer,
buyer integer,
foreign key(product) references product(id),
foreign key(buyer) references buyer(id));
create table buyer (
id integer primary key,
active boolean,
last_offer integer);
你应该得到你想要的东西:
select (
user.id,
sum(case when product.price > buyer.last_offer then 1 end) as expensive,
sum(case when product.price = buyer.last_offer then 1 end) as same,
sum(case when product.price < buyer.last_offer then 1 end) as cheap)
from
user join product on user.id=product.user
join product_buyer on product.id=product_buyer.product
join buyer on product_buyer.buyer=buyer.id
where user.active=1 and buyer.active=1
group by user.id;
您可以查看用于CASE语句here的条件表达式的django文档。
希望它有所帮助。
修改强> 我试图用你的模型将查询翻译成django(未经测试)。
Product.objects.filter(
user=my_user,
active=True,
buyers__valid=True,
buyers__offers__valid=True
).annotate(
max_date=Max("buyers__offers__datetime")
).filter(
datetime=F("max_date")
).annotate(
expensive=Case(
When(buyers__a_price__gt=F("buyers__offers__eur_price"),
then=Value(1))
),
same=Case(
When(buyers__a_price=F("buyers__offers__eur_price"),
then=Value(1))
),
cheap=Case(
When(buyers__a_price__lt=F("buyers__offers__eur_price"),
then=Value(1))
)
).annotate(
n_expensive=Sum("expensive"),
n_same=Sum("same"),
n_cheap=Sum("cheap")
).values("user", "n_expensive", "n_same", "n_cheap")
我不确定是否有办法以更简洁的方式编写它,这是最远的我没有实际制作django测试应用程序来检查它。 因为你最终拥有测试模型,所以我会把改进留给你,但是考虑到上面的SQL,翻译过程应该只是通过django文档。