I have the following models which describe a painting with an optional number of images:
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')
This all works fine but the admin list view is slow and doing lots of queries because it is calling thumbnail() on Painting, which is doing a separate query to PaintingImage for each painting.
I tried to override queryset() in ModelAdmin to attach the image to each instance manually, in an efficient manner. 'thumbnail' in list_display now points to thumbnail(self, obj) in PaintingAdmin which reads the stored attribute, but it isn't working - obj.thumb can't be read in thumbnail(self, obj) below:
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
I'm using django 1.5.2 and python 2.7.