我有以下模型描述具有可选数量图像的绘画:
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。
答案 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 ''