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

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>")

Related

Specifying Django widget attribute in ModelForm rather than as HTML <style>

I have a Django input slider defined as follows:
#widgets.py
from django.forms.widgets import NumberInput
class RangeInput(NumberInput):
input_type = 'range'
#forms.py
from polls.widgets import RangeInput
class VoteForm(forms.ModelForm):
class Meta:
#model = CC_Responses
model = CC_Resp_NoFK
fields = ['Person_ID', 'Test_date', 'Response_value']
# following added from SliderModelForm
widgets ={
'Response_value':RangeInput
}
which is “processed” by the view
def p2vote(request,q_id):
CC_question = get_object_or_404(CC_Questions, pk=q_id)
#
if request.method == 'POST':
form = VoteForm(request.POST)
if form.is_valid():
item = form.save(commit=False)
item.Q_ID = q_id
item.save()
return redirect('/polls/p2')
else:
formV = VoteForm()
return render(request, 'pollapp2/vote.html', {'var_name':CC_question,'form' : VoteForm()})
and in the template I have the inline CSS
<style>
/* Following CSS is used by the input slider (range) since Django assigns id value of id_Response_value */
/*See: https://stackoverflow.com/questions/110378/change-the-width-of-form-elements-created-with-modelform-in-django */
#id_Response_value{width:300px;}
</style>
which is associated with the slider/range
<label for="Response_value">Response value</label>
{% render_field form.Response_value rows="1" class="form-control" %}
I obtained id_Response_value by looking at the source of the rendered HTML in the browser – I guess I could explicitly set an ID. All the above works exactly as I want. I can control the width of the range slider using CSS.
Now, I believe, inline CSS is a “bad thing”, so as a step to improve my code I’m trying to associate the attribute “more directly” with the slider.
In Django documentation in the section: https://docs.djangoproject.com/en/4.0/topics/forms/modelforms/#overriding-the-default-fields
there is the following example:
from django.forms import ModelForm, Textarea
from myapp.models import Author
class AuthorForm(ModelForm):
class Meta:
model = Author
fields = ('name', 'title', 'birth_date')
widgets = {
'name': Textarea(attrs={'cols': 80, 'rows': 20}),
}
The dictionary attrs created looks like it has CSS “stuff” in it, so I coded in widgets in the model form in forms.py
from polls.widgets import RangeInput
class VoteForm(forms.ModelForm):
class Meta:
model = CC_Resp_NoFK
fields = ['Person_ID', 'Test_date', 'Response_value']
widgets ={
'Response_value':RangeInput(attrs={'width': '1000px'}),
}
I tried both:
attrs={'width': '1000px'}
and
attrs={'width': 1000}
Neither changed the width of the range slider. Is what I am doing possible in the (model)form? Have I just got a problem with what I am coding in attrs?
I may have misunderstood but I see something like the following promoted as what needs to go in the ModelForm
def __init__(self, *args, **kwargs):
super(ProductForm, self).__init__(*args, **kwargs) # Call to ModelForm constructor
self.fields['long_desc'].widget.attrs['cols'] = 10
self.fields['long_desc'].widget.attrs['rows'] = 20
No. Really? To change the attributes?

How to display base64 image in django admin?

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

Creating buttons in a column of a table created by django tables2

