【问题标题】:Can't override queryset() in ModelAdmin无法在 ModelAdmin 中覆盖 queryset()
【发布时间】:2013-08-27 23:53:36
【问题描述】:

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

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。

【问题讨论】:

  • 签出select_related()
  • 反向外键无效,反正管理员默认使用
  • 然后使用prefetch_related()
  • 这也不适用于外键。即使这样做了,真正的问题是在 queryset() 中向查询集添加内容不起作用。
  • 确实如此。您需要添加prefetch_related('images')。请参阅此question。我会先解决这个问题,然后再专注于另一个问题。似乎它们是无关的。

标签: python django


【解决方案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 ''

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-04
    • 2012-05-08
    • 1970-01-01
    • 2022-11-10
    • 2019-10-20
    • 2011-06-18
    • 1970-01-01
    相关资源
    最近更新 更多