Django Admin以内联形式显示外键图像字段的缩略图

时间:2012-07-23 10:49:52

标签: django django-admin thumbnails imagefield

我有一个简单的结构,包含Product模型,AppMedia模型和ProductMedia连接表。

产品(有很多)ProductMedia

ProductMedia(有一个)AppMedia

我真正想要的是在Product中以内联形式查看AppMedia媒体字段的缩略图。

e.g。在Django admin中编辑产品,显示StackedInline表单。这包含(目前)所有AppMedia的下拉(选择)。

我需要的是缩略图。

任何帮助表示感谢。

我确信这并不困难,但我很挣扎。将缩略图放入AppMedia表格(这是媒体ImageField所在的位置)非常简单,但不适用于将AppMedia用作ForeignKey的ProductMedia表单。

基本型号......

class Product(models.Model):
    name             = models.CharField(max_length=100)

class AppMedia(models.Model):
    media           = models.ImageField(upload_to=appmedia_upload_to) #appmedia_upload_to defined elsewhere)

class ProductMedia(models.Model):
    title           = models.CharField(max_length=150)
    media           = models.ForeignKey(AppMedia)
    media_order     = models.IntegerField(default=0)
    product         = models.ForeignKey(Product)

以这种方式共享AppMedia以停止同一文件的多次上传,并且还存储图像中的额外元数据(此处未显示)。

2 个答案:

答案 0 :(得分:0)

我有同样的问题。我希望至少有一次尝试可以用于你的案子:

我首次尝试解决此问题(不覆盖ForeignKeyRawIdWidget)是:

  • 在内联管理中设置form属性,其中应显示缩略图
  • 使用小部件将另一个字段添加到给定的表单类中,该小部件将显示缩略图

但是我放弃了这个解决方案,因为我认为我必须在表单的构造函数中将缩略图数据注入给定字段,我不认为这是一个很好的解决方案。

我的下一个解决方案是在某些字段中使用MultiWidget。在这种情况下,我不需要添加另一个字段来形成,我将需要在窗口小部件中显示缩略图所需的数据,而无需在构造函数中注入它们。

class ThumbnailMultiWidget(MultiWidget):

   def decompress(self, value):
       #has to be overriden
       return [None,None]


class ThumbnailWidget(Widget):

    def render(self, name, value, attrs=None):
       #not quite sure what is in `value`, I've not been so far
       return '<img src="%s"/>' % value.url


class PhotoInlineForm(forms.ModelForm):


    def __init__(self, *args, **kwargs):
        super(PhotoInlineForm, self).__init__(*args, **kwargs)


        wdgts = [self.fields['media'].widget, ThumbnailWidget()]
        self.fields['media'].widget = ThumbnailMultiWidget(widgets=wdgts)


    class Meta:
        model = RecipePhoto

但是我也放弃了这个解决方案,因为我发现实际上在ForeignKeyRawIdWidget(我使用的小部件)中有一个实例的表示,我需要显示缩略图所需的所有数据。这是我的最终解决方案:

因为我的内联项目有raw_id_field用于选择内联记录,我可以简单地覆盖label_for_value中的方法ForeignKeyRawIdWidget,该方法用于表示现有的内联记录。通常它是__unicode__(我认为)。我继承了ForeignKeyRawIdWidget并重写此方法以显示图片缩略图:

class PhotoForeignKeyRawIdWidget(ForeignKeyRawIdWidget):

    def label_for_value(self, value):
        key = self.rel.get_related_field().name
        try:
            obj = self.rel.to._default_manager.using(self.db).get(**{key: value})
        except (ValueError, self.rel.to.DoesNotExist):
            return ''
        else:
            """
            there's utilized sorl.thumbnail, but you can return st like this:
            <img src='%s' /> % obj.media.url
            """
            return Template("""{% load thumbnail %}
            {% thumbnail image.image "120x120" crop="center" as one %}
                <img src="{{ one.url }}" />
            {% endthumbnail %}""").render(Context({
                'image': obj
            }))


class AppMediaInlineAdmin(admin.TabularInline):
    model = AppMedia
    extra = 1

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
        if db_field.name == 'media':
            db = kwargs.get('using')
            kwargs['widget'] = PhotoForeignKeyRawIdWidget(db_field.rel, self.admin_site, using=db)
        return super(AppMediaInlineAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

答案 1 :(得分:-1)

您应该查看外键的related_name参数,这样您就可以反向访问ProductMedia,即如果您将ProductMedia模型更改为:

class ProductMedia(models.Model):
    title           = models.CharField(max_length=150)
    media           = models.ForeignKey(AppMedia)
    media_order     = models.IntegerField(default=0)
    product         = models.ForeignKey(Product, related_name='media')

您可以访问产品型号中的媒体对象,这样您就可以将其放入管理员的在线表单中。即你会有(为了更简单的解释,我把ImageField放在ProductMedia中):

class Product(models.Model):
    name             = models.CharField(max_length=100)

    def admin_image(self):
        return '<img src="%s"/>' % (self.media.all()[0].image.url)
    admin_image.allow_tags = True

class ProductMedia(models.Model):
    title           = models.CharField(max_length=150)
    image           = models.ImageField(upload_to=appmedia_upload_to) #appmedia_upload_to defined elsewhere)
    media_order     = models.IntegerField(default=0)
    product         = models.ForeignKey(Product, related_name='media')

然后在你的admin.py中:

class ProductAdmin(admin.ModelAdmin):
    list_display = ('name', 'admin_image')

admin.site.register(models.Product, ProductAdmin)

https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.related_name

希望我能正确理解你的问题,这有帮助。此外,代码未经过测试,但我确信它应该可以使用。