Show of hands of who has pushed the admin onto a client. We do it too, but with disclaimers, and customizations.
Some of the problems we, as developers, face:
... So we write software to solve software problems. Amirite?
"Before we get to the bad news, let me answer the question on your face."
Whoa.. Why should I care what the client thinks about the admin experience?
- You
The admin doesn't help you help yourself! Blegh!
The admin is generic and hasn't been tailored to how users think. Example, when adding an article, you may need categories, you may need meta information. Users don't think about this stuff - they think about adding articles.
Users are spoiled by fancy pants features in other software.
If you hand the admin to these users, they will make the comparison.
GMail makes it really easy to undo mistakes. Undo discard, undo move, etc.
Everyone makes mistakes. The admin is an unforgiving mistress.
Just how many fields were on that Satchmo model?
Your form is a conversation with your user.
http://www.flickr.com/photos/11263821@N05/2224626086
1 class ArticleAdmin(admin.ModelAdmin):
2 class Media:
3 css = {
4 "all": ("my_styles.css",)
5 }
6 js = ("my_code.js",)
django.contrib.admin
is a "reusable application"admin/base.html
admin/index.html
admin/change_form.html
admin/change_list.html
Templates can be overridden:
admin/change_form.html
admin/<my_app>/change_form.html
admin/<my_app>/<my_model>/change_form.html
demo_app/templates/admin/demo_app/change_list.html
1 {% extends "admin/change_list.html" %}
2
3 {% block object-tools %}
4 <h1 class="errornote">
5 Look Here!
6 </h1>
7 {{ block.super }}
8 {% endblock %}
{{ block.super }}
to extend blocksextrahead
block in base.html
for admin-wide medialist_display
, fields
, ordering
, etc.)exclude
, inlines
, form
, etc.)list_editable
, actions
, etc.) 1 from django.contrib import admin
2 from django.contrib.auth.admin import UserAdmin
3 from demo_app.models import UserProfile
4
5 class UserProfileInline(admin.TabularInline):
6 model = UserProfile
7 fk_name = 'user'
8 max_num = 1
9
10 class CustomUserAdmin(UserAdmin):
11 inlines = [UserProfileInline]
12
13 admin.site.unregister(User)
14 admin.site.register(User, CustomUserAdmin)
1 class ArticleAdmin(admin.ModelAdmin):
2 def save_model(self, request, obj, form, change):
3 obj.user = request.user
4 obj.save()
5
6 def queryset(self, request):
7 qs = self.model._default_manager.filter(user=request.user)
8 return qs
ModelAdmin
's functionality is a wrapper around ModelForm
ModelAdmin
, chances are ModelForm
can helpdjango.forms
and no different in functionality 1 class AuthorForm(forms.ModelForm):
2 exclude_states = ['AS', 'GU', 'MP', 'VI',]
3 def __init__(self, *args, **kwargs):
4 # initalize form
5 super(AuthorForm, self).__init__(*args, **kwargs)
6
7 # rebuild choices
8 w = self.fields['state'].widget
9 choices = []
10 for key, value in w.choices:
11 if key not in self.exclude_states:
12 choices.append((key, value))
13 w.choices = choices
14
15 class AuthorAdmin(admin.ModelAdmin):
16 form = AuthorForm
http://www.flickr.com/photos/sharynmorrow/3019436/
django.contrib.admin.sites.AdminSite
django.contrib.admin.options.ModelAdmin
django.forms.models.ModelForm
django.contrib.admin.options.InlineModelAdmin
django.forms.formsets
ipdb.set_trace()
)
http://www.flickr.com/photos/bobtravis/485216368/
1 class PostAdmin(admin.ModelAdmin):
2 def my_view(self, request):
3 return admin_my_view(request, self)
4
5 def get_urls(self):
6 urls = super(PostAdmin, self).get_urls()
7 my_urls = patterns('',
8 (r'^my_view/$', self.my_view)
9 )
10 return my_urls + urls
1 @permission_required('blog.add_post')
2 def admin_my_view(request, model_admin):
3 opts = model_admin.model._meta
4 admin_site = model_admin.admin_site
5 has_perm = request.user.has_perm(opts.app_label + '.' \
6 + opts.get_change_permission())
7 context = {
8 'admin_site': admin_site.name,
9 'title': "My Custom View",
10 'opts': opts,
11 'root_path': '/%s' % admin_site.root_path,
12 'app_label': opts.app_label,
13 'has_change_permission': has_perm
14 }
15 template = 'admin/demo_app/admin_my_view.html'
16 return render_to_response(template, context,
17 context_instance=RequestContext(request))
1 {% extends "admin/base_site.html" %}
2 {% load i18n %}
3 {% block breadcrumbs %}
4 <div class="breadcrumbs">
5 <a href="../../../">{% trans "Home" %}</a> ›
6 <a href="../../">{{ app_label|capfirst|escape }}</a> ›
7 {% if has_change_permission %}<a href="../">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %} › My Custom View
8 </div>
9 {% endblock %}
10
11 {% block content %}
12 <!-- do stuff here -->
13 {% endblock %}
Peter Baumgartner
pete@lincolnloop.com
Michael Trythall
michael@lincolnloop.com
(@mtrythall)
Please rate this talk at:
http://spkr8.com/t/4366
Built with Landslide (http://github.com/adamzap/landslide)