如何改进这种多对多的Django ORM查询和模型集?

时间:2011-12-03 16:40:44

标签: python sql django postgresql django-models

我有一个Django查询和一些Python代码,我正在尝试优化,因为1)它很难看,它不像我可以用来编写它的一些SQL那样高效,2)因为数据的层次重组看起来很好对我来说很麻烦。

所以, 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)

所以,粗略的想法是我希望通过GalleryPhoto获取图库中的所有照片,并且对于每张照片,我想获得所有的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 个答案:

答案 0 :(得分:3)

当你有sql查询时,使用orm很难编写 - 你可以使用postgresql视图。关于mysql不确定。在这种情况下,您将拥有:

Raw SQL like:

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 Queryset如:

PhotoUrls.objects.filter(gallery_id=5)

希望它会有所帮助。

答案 1 :(得分: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。

答案 2 :(得分: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(通过反向关系)和{ {1}} Photo__Photo__PhotoSizing__inner join ....

您会收到一个词典列表:

PhotoSizing

您可以选择所需的行并将所有值作为词典列表...然后您可以编写循环函数或迭代器来遍历此列表并创建一个新的字典,将数据分组...