Advanced topics in Django admin - custom forms, fields, inlines, Grappelli, other amusing hacks
Michael “Sveder” Sverdlin
Respect ma authoritah!
Fear of the admin
And now we are BFFs
Whirlwind tour
Widgets
Form Fields
Forms
Formset
(+inlines)
Level 1 - The face lift
Grappelli
Not just a pretty face
django-relatedadminwidget
Form Assets - Media class
class CalendarWidget(TextInput):
class Media:
css = {
'all': ('pretty.css',),
'print': ('print.css',)
}
js = ('animations.js', 'actions.js')
All together - bigger widgets
class BiggerIconsRelatedWidget(RelatedWidgetWrapperBase):
class Media:
css = { 'all': ('bigger_related_widget_icons.css',)}
.related-widget-wrapper-link > img {
width: 18px; height: 18px;
}
LogEntry.objects.log_action(user_id=user.id, content_type_id=ContentType.objects.get_for_model(model).pk, object_id=model.id, object_repr=repr(model), action_flag=ADDITION|CHANGE|DELETION, change_message=”Some message”)
Level 15 - Customization
Safer Logins
Prevent MITM attacks, prevent anything sniffing on the server. We REALLY don’t want to know your password.
from django.contrib import admin
admin.autodiscover()
admin.site.login_form = PasswordHashingAuthForm
class PasswordHashingForm(AuthenticationForm):
class Media:
js = ("password_hasher.js", "http://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/core-min.js",)
form.addEventListener("submit", hashPassword, false);
function hashPassword() {
password_input = document.getElementById("id_password");
password_input.value = CryptoJS.SHA1(password_input.value + "salt").toString(CryptoJS.enc.Hex);
}
Custom Fields and Widgets
def render(self, name, value, attrs=None):
result = super(CheckBoxInputMoneyField, self).render(name, value, attrs)
name = get_checkbox_name(name)
attrs['id'] = 'id_' + name
# If the value is empty, it means that the amount was not given and thus the checkbox should be on:
if not value:
attrs["checked"] = "checked"
return self.pretty_check_box.render(name, False, attrs, label=self.label) + result
class PrettyCheckboxWidget(CheckboxInput):
def render(self, name, value, attrs=None, label=None):
final_attrs = self.build_attrs(attrs, type='checkbox', name=name)
if self.check_test(value):
final_attrs['checked'] = 'checked'
return format_html('<label style="float:None;display:inline;margin-right:10px" for="{0}"><input{1} /> {2}</label>',
attrs['id'], flatatt(final_attrs), label)
Boss Fight:
̸̨̡̹̫͈̺̩̬̙ͅT̻̦h̺̼̬̰͔̫̙͓͘͟è̛͈̫̲̲͚̩͈͖ ̻̀w̴̼̪i̶̡͈͞ͅd͓̘͔̳̯g͔̝̖͖͔̺͍͈̦͡e͘҉̜̲̞̜̘t̗͉̻ ̧̞͓͖̺̱̳̫͇f̵̗̝͈͎̺̺̯͞r̨̤̰̮͔͢o͏̛͔̱̘̲͓̮m͞҉̘̹̰̀ ̴̯̻̭h̗̯͖͕̹̗e̛͚̙̳̘͔l̨͚̜̪͈͖͕͚͡l͚̥!̴̜̩̮̠̤̲͘͢
̵̵͔̳̠̻̪
Non Ajax Autocomplete
AutocompleteSelectWidget
if name not in self.__class__.names_seen:
self.__class__.names_seen.append(name)
else:
self.__class__.names_seen = [name]
self.__class__.in_dom = False
tag_name = self.get_name()
if not self.__class__.in_dom:
autocomplete_tags = u",".join([u'"%s"' % choice for choice in self.choices])
tags_setup = u"""window.%s_tags = [%s];""" % (tag_name, autocomplete_tags)
self.__class__.in_dom = True
autocomplete_template = u"""<script>
$(function() {
%s
$( "#%s" ).autocomplete({
source: window.%s_tags
});});
</script>
<div class="ui-widget">
<input id="%s" name="%s" value="%s" width=200>
</div>""" % (tags_setup, name, tag_name, name, name, value)
return mark_safe(autocomplete_template)
And then we had * problem
But it works™
So what did we learn
About me
Q and A