I have an extended admin model that creates action buttons. I have created a view to do pretty much the same thing. I have used tables2 and everything is just fine except for the actions column. I cannot find a way to generate the same button in the table. Is there a way to do this at all?
tables.py
from .models import Ticket
import django_tables2 as tables
'''from .admin import river_actions, create_river_button'''
class TicketTable(tables.Table):
class Meta:
model=Ticket
template_name='django_tables2/table.html'
fields = ('id','subject','request_type','material_type','productline','business','measurement_system',
'created_at','updated_at','status','river_action','project') # fields to display
attrs = {'class': 'mytable'}
'''attrs = {"class": "table-striped table-bordered"}'''
empty_text = "There are no tickets matching the search criteria..."
admin.py (the part that includes the model etc)
# Define a new User admin to get client info too while defining users
class UserAdmin(BaseUserAdmin):
inlines = (UserExtendInline, )
def create_river_button(obj,proceeding):
return '''
<input
type="button"
style=margin:2px;2px;2px;2px;"
value="%s"
onclick="location.href=\'%s\'"
/>
'''%(proceeding.meta.transition,
reverse('proceed_ticket',kwargs={'ticket_id':obj.pk, 'next_state_id':proceeding.meta.transition.destination_state.pk})
)
class TicketAdmin(admin.ModelAdmin):
list_display=('id','client','subject','request_type','material_type','productline','business','measurement_system', \
'created_at','updated_at','created_by','status','river_actions')
#list_display_links=None if has_model_permissions(request.user,Ticket,['view_ticket'],'mmrapp')==True else list_display
#list_display_links=list_display #use None to remove all links, or use a list to make some fields clickable
#search_fields = ('subject','material_type')
list_filter=[item for item in list_display if item!='river_actions'] #exclude river_actions since it is not related to a field and cannot be filtered
#Using fieldset, we can control which fields should be filled by the user in the ADD method. This way, created_by will be the
#logged in user and not a drop down choice on the admin site
fieldsets = [
(None, {
'fields': ('client','subject','description','request_type','material_type', \
'productline','business','measurement_system', 'project')
} ), #to make some field appear horizontal, put them into a []
]
formfield_overrides = {
models.CharField: {'widget': TextInput (attrs={'size':'40'})},
models.TextField: {'widget': Textarea(attrs={'rows':4, 'cols':80})},}
def get_list_display(self, request):
self.user=request.user
return super(TicketAdmin,self).get_list_display(request)
def river_actions(self,obj):
content=""
for proceeding in obj.get_available_proceedings(self.user):
content+=create_river_button(obj, proceeding)
return content
river_actions.allow_tags=True
#override save_model method to save current user since it is not on the admin page form anymore
def save_model(self, request, obj, form, change):
if not change:
# the object is being created and not changed, so set the user
obj.created_by = request.user
obj.save()
views.py
def display_tickets(request):
table = TicketTable(Ticket.objects.all())
RequestConfig(request).configure(table)
'''table = CustomerTable(Customer.objects.filter(self.kwargs['company']).order_by('-pk'))'''
return render(request,'mmrapp/ticket_display.html',{'table':table})
buttons created in admin page:
table created using tables2 in views missing buttons:
You must pass empty_values=() to the column, because by default, django-tables2 only renders the column if the value is not contained in empty_values for that column.
import django_tables2 as tables
from .admin import river_actions, create_river_button
from .models import Ticket
class TicketTable(tables.Table):
river_action = tables.Column(empty_values=())
class Meta:
model=Ticket
template_name='django_tables2/table.html'
fields = (
'id', 'subject', 'request_type', 'material_type', 'productline', 'business', 'measurement_system',
'created_at', 'updated_at', 'status', 'river_action', 'project'
) # fields to display
attrs = {'class': 'mytable'}
empty_text = "There are no tickets matching the search criteria..."
def render_river_action(self, record):
return create_river_button(record, ...)
This is also documented as Table.render_foo methods

django admin tinymce for part of textareas

