2011-12-03 23 views
3

我有一個Django查詢和一些我試圖優化的Python代碼,因爲1)它很醜並且不像我可以用來編寫它的一些SQL那樣高性能,2)因爲數據的分層重組看起來很亂對我來說。如何改進這個多對多的Django ORM查詢和模型集?

因此, 1.是否有可能改進這是一個單一的查詢? 2.如何提高我的Python代碼以使其變得更加Pythonic?

背景

這是一個圖片庫系統。特定視圖試圖顯示畫廊中所有照片的縮略圖。每張照片靜態大小几次,以避免動態調整大小,我還想檢索每個大小的URL和「大小類型」(例如縮略圖,中等,大),以便我可以在不觸碰數據庫的情況下對備用大小進行Lightbox 。

實體

我有5款是相關的:

class Gallery(models.Model): 
    Photos = models.ManyToManyField('Photo', through = 'GalleryPhoto', blank = True, null = True) 

class GalleryPhoto(models.Model): 
    Gallery = models.ForeignKey('Gallery') 
    Photo = models.ForeignKey('Photo') 
    Order = models.PositiveIntegerField(default = 1) 

class Photo(models.Model): 
    GUID = models.CharField(max_length = 32) 

class PhotoSize(models.Model): 
    Photo = models.ForeignKey('Photo') 
    PhotoSizing = models.ForeignKey('PhotoSizing') 
    PhotoURL = models.CharField(max_length = 1000) 

class PhotoSizing(models.Model): 
    SizeName = models.CharField(max_length = 20) 
    Width = models.IntegerField(default = 0, null = True, blank = True) 
    Height = models.IntegerField(default = 0, null = True, blank = True) 
    Type = models.CharField(max_length = 10, null = True, blank = True) 

所以,粗略的想法是,我想獲得的所有照片通過文件庫一庫,併爲每個照片,我想獲取所有PhotoSizes,我希望能夠通過字典循環訪問所有這些數據。

的SQL的草圖可能是這樣的:

Select PhotoSize.PhotoURL 
From PhotoSize 
Inner Join Photo On Photo.id = PhotoSize.Photo_id 
Inner Join GalleryPhoto On GalleryPhoto.Photo_id = Photo.id 
Inner Join Gallery On Gallery.id = GalleryPhoto.Gallery_id 
Where Gallery.id = 5 
Order By GalleryPhoto.Order Asc 

我願把它變成有這樣一個模式的列表:

(
    photo: { 
     'guid': 'abcdefg', 
     'sizes': { 
      'Thumbnail': 'http://mysite/image1_thumb.jpg', 
      'Large': 'http://mysite/image1_full.jpg', 
      more sizes... 
     } 
    }, 
    more photos... 
) 

目前我有以下Python代碼(它並不完全模仿上面的模式,但它會做一個例子)。

gallery_photos = [(photo.Photo_id, photo.Order) for photo in GalleryPhoto.objects.filter(Gallery = gallery)] 
photo_list = list(PhotoSize.objects.select_related('Photo', 'PhotoSizing').filter(Photo__id__in=[gallery_photo[0] for gallery_photo in gallery_photos])) 

photos = {} 
for photo in photo_list: 
    order = 1 
    for gallery_photo in gallery_photos: 
     if gallery_photo[0] == photo.Photo.id: 
      order = gallery_photo[1] //this gets the order column value 

      guid = photo.Photo.GUID 
      if not guid in photos: 
       photos[guid] = { 'Photo': photo.Photo, 'Thumbnail': None, 'Sizes': [], 'Order': order } 

      photos[guid]['Sizes'].append(photo) 

    sorted_photos = sorted(photos.values(), key=operator.itemgetter('Order')) 

的實際問題,第1部分

所以,我的問題是,首先我是否可以做我的許多一對多查詢更好,這樣我就不用做雙查詢gallery_photos和photo_list。

的實際問題,第2部分

我看看這段代碼,我不與它的樣子太激動。我當然希望有一種更好的方法可以將分層查詢集結果按列名分組到字典中。在那兒?

回答

3

當你有SQL查詢,很難用orm寫 - 你可以使用postgresql視圖。不確定關於MySQL。在這種情況下,你將有:

原始SQL這樣的:

CREATE VIEW photo_urls AS 
Select 
photo.id, --pseudo primary key for django mapper 
Gallery.id as gallery_id, 
PhotoSize.PhotoURL as photo_url 
From PhotoSize 
Inner Join Photo On Photo.id = PhotoSize.Photo_id 
Inner Join GalleryPhoto On GalleryPhoto.Photo_id = Photo.id 
Inner Join Gallery On Gallery.id = GalleryPhoto.Gallery_id 
Order By GalleryPhoto.Order Asc 

的Django模型,如:

class PhotoUrls(models.Model): 
    class Meta: 
     managed = False 
     db_table = 'photo_urls' 
    gallery_id = models.IntegerField() 
    photo_url = models.CharField() 

ORM查詢集,如:

PhotoUrls.objects.filter(gallery_id=5) 

希望這將有助於。

+0

我喜歡這個主意......非常有趣的尼古拉。 – Jordan

+0

它幫助我極大地減少了重頁上的查詢次數。 –

1

Django有一些內置函數可以清理代碼的外觀。這將導致子查詢,所以我想這取決於性能。 https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.values

gallery_photos = GalleryPhoto.objects.filter(Gallery=gallery).values('Photo_id', 'Order') 
photo_queryset = PhotoSize.objects.selected_related('Photo', 'PhotoSizing').filter(
       Photo__id__in=gallery_photos.values_list('Photo_id', flat=True)) 

調用list()將立即評估查詢集,如果你有大量的數據,這可能會影響性能。

此外,應該有一個相當簡單的方法來擺脫if gallery_photo[0] == photo.Photo.id:這似乎可以很容易解決與另一個查詢,獲取所有照片gallery_photos。

+0

那麼,如果gallery_photo == photo.id部分的重點是因爲它需要分層分組。這部分不能用查詢完成,因爲顯然SQL將返回一個平坦的結果集。我不知道更好的按層次結構進行分組的方法,除了帶有if檢查的double循環外。此外,子查詢對性能影響太大,因爲可能會有大量照片。 – Jordan

1

您可以使用單個查詢檢索所有數據,並獲取數據字典列表。然後你就可以管理這個字典或創建一個新的,以形成最終的字典...您可以使用反向關係,在過濾從表中選擇特定的行 ...所以:

x是你的選擇Galery ...

GalleryPhoto.objexts.filter(Galery=x).values('Order', 'Photo__GUID', 'Photo__Photo__PhotoURL', 'Photo__Photo__PhotoSizing__SizeName', 'Photo__Photo__PhotoSizing__Width', 'Photo__Photo__PhotoSizing__Height', 'Photo__Photo__PhotoSizing__Type') 

使用Photo__將創建一個inner joinPhoto表而Photo__Photo__將創建inner joinPhotoSize(經由反向關係)和Photo__Photo__PhotoSizing__inner joinPhotoSizing ....

你得到的詞典列表:

[{'Order':....,'GUID': ..., 'PhotoURL':....., 'SizeName':...., 'Width':...., 'Height':..., 'Type':...}, {'Order':....,'GUID': ..., 'PhotoURL':....., 'SizeName':...., 'Width':...., 'Height':..., 'Type':...},....] 

您可以選擇您需要的行,並得到所有值的詞典列表...然後,你可以寫一個循環函數或迭代器遍歷這個列表,並創建一個新的字典惠特分組您的數據...