How to display base64 image in django admin? - django

I have legacy software that stores the institutions logo through django's BinaryField, I need to show this image on the django admin record editing page
this my admin:
class InstituicaoAdmin(admin.ModelAdmin):
list_display = ['ds_instituicao', 'sigla']
search_fields = ['ds_instituicao']
formfield_overrides = {
BinaryField: {'widget': BinaryFileInput()},
}
readonly_fields = ["imagem_logo",]
def imagem_logo(self, obj):
base64Encoded = base64.b64encode(obj.logo)
return mark_safe('<img src="data:;base64,base64Encoded">')
This not work
I want some like this: Image_FIeld

I would use format_html instead of mark_safe:
def imagem_logo(self, obj):
base64Encoded = base64.b64encode(obj.logo)
return format_html('<img src="data:;base64,{}">', base64Encoded)
See the docs at: https://docs.djangoproject.com/en/2.2/ref/utils/#django.utils.html.format_html

Related

Django. Override the html format of a FileField field in the change page via ModelAdmin

This is my sample code (and see the screenshot):
models.py
class User(models.Model):
# the variable to take the inputs
user_name = models.CharField(max_length=100)
user_avatar = models.FileField(upload_to = 'images/%Y%m%d')
admin.py
class UserAdmin(admin.ModelAdmin):
exclude = ('user_avatar',)
readonly_fields = ('avatar_readonly',)
def avatar_readonly(self, instance):
value_link = "/mediafileupdown/download/" + str(instance.id)
value_desc = str(instance.user_avatar.name)
return format_html('{}', value_link, value_desc)
avatar_readonly.short_description = "User avatar (read only)"
avatar_readonly.allow_tags=True
screenshot
The problem is: i'd like obtain the 'Currently' user_avatar's link (that's a models.FileField field) via the my 'download' views and not as simple url. I can do that if the field is in the 'readonly_fields' list and so I can overide the html format ... in that case, on the change page, the 'avatar_readonly' link is redirected to the my 'download' view.
The question is: how can I get the same result (overide the html format) in a FileField (like my 'user_avatar') when is not in the readonly_fileds list?
Sorry for my english and many Thanks for the help.
You can use formfield_overrides to modify the AdminFileWidget. This AdminFileWidget can display images and mp4 files, but you can use only for images omitting the if file_name[-4:] == '.mp4':
part.
from django.db.models.fields.files import FileField
from django.contrib.admin.widgets import AdminFileWidget
class AdminMediaWidget(AdminFileWidget):
def render(self, name, value, attrs=None):
output = []
if value and getattr(value, "url", None):
image_url = value.url
file_name = str(value)
if file_name[-4:] == '.mp4':
output.append('<video width="320" height="240" controls>'
'<source src="{0}" type="video/mp4">'
'Your browser does not support the video tag.'
'</video>'.format(image_url))
else:
output.append('<a href="{0}" target="_blank">'
'<img height=200 src="{1}" alt="{2}" />'
'</a>'.format(image_url, image_url, file_name))
output.append(super(AdminFileWidget, self).render(name, value, attrs))
return mark_safe(''.join(output))
class UserAdmin(admin.ModelAdmin):
formfield_overrides = {
FileField: {'widget': AdminMediaWidget},
}
exclude = ('user_avatar',)
readonly_fields = ('avatar_readonly',)
def avatar_readonly(self, instance):
value_link = "/mediafileupdown/download/" + str(instance.id)
value_desc = str(instance.user_avatar.name)
return format_html('{}', value_link, value_desc)
avatar_readonly.short_description = "User avatar (read only)"
avatar_readonly.allow_tags=True
Hope it helps!

How can I add custom button on Inline Django Admin fields?

I have main django admin class that includes inline class with two fields pos_x and pos_y.
I want to update this fields via JS and modal-window.
Can I add button in InlineClass without changing any admin html?
class InlineClass(CommonInlineConfigurationMixin):
model = MapPoint
fields = ['title', ('pos_x', 'pos_y')]
form = Form
I guess you can do it like this:
class InlineClass(CommonInlineConfigurationMixin):
model = MapPoint
fields = ['title', ('pos_x', 'pos_y'), 'button']
readonly_fields = ("button",)
form = Form
class Media:
js = ("js/my_code.js",)
def button (self, obj):
return "<button type='button' onclick='my_code()'>Click Me!</button>"
button.allow_tags=True
And put the my_code.js into your "static/js" folder.
Using ger.s.brett answer i have to format html in return, like this:
def button (self, obj):
return format_html("<button type='button' onclick='my_code()'>Click Me!</button>")

add link to change page using raw_id_fields in django admin

Is there any chance to add link to change page of a Foreign key object next to 'raw_id_fields' in django admin?
I use raw_id_fields to reduce number of queries to mysql.
I could edit or add a foreign key object when I didn't use raw_id_fields.
How to do it with raw_id_fields?
#AntoinePinsard, I've tried the second solution. The field 'Edit My FK' was created but there is nothing except for a hyphen.
That's my modeladmin.
class FlatAdmin(admin.ModelAdmin):
inlines = [NeedInline]
readonly_fields = ('flat_house_edit_link',)
raw_id_fields = ('flat_house',)
list_display = ('show_date','show_block','show_house','show_rooms','show_price','show_stage')
list_filter = ('flat_house__house_block','flat_rooms')
search_fields = ('flat_house__id',)
field = ['flat_house','flat_house_edit_link','flat_owner','flat_price','flat_rooms','flat_total_sq','flat_life_sq','flat_kitchen_sq','flat_floors']
def flat_house_edit_link(self, instance):
if instance:
fk_id = instance.user_id
else:
fk_id = None
if fk_id:
opts = instance._meta.get_field('flat_house').rel.model._meta
related_url = reverse(
'admin:{}_{}_change/?_to_field=id&_popup=1'.format(
opts.ha,
opts.house,
),
args=[fk_id],
)
return format_html(
'<a target=_blank href="{}">Go!</a>', related_url)
else:
return "No related object"
flat_house_edit_link.short_description = "Change house"
admin.site.register(Flat,FlatAdmin)
Note This behavior is now built-in since Django 1.10.
You can create a custom widget to render this field as you would like to.
from django.contrib.admin.widgets import ForeignKeyRawIdWidget
from django.core.urlresolvers import reverse, NoReverseMatch
from django.utils.html import format_html
class ForeignKeyLinkedRawIdWidget(ForeignKeyRawIdWidget):
def render(self, name, value, attrs=None):
output = super().render(name, value, attrs)
try:
related_url = reverse(
'admin:{}_{}_change'.format(
self.rel.model._meta.app_label,
self.rel.model._meta.model_name,
),
args=[value],
)
except NoReverseMatch:
return output
return format_html('{output} edit',
output=output, url=related_url)
And use this widget in your form, rather than using raw_id_fields:
from myapp.forms.widgets import ForeignKeyLinkedRawIdWidget
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
fields = '__all__'
widgets = {
'my_foreignkey': ForeignKeyLinkedRawIdWidget(),
}
class MyModelAdmin(admin.ModelAdmin):
form = MyForm
However, it would be much simpler to add it as a "fake" readonly_field below the actual field:
from django.core.urlresolvers import reverse
from django.utils.html import format_html
class MyModelAdmin(admin.ModelAdmin):
readonly_fields = ['my_foreignkey_link']
raw_id_fields = ['my_foreignkey']
fields = [..., 'my_foreignkey', 'my_foreignkey_link', ...]
def my_foreignkey_link(self, instance):
if instance:
fk_id = instance.my_foreignkey_id
else:
fk_id = None
if fk_id:
opts = instance._meta.get_field('my_foreignkey').rel.model._meta
related_url = reverse(
'admin:{}_{}_change'.format(
opts.app_label,
opts.model_name,
),
args=[fk_id],
)
return format_html(
'<a target=_blank href="{}">Go!</a>', related_url)
else:
return "No related object"
my_foreignkey_link.short_description = "Edit My FK"
Further reading: https://docs.djangoproject.com/en/1.9/ref/contrib/admin/#django.contrib.admin.ModelAdmin.readonly_fields
Here's a snippet page (updated for 6 years) that does it for me:
https://djangosnippets.org/snippets/2217/
Just use ImproveRawIdFieldsForm in place of ModelAdmin and all raw_id fields automatically links the object name displayed next to the id input.

A Category model which creates proxy models for related model admin

So I'm having a bit of trouble with trying to create a model that will define dynamic proxy models that manage a related model in the admin site. I know that sentence was confusing, so I'll just share my code instead.
models.py
class Cateogry(models.Model):
name = models.CharField(...)
class Tag(models.Model):
name = models.CharField(...)
category = models.ForeignKey(Cateogry)
What I want to achieve is that in the admin site, instead of having one ModelAdmin for the Tag model, for each category I will have a modeladmin for all related tags. I have achieved this using this answer. Say I have a category named A:
def create_modeladmin(modeladmin, model, name = None):
class Meta:
proxy = True
app_label = model._meta.app_label
attrs = {'__module__': '', 'Meta': Meta}
newmodel = type(name, (model,), attrs)
admin.site.register(newmodel, modeladmin)
return modeladmin
class CatA(TagAdmin):
def queryset(self, request):
qs = super(CatA, self).queryset(request)
return qs.filter(cateogry = Cateogry.objects.filter(name='A'))
create_modeladmin(CatA, name='CategoryAtags', model=Tag)
But this is not good enough, because obviously I still need to manually subclass the TagAdmin model and then run create_modeladmin. What I need to do, is loop over all Category objects, for each one create a dynamic subclass for Tagadmin (named after the category), then create a dynamic proxy model from that, and this is where my head starts spinning.
for cat in Category.objects.all():
NewSubClass = #somehow create subclass of TagAdmin, the name should be '<cat.name>Admin' instead of NewSubClass
create_modeladmin(NewSubClass, name=cat.name, model=Tag)
Any guidance or help would be much appreciated
Dynamic ModelAdmins don't work well together with the way admin registeres models.
I suggest to create subviews in the CategoryAdmin.
from django.conf.urls import patterns, url
from django.contrib import admin
from django.contrib.admin.options import csrf_protect_m
from django.contrib.admin.util import unquote
from django.core.urlresolvers import reverse
from demo_project.demo.models import Category, Tag
class TagAdmin(admin.ModelAdmin):
# as long as the CategoryTagAdmin class has no custom change_list template
# there needs to be a default admin for Tags
pass
admin.site.register(Tag, TagAdmin)
class CategoryTagAdmin(admin.ModelAdmin):
""" A ModelAdmin invoked by a CategoryAdmin"""
read_only_fields = ('category',)
def __init__(self, model, admin_site, category_admin, category_id):
self.model = model
self.admin_site = admin_site
self.category_admin = category_admin
self.category_id = category_id
super(CategoryTagAdmin, self).__init__(model, admin_site)
def queryset(self, request):
return super(CategoryTagAdmin, self).queryset(request).filter(category=self.category_id)
class CategoryAdmin(admin.ModelAdmin):
list_display = ('name', 'tag_changelist_link')
def tag_changelist_link(self, obj):
info = self.model._meta.app_label, self.model._meta.module_name
return '<a href="%s" >Tags</a>' % reverse('admin:%s_%s_taglist' % info, args=(obj.id,))
tag_changelist_link.allow_tags = True
tag_changelist_link.short_description = 'Tags'
#csrf_protect_m
def tag_changelist(self, request, *args, **kwargs):
obj_id = unquote(args[0])
info = self.model._meta.app_label, self.model._meta.module_name
category = self.get_object(request, obj_id)
tag_admin = CategoryTagAdmin(Tag, self.admin_site, self, category_id=obj_id )
extra_context = {
'parent': {
'has_change_permission': self.has_change_permission(request, obj_id),
'opts': self.model._meta,
'object': category,
},
}
return tag_admin.changelist_view(request, extra_context)
def get_urls(self):
info = self.model._meta.app_label, self.model._meta.module_name
urls= patterns('',
url(r'^(.+)/tags/$', self.admin_site.admin_view(self.tag_changelist), name='%s_%s_taglist' % info )
)
return urls + super(CategoryAdmin, self).get_urls()
admin.site.register(Category, CategoryAdmin)
The items in the categories changelist have an extra column with a link made by the tag_changelist_link pointing to the CategoryAdmin.tag_changelist. This method creates a CategoryTagAdmin instance with some extras and returns its changelist_view.
This way you have a filtered tag changelist on every category. To fix the breadcrumbs of the tag_changelist view you need to set the CategoryTagAdmin.change_list_template to a own template that {% extends 'admin/change_list.html' %} and overwrites the {% block breadcrumbs %}. That is where you will need the parent variable from the extra_context to create the correct urls.
If you plan to implement a tag_changeview and tag_addview method you need to make sure that the links rendered in variouse admin templates point to the right url (e.g. calling the change_view with a form_url as paramter).
A save_model method on the CategoryTagAdmin can set the default category when adding new tags.
def save_model(self, request, obj, form, change):
obj.category_id = self.category_id
super(CategoryTagAdmin, self).__init__(request, obj, form, change)
If you still want to stick to the apache restart aproach ... Yes you can restart Django. It depends on how you are deploying the instance.
On an apache you can touch the wsgi file that will reload the instance os.utime(path/to/wsgi.py.
When using uwsgi you can use uwsgi.reload().
You can check the source code of Rosetta how they are restarting the instance after the save translations (views.py).
So I found a half-solution.
def create_subclass(baseclass, name):
class Meta:
app_label = 'fun'
attrs = {'__module__': '', 'Meta': Meta, 'cat': name }
newsub = type(name, (baseclass,), attrs)
return newsub
class TagAdmin(admin.ModelAdmin):
list_display = ('name', 'category')
def get_queryset(self, request):
return Tag.objects.filter(category = Category.objects.filter(name=self.cat))
for cat in Category.objects.all():
newsub = create_subclass(TagAdmin, str(cat.name))
create_modeladmin(newsub, model=Tag, name=str(cat.name))
It's working. But every time you add a new category, you need to refresh the server before it shows up (because admin.py is evaluated at runtime). Does anyone know a decent solution to this?

Prevent django admin from escaping html

I'm trying to display image thumbnails in django admin's list_display and I am doing it like this:
from django.utils.safestring import mark_safe
class PhotoAdmin(admin.ModelAdmin):
fields = ('title', 'image',)
list_display = ('title', '_get_thumbnail',)
def _get_thumbnail(self, obj):
return mark_safe(u'<img src="%s" />' % obj.admin_thumbnail.url)
Admin keeps displaying the thumbnail as escaped html, although I marked the string as safe. What am I doing wrong?
As of Django 1.9, you can use format_html(), format_html_join(), or allow_tags in your method. See the list_display docs for more info.
The code in the question using mark_safe will work. However a better option for methods like these might be format_html, which will escape arguments.
def _get_thumbnail(self, obj):
return format_html(u'<img src="{}" />', obj.admin_thumbnail.url)
In earlier versions of Django, using mark_safe() would not work, and Django would escape the output. The solution was to give the method an allow_tags attribute with the value set to True.
class PhotoAdmin(admin.ModelAdmin):
fields = ('title', 'image',)
list_display = ('title', '_get_thumbnail',)
def _get_thumbnail(self, obj):
return u'<img src="%s" />' % obj.admin_thumbnail.url
_get_thumbnail.allow_tags = True
I know this is a rather late answer, but I thought a more complete implementation would be helpful to others...
If you don't have it already with django-filer, get easy_thumbnails pip install easy-thumbnails.
# -*- coding: utf-8 -*-
from django.contrib import admin
from easy_thumbnails.files import get_thumbnailer
from models import Photo
class PhotoAdmin(admin.ModelAdmin):
list_display = ('_thumbnail', 'title', )
list_display_links = ('_thumbnail', 'title', ) # This makes the icon clickable too
readonly_fields = ('_thumbnail', )
fields = ('title', 'photo', )
def _thumbnail(self, obj):
if obj.photo:
thumbnailer = get_thumbnailer(obj.photo)
thumb = thumbnailer.get_thumbnail({
'crop': True,
'size': (50, 50),
# Sharpen it up a little, since its so small...
'detail': True,
# Put other options here...
})
# Note: we get the actual width/height rather than
# hard-coding 50, 50, just to be DRYer
return u'<img src="%s" alt="thumbnail: %s" width="%d" height="%d"/>' % (thumb.url, obj.photo.name, thumb.width, thumb.height)
else:
return "[No Image]"
# Optional, Provide a nicer label in the display
_thumbnail.short_description = 'Thumbnail'
# Required, leaves the markup un-escaped
_thumbnail.allow_tags = True
admin.site.register(Photo, PhotoAdmin)