【发布时间】:2017-05-14 16:35:19
【问题描述】:
考虑下面的例子:
class Customer(models.Model):
first_name = models.CharField()
last_name = models.CharField()
rental_date = models.DateTimeField()
rented_car = models.ForeignKey(Car)
class Car(models.Model):
color = models.CharField()
reg_no = models.IntegerField()
我想按汽车对所有租赁进行分组(假设客户不能租赁超过一辆车,但客户可以多次租赁汽车),并且对于每个组,仅返回最近 rental_date 的租赁并访问客户和汽车的名称reg_no。
SQL 看起来像这样:
SELECT * FROM Customer c where rental_date = (SELECT max(rental_date) FROM Customer WHERE rented_car_id = c.rented_car_id);
或者像这样:
SELECT *, max(rental_date) from Customer group by rented_car;
然后,生成的查询集将按客户 first_name 或 car reg_no 排序,并且可以应用其他过滤器(例如,仅获取蓝色汽车或名称以“A”开头的客户)。
我尝试过的事情:
聚合:
from django.db.models Max
Customer.objects.values('rented_car').annotate(max_start_date=Max('rental_date'))
但这会返回一个包含Car 对象主键的字典。我也需要来自Customer 的值。修改查询以包含来自 Customer (.values('rented_car', 'first_name')) 的字段将更改 SQL 并改变最终结果。
我使用的第二种方法是raw:
Customer.objects.raw("""SELECT * FROM Customer c where rental_date = (SELECT max(rental_date) FROM Customer WHERE rented_car_id = c.rented_car_id)""")
但这会返回一个不允许进一步过滤或排序的 RawQuerySet 实例。此外,Customer.objects.filter(...).raw(...).order_by(...) 将不起作用,因为 raw 方法将覆盖过滤。
另一个可以返回查询集并允许额外过滤的方法是extra:
Customer.objects.filter(...).extra(select={
'customer_instances': """SELECT *, max(rental_date) from Customer group by rented_car"""
})
但他总是会返回错误(1241, 'Operand should contain 1 column(s)')。另外,从discussion 我发现 QuerySet.extra(...) 将不再有支持,应该改用aggregation。
【问题讨论】:
-
您的模型/架构对我来说似乎很奇怪 - 我会使用中间的
Rent模型,在Car和Customer上带有日期和外键。 -
感谢您的回复。但是,这类似于我正在从事的项目中的情况。由于 NDA 协议,我无法提供更多细节,但是,拥有其他型号并不合适。
-
为什么您的项目不接受理智的数据库设计?此外,如果您受到严格的 NDA 之类的限制,那么就会涉及到金钱,所以请聘请一位熟悉 Django 和 SQL 的顾问,不要来这里要求其他人免费完成您的工作。
-
感谢您的回复!我很欣赏你的方法和奉献精神。关于数据库设计的合理性,请让我假设您也是一名具有复杂项目经验的开发人员,因此知道许多项目都会遭受修改,尤其是在开发的最后阶段。这是其中一种情况。另外,我并不是要求任何人免费完成我的工作,而是我提出了一个极端案例,在这种情况下 Django 不是一个很好的伴侣,也许在开发复杂的应用程序时找到一种解决方法会帮助许多其他人。
-
Ergo,边缘案例:拥有两个相关模型(ModelA 和 ModelB),我如何通过 ModelB 实例对 ModelA 进行分组,然后在不使用自定义 SQL 并且能够进一步过滤结果(这意味着返回一个 Django QuerySet,而不是一些 RawQuerySet 或字典中的一些值)?
标签: python mysql django django-1.8