无法覆盖ModelAdmin中的queryset()

时间:2013-08-25 10:35:53

标签: python django

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

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(),它为每个绘图执行了对PaintingImage的单独查询。

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

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。

1 个答案:

答案 0 :(得分: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 ''