0

我有以下模型来描述具有可选图像数量的绘画:

class Painting(Base, Seo, Timestamp):
    artist = models.ForeignKey(Artist, related_name='paintings')
    medium = models.ForeignKey(Medium)

    def thumbnail(self):
        thumbnail = self.images.filter(position=0)
        if thumbnail.exists():
            return thumbnail[0].thumbnail_html()
        else:
            return ''
    thumbnail.allow_tags = True

class PaintingImage(models.Model):
    painting = models.ForeignKey(Painting, related_name='images')
    alt = models.CharField(max_length=100)
    position = models.PositiveSmallIntegerField("Position", default=0)

    # use pi.image = 'path/to/file' for direct access to the underlying image filename
    image = models.ImageField(upload_to='paintings')
    thumbnail = models.ImageField(upload_to='paintings')

这一切都很好,但管理列表视图很慢并且执行大量查询,因为它在绘画上调用 thumbnail(),它为每幅绘画对绘画图像进行单独的查询。

我试图覆盖 ModelAdmin 中的 queryset() 以有效地手动将图像附加到每个实例。list_display 中的 'thumbnail' 现在指向 PaintingAdmin 中的 thumbnail(self, obj) ,它读取存储的属性,但它不起作用 - 无法在下面的 thumbnail(self, obj) 中读取 obj.thumb:

class PaintingAdmin(admin.ModelAdmin):
    fields = [('title', 'slug', 'display'),
              'artist', ('categories', 'medium'), 'price', 'description', 'note',
              ('sold', 'reserved', ),
              ('height', 'width', 'frame_height', 'frame_width')
    ]
    prepopulated_fields = {"slug": ("title",)}
    inlines = [ImageInline, InvoiceInline]
    list_display = ('title', 'artist', 'slug', 'medium', 'price',
                    'sold', 'reserved', 'display', 'created', 'thumbnail', 'invoice_link')
    list_filter = ['display', 'sold', 'reserved', 'medium', PriceFilter]
    search_fields = ['title', 'description', 'note', 'artist__last_name', 'artist__first_name']

    date_hierarchy = 'created'

    def thumbnail(self, obj):
        return obj.thumb     # error because it doesn't exist

    def queryset(self, request):
        """make it more efficient by not getting painting image every time
        """
        paintings = super(PaintingAdmin, self).queryset(request)

        # get all images which are thumbnails
        images = PaintingImage.objects.filter(
            painting__in=paintings,
            position=0
        )

        painting_id_to_thumbnail = {}
        for image in images:
            painting_id_to_thumbnail[image.painting_id] = image.thumbnail_html()

        # attach thumbnail to each instance
        for painting in paintings:
            painting.thumb = painting_id_to_thumbnail.get(painting.id, '')

        return paintings

我正在使用 django 1.5.2 和 python 2.7。

4

1 回答 1

1

可以在反向关系上使用 prefetch_related ,因此 queryset() 中的上述代码可以替换为:

paintings = super(PaintingAdmin, self).queryset(request)
return paintings.prefetch_related('images')

问题在于这种绘画方法:

def thumbnail(self):
        thumbnail = self.images.filter(position=0)
        if thumbnail.exists():
            return thumbnail[0].thumbnail_html()
        else:
            return ''

即使使用 prefetch_related,它也会做额外的查询。所以解决方案是将其替换为:

def thumbnail(self):
        if self.images.count():
            return self.images.all()[0].thumbnail_html()
        else:
            return ''
于 2013-08-26T12:01:33.930 回答