Как я могу улучшить этот многопользовательский запрос и набор моделей Django ORM? - PullRequest
3 голосов
/ 03 декабря 2011

У меня есть запрос 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, и для каждой фотографии я хочу получить все размеры фотографий, и я хотел бы иметь возможность циклически просматривать и получать доступ ко всем этим данным через словарь.

Черновой набросок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

Iпосмотрите на этот код, и я не слишком взволнован тем, как он выглядит.Я уверен, что есть лучший способ сгруппировать результаты иерархического набора запросов по имени столбца в словарь.Есть ли?

Ответы [ 3 ]

3 голосов
/ 12 декабря 2011

Если у вас есть 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 Queryset, например:

PhotoUrls.objects.filter(gallery_id=5)

Надеюсь, это поможет.

1 голос
/ 10 декабря 2011

Вы можете получить все данные одним запросом и получить список словарей данных. Затем вы можете управлять этим словарем или создать новый для формирования окончательного словаря ... Вы можете использовать обратные отношения в фильтрации и , выбирая определенные строки из таблицы ... Итак:

Пусть 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 join до Photo, тогда как Photo__Photo__ создаст inner join до PhotoSize (через обратную связь), а Photo__Photo__PhotoSizing__ будет inner join до PhotoSizing .. ..

Вы получаете список словарей:

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

Вы можете выбрать нужные вам строки и получить все значения в виде списка словарей ... Затем вы можете написать функцию цикла или итератор для циклического перемещения по этому списку и создать новый словарь с группировкой ваших данных ...

1 голос
/ 04 декабря 2011

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 для всех фотографий.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...