2016-12-30 144 views
0

考虑下面的例子:在Django创建自定义查询1.8

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或汽车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

+0

您的模型/模式对我来说似乎很奇怪 - 我会在'Car'和'Customer'中使用一个中间的'Rent'模型,日期和外键。 –

+0

谢谢你的回应。但是,这与我正在研究的项目的情况类比。由于NDA协议,我不能提供任何更多的细节,但是,另一种模式不适合。 –

+0

为什么你的项目不接受一个理智的数据库设计?另外,如果你有严格的NDA和东西,那么就会涉及金钱,所以聘请一位熟悉Django和SQL的顾问,并且不要过来请求其他人免费工作。 –

回答

0

您通常不需要在Django中定制SQL。如果您发现使用ORM制定数据库查询时遇到问题,请考虑您的数据库架构是否可以采用让您轻松(且高效)获取数据的表单。

您或许应该将normalize租赁数据放到另一张表中。

我也已将颜色和品牌标准化为他们自己的模型(以及表格),因为您可能希望通过它寻找并在UI中显示可用颜色和品牌的列表。如果你允许你的用户在野外输入颜色,你会发现你的汽车模型中有1000种不同的“蓝色”拼写错误。想象一下从“蓝色”,“蓝色”,“蓝色”,“bLue”等搜索,并向用户展示这些可怕的东西。呃...

你的车型(轿车,皮卡,...)也可能是一张桌子。

class Color(models.Model): 
    name = models.CharField(max_length=16) 
    hex = models.CharField(max_length=16) 

class Brand(models.Model): 
    name = models.CharField(max_length=16) 

class Car(models.Model): 
    # make sure your database constraints are thought-out 
    reg_no = models.CharField(max_length=16, unique=True) 
    year = models.DateField() 
    color = models.ForeignKey(Color) 
    brand = models.ForeignKey(Brand) 

    class Meta: 
     order_by = ('reg_no',) 

class Customer(models.Model): 
    first_name = models.CharField(max_length=64) 
    last_name = models.CharField(max_length=64) 

    class Meta: 
     order_by = ('first_name', 'last_name', 'pk') 

class Rental(models.Model): 
    date = models.DateField() # if it's a date, use a DateField 
    car = models.ForeignKey(Car) 
    customer = models.ForeignKey(Customer) 

    class Meta: 
     # Let's not allow duplicate rentals for the same day 
     unique_together = ('date', 'car') 
     order_by = ('date', 'car', 'customer') 

# Since your customers are already sorted by names, 
# and your cars by 'reg_no', they follow that sorting. 
# Your rentals are ordered by 'date' and therefore have the 
# most recent date first in the table, so you can just select 
# the most recent 'car' instances, which results in you getting 
# the most recent 'car' rentals sorted by your dates, 
# nice and easy: 

Rental.objects.all().distinct('car') 

# Pretty easy to search your rentals now. 
# If you want to filter just use something like: 

Rental.objects.filter(
    date__range=(datetime(2016, 1, 1), datetime(2017, 1, 1), 
    car__brand__icontains='Toyota', 
    customer__first_name__icontains='John' 
) 

在Django中,你通常可以找出你的模式应该是什么样子的寻找到你的产品,并搞清楚你有什么样的抽象的存在。您似乎至少在这里有用户,汽车,租赁和颜色。

您可能还会在此找到django-filter包。如果您要构建一个,它可以很好地与您的用户界面和可能的API配合使用。