I'm using django admin + grappelli + tinymce from grappelli.
It works perfect, but I can't figure out how to make an admin form with textareas and apply tinymce only for part of them.
For example, I've two fields: description and meta-description. I need tinymce only for description and I need textarea without tinymce for meta-descrption.
tinymce in admin is enabled via form like this:
class AdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
"""Sets the list of tags to be a string"""
instance = kwargs.get('instance', None)
if instance:
init = kwargs.get('initial', {})
kwargs['initial'] = init
super(AdminForm, self).__init__(*args, **kwargs)
class Media:
js = (
settings.STATIC_URL + 'grappelli/tinymce/jscripts/tiny_mce/tiny_mce.js',
settings.STATIC_URL + 'grappelli/tinymce_setup/tinymce_setup.js',
)
and enabled in admin.py:
class ModelAdmin(admin.ModelAdmin):
form = AdminForm
It looks like behaviour is described in the beginning of tinymce init:
tinyMCE.init({
mode: 'textareas',
theme: 'advanced',
skin: 'grappelli',
...
Is there a way to solve my issue?
By setting the mode to textareas, it won't give you any sort of selector control as to which one it applies the editor to. You'll most likely need to drop the mode setting and go with selector: http://www.tinymce.com/wiki.php/Configuration:selector
The django-tinymce config options are just a mirror for TinyMCE's settings.
On a field by field basis you can use this technique providing a custom ModelForm:
class XAdminForm(forms.ModelForm):
name = forms.CharField(label='Name', max_length=100,
widget=forms.TextInput(attrs={'size': '100'}))
something = forms.CharField(label='Something', max_length=SOME_MAX_LENGTH,
widget=forms.Textarea(attrs={'rows': '10', 'cols': '100'}))
note = forms.CharField(label='Note', max_length=NOTE_MAX_LENGTH,
widget=forms.Textarea(attrs={'class': 'ckeditor'}))
class Meta:
model = x
class Meta:
model = x
class XAdmin(admin.ModelAdmin):
model = X
form = XAdminForm
class Media:
js = ('/static/js/ckeditor/ckeditor.js',)
admin.site.register(X, XAdmin)

Django: override RelatedFieldWidgetWrapper

I want to change the way that the "+" icon for the foreign key in the admin site is shown.
I found that the widget that prints the code is RelatedFieldWidgetWrapper that is in django/contrib/admin/widgets.py.
So I wrote my version of this class and I changed its render function.
But now how can I use it? I mean... in the definition of my model do I have to use the formfield_overrides in this way?
formfield_overrides = {
models.ForeignKey: {'widget': customRelatedFieldWidgetWrapper},
}
I think that this is not the right way, because that widget is not the one that manage the whole foreign key, but only the "+" icon.
Am I wrong?
Thanks a lot.
You would need to create custom ModelForm for ModelAdmin and override widget there.
Example code:
#forms.py
class CustomForm(forms.ModelForm):
user = forms.ModelChoiceField(queryset=User.objects.all(), widget=yourCustomWidget)
class Meta:
model = MyModel
#admin.py
class MyModelAdmin(admin.ModelAdmin):
form = CustomForm
I approached this slightly differently by monkey-patching the widget - that way the change is reflected in all forms and you're not monkeying around with django's source code.
I ran into this as I was working on customizing yawd admin, a very nice Twitter-Bootstrap skin for admin interface. Now all my icons are jazzed up.
import django.contrib.admin.widgets
class MyRelatedFieldWidgetWrapper(django.contrib.admin.widgets.RelatedFieldWidgetWrapper):
"""
This class is a wrapper to a given widget to add the add icon for the
admin interface.
"""
def render(self, name, value, *args, **kwargs):
rel_to = self.rel.to
info = (rel_to._meta.app_label, rel_to._meta.model_name)
self.widget.choices = self.choices
output = [self.widget.render(name, value, *args, **kwargs)]
if self.can_add_related:
related_url = reverse(
'admin:%s_%s_add'
% info, current_app=self.admin_site.name
)
output.append(
"""
<a href="%s"
onclick="return showAddAnotherPopup(this);
alt="%s">
<i class="help icon-large icon-plus-sign"
id="add_id_%s"
data-original-title>
</i>
</a>""" % (related_url, _('Add Another'), name))
return mark_safe(''.join(output))
# Monkeypatch it
django.contrib.admin.widgets.RelatedFieldWidgetWrapper = MyRelatedFieldWidgetWrapper