【问题标题】:How can I improve this many-to-many Django ORM query and model set?如何改进这个多对多 Django ORM 查询和模型集?
【发布时间】:2012-01-12 05:46:25
【问题描述】:

我有一个 Django 查询和一些我正在尝试优化的 Python 代码,因为 1)它很丑,而且它的性能不如我可以用来编写它的某些 SQL,以及 2)因为数据的分层重组看起来对我来说很乱。

所以, 1.是否可以将其改进为单个查询? 2. 如何改进我的 Python 代码使其更加 Pythonic?

背景

这是一个照片库系统。特定视图试图显示画廊中所有照片的缩略图。每张照片都经过多次静态调整以避免动态调整大小,我还想检索每个大小的 URL 和“大小类型”(例如缩略图、中、大),这样我就可以在不再次访问数据库的情况下将备用大小装箱.

实体

我有 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 获取 Gallery 中的所有照片,并且对于每张照片,我想获取所有 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 部分

我查看了这段代码,但对它的外观并不太满意。我当然希望有一种更好的方法可以通过列名将分层查询集结果分组到字典中。有吗?

【问题讨论】:

    标签: python sql django postgresql django-models


    【解决方案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。

    【讨论】:

    • 好吧,if gallery_photo == photo.id 部分的重点是因为它需要分层分组。这部分不能通过查询来完成,因为显然 SQL 将返回一个平面结果集。除了带有 if 检查的双 for 循环之外,我不知道按层次结构分组的更好方法。此外,子查询对性能的影响太大,因为可能有大量照片。
    【解决方案2】:

    您可以通过单个查询检索所有数据,并获取数据字典列表。然后你可以管理这个字典或创建一个新的字典来形成你的最终字典......你可以在过滤从表中选择特定行中使用反向关系......所以:

    x 成为您选择的画廊...

    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':...},....]
    

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

    【讨论】:

      【解决方案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)
      

      希望它会有所帮助。

      【讨论】:

      • 我喜欢这个主意...非常有趣的 Nikolay。
      • 它帮助我显着减少了重页的查询次数。
      猜你喜欢
      • 2014-05-27
      • 2010-10-06
      • 2023-03-03
      • 2018-02-09
      • 2020-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-03
      相关资源
      最近更新 更多