2016-12-30 109 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配合使用。