3

I've got web project with Django backend for which I decided to store images as BinaryField, it seemed to be more convenient for me in case of making backups and restoring them.

At first I've created model:

class ThermalSource(ClusterUnit):
    ...
    scheme_image = models.BinaryField(verbose_name='Scheme', blank=True, null=True, editable=True)
    ...

Then created serializer to use in viewset (I know it is not related to admin interface, but maybe it will be useful):

class Base64BinaryField(serializers.Field):

    def to_representation(self, value):
        from base64 import b64encode
        return b64encode(value)

    def to_internal_value(self, data):
        from base64 import b64decode
        return b64decode(data)


class ThermalSourceSerializer(APAMModelSerializer):
    ...
    scheme_image_base64 = Base64BinaryField(source='scheme_image')
    ...

Now I could get and set images converted to Base64 correctly via Django REST Framework.

Admin class for ThermalSource looks so now:

class ThermalSourceForm(forms.ModelForm):

    scheme_image = forms.FileField(required=False)

    def save(self, commit=True):
        if self.cleaned_data.get('scheme_image') is not None \
                and hasattr(self.cleaned_data['scheme_image'], 'file'):
            data = self.cleaned_data['scheme_image'].file.read()
            self.instance.scheme_image = data
        return self.instance

    def save_m2m(self):
        # FIXME: this function is required by ModelAdmin, otherwise save process will fail
        pass

class ThermalSourceAdmin(admin.ModelAdmin):
    form = ThermalSourceForm
    readonly_fields = ('id', )
    list_display = ('id', ... 'scheme_image')

But when I open Django admin interface, I could only download files to scheme image field, no preview is configured.

Could anybody suggest me how could I configure admin class for my model to be able to see images previews in Django admin interface?

Dmitriy Vinokurov
  • 365
  • 1
  • 6
  • 28

1 Answers1

5

Fist, add the code below to your model.

def scheme_image_tag(self):
    from base64 import b64encode
    return mark_safe('<img src = "data: image/png; base64, {}" width="200" height="100">'.format(
        b64encode(self.scheme_image).decode('utf8')
    ))

scheme_image_tag.short_description = 'Image'
scheme_image_tag.allow_tags = True

Then, you can render this field on your list_display (admin).

list_display = (...'scheme_image_tag')

As mentioned by @DmitriyVinokurov, you can add this tag to readonly_fields.

readonly_fields = (..., 'scheme_image_tag')

P.S. This answer explains why I'm using decode('utf8').

Eliakin Costa
  • 930
  • 6
  • 16
  • Cool, thanks! One comment and one question. Comment - I added tag to `readonly_fields` instead of `list_display` cause I want it to show on item details page, not on list page. Question - maybe you could also help me to find how to clear binary field from admin interface to make this solution complete? – Dmitriy Vinokurov Dec 03 '19 at 18:00
  • Hi, @DmitriyVinokurov. I'm glad I could help you. About your question: what do you mean by `clear binary field` ?. – Eliakin Costa Dec 03 '19 at 18:37
  • I mean set `None` to `scheme_image` value of `ThermalSource` instance. In admin interface I see only file selector to upload file, no button so set it to `None`. – Dmitriy Vinokurov Dec 04 '19 at 01:39
  • 1
    Hi, @DmitriyVinokurov. There's no clear button by default, but you can implement a [admin action](https://docs.djangoproject.com/en/2.2/ref/contrib/admin/actions/) or use this [package](https://github.com/ninemoreminutes/django-admin-object-actions/) to add actions to the detail view. – Eliakin Costa Dec 04 '19 at 20:52
  • Hi! Thanks for reply. Tried `django-admin-object-actions` package. Following `fail` action example from https://github.com/ninemoreminutes/django-admin-object-actions/blob/master/test_project/test_app/admin.py I created my own solution after few attempts. One problem - I created `def clear_scheme_image(obj: ThermalSource, form)` function, tested it and got `clear_scheme_image takes 2 positional arguments but 3 were given` error. Added fake `some_parameter` to the end of function args list definition - another error, to the beginning - OK. I'm really curious what is this parameter actually :) – Dmitriy Vinokurov Dec 05 '19 at 17:41