Slide 1

Slide 1 text

Hallo!

Slide 2

Slide 2 text

How Django Admin Works

Slide 3

Slide 3 text

• AdminSite • ModelAdmin • ChangeList

Slide 4

Slide 4 text

AdminSite Encapsulates an instance of the Django admin application, ready to be hooked in to your URLconf

Slide 5

Slide 5 text

ModelAdmin Encapsulates all admin options and functionality for any given model

Slide 6

Slide 6 text

ChangeList Makes sure that the list of objects in a model applies correct filters, orders and pagination

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

AdminSite

Slide 9

Slide 9 text

AdminSite Encapsulates an instance of the Django admin application, ready to be hooked in to your URLconf

Slide 10

Slide 10 text

AdminSite Encapsulates an instance of the Django admin application, ready to be hooked in to your URLconf • URLs • ModelAdmin registry • Basic site configuration

Slide 11

Slide 11 text

AdminSite Autodiscover admin.autodiscover()

Slide 12

Slide 12 text

AdminSite URLs url(r'^admin/', include(admin.site.urls)),

Slide 13

Slide 13 text

AdminSite URLs site = AdminSite()

Slide 14

Slide 14 text

AdminSite URLs class AdminSite(object): … @property def urls(self): return self.get_urls(), 'admin', self.name

Slide 15

Slide 15 text

AdminSite URLs class AdminSite(object): … @property def get_urls(self): # Admin-site-wide urls urlpatterns = [ url(r'^$', wrap(self.index), name='index'), url(r'^login/$', self.login, name='login'), url(r'^logout/$', wrap(self.logout), name='logout'), ] …

Slide 16

Slide 16 text

AdminSite URLs # Add in each model's views, and create a list of # valid URLS for the app_index for model, model_admin in self._registry.items(): urlpatterns += [ url(r'^%s/%s/' % (model._meta.app_label, model._meta.model_name), include(model_admin.urls)), ]

Slide 17

Slide 17 text

AdminSite ModelAdmin registry from django.contrib import admin admin.site.register(Author, AuthorAdmin)

Slide 18

Slide 18 text

AdminSite ModelAdmin registry from django.contrib import admin admin.site.register(Author, AuthorAdmin) or @admin.register(Author) class AuthorAdmin(admin.ModelAdmin):

Slide 19

Slide 19 text

AdminSite ModelAdmin registry from django.contrib import admin admin.site.register(Author, AuthorAdmin) or @admin.register(Author) class AuthorAdmin(admin.ModelAdmin): or admin.site.register(Author)

Slide 20

Slide 20 text

AdminSite ModelAdmin registry admin.register(Author, list_display=('name', 'age'))

Slide 21

Slide 21 text

AdminSite Configuration class AdminSite(object): # Text to put at the end of each page's . site_title = ugettext_lazy('Django site admin') # Text to put in each page's

. site_header = ugettext_lazy('Django administration') # Text to put at the top of the admin index page. index_title = ugettext_lazy('Site administration')

Slide 22

Slide 22 text

AdminSite Configuration class AdminSite(object): login_form = None index_template = None app_index_template = None login_template = None logout_template = None password_change_template = None password_change_done_template = None

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

ModelAdmin

Slide 25

Slide 25 text

ModelAdmin Encapsulates all admin options and functionality for any given model.

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

Ouch

Slide 28

Slide 28 text

ModelAdmin Custom views def get_urls(self): urlpatterns = [ url(r'^$', wrap(self.changelist_view), name='%s_%s_changelist' % info), url(r'^add/$', wrap(self.add_view), name='%s_%s_add' % info), url(r'^(.+)/history/$', wrap(self.history_view), name='%s_%s_history' % info), url(r'^(.+)/delete/$', wrap(self.delete_view), name='%s_%s_delete' % info), url(r'^(.+)/change/$', wrap(self.change_view), name='%s_%s_change' % info), # For backwards compatibility (was the change url before 1.9) url(r'^(.+)/$', wrap(RedirectView.as_view( pattern_name='%s:%s_%s_change' % ((self.admin_site.name,) + info) ))), ] return urlpatterns

Slide 29

Slide 29 text

ModelAdmin Custom views def wrap(view): def wrapper(*args, **kwargs): return self.admin_site.admin_view(view)(*args, **kwargs) wrapper.model_admin = self return update_wrapper(wrapper, view)

Slide 30

Slide 30 text

ModelAdmin Custom views class MyModelAdmin(admin.ModelAdmin): def get_urls(self): urlpatterns = super(MyModelAdmin, self).get_urls() my_urls = [ url(r'^$', wrap(self.custom_view), name='app_model_custom'), ] return my_urls + urlpatterns

Slide 31

Slide 31 text

ModelAdmin Custom views class ModelAdmin(object): ... def add_view(self, request, form_url='', extra_context=None): return self.changeform_view(request, None, form_url, extra_context) def change_view(self, request, object_id, form_url='', extra_context=None): return self.changeform_view(request, object_id, form_url, extra_context)

Slide 32

Slide 32 text

ModelAdmin Custom views class ModelAdmin(object): ... def has_change_permission(self, request, obj=None): opts = self.opts codename = get_permission_codename('change', opts) return request.user.has_perm("%s.%s" % (opts.app_label, codename)) ...

Slide 33

Slide 33 text

ModelAdmin Templates overriding if add and self.add_form_template is not None: form_template = self.add_form_template else: form_template = self.change_form_template return TemplateResponse(request, form_template or [ "admin/%s/%s/change_form.html" % (app_label, opts.model_name), "admin/%s/change_form.html" % app_label, "admin/change_form.html" ], context)

Slide 34

Slide 34 text

ModelAdmin Saving changeform_view form class ModelAdmin(object): def changeform_view(self): ... ModelForm = self.get_form(request, obj) if request.method == 'POST': form = ModelForm(request.POST, request.FILES, instance=obj) if form.is_valid(): new_object = self.save_form(request, form, change=not add) else: new_object = form.instance if all_valid(formsets) and form_validated: self.save_model(request, new_object, form, not add) self.save_related(request, form, formsets, not add) ...

Slide 35

Slide 35 text

ModelAdmin Writing your own custom view, maybe?

Slide 36

Slide 36 text

ModelAdmin Custom views: Permissions class ModelAdmin(object): def has_change_permission(self, request, obj=None): opts = self.opts codename = get_permission_codename('change', opts) return request.user.has_perm("%s.%s" % (opts.app_label, codename)) class EventAdmin(admin.ModelAdmin): ... def stats_view(self, request, obj=None): if not self.has_change_permission(request, obj): raise PermissionDenied ...

Slide 37

Slide 37 text

ModelAdmin Custom views: Permissions class EventAdmin(admin.ModelAdmin): ... def has_change_permission(request, obj): if request.user in obj.team and super(EventAdmin, self).has_change_permission(request, obj): return True else: return False def stats_view(self, request, obj=None): if not self.has_change_permission(request, obj): raise PermissionDenied ...

Slide 38

Slide 38 text

ModelAdmin Gotcha! Admin is initialized on process startup admin.site.register(Model)

Slide 39

Slide 39 text

ModelAdmin Gotcha! Admin is initialized on process startup Don't do this: def has_change_permission(request, obj): if request.user in obj.team and super(EventAdmin, self).has_change_permission(request, obj): self.can_access = True

Slide 40

Slide 40 text

ModelAdmin pip install django-admin-views class TestAdmin(AdminViews): admin_views = ( ('Redirect to BBC', 'redirect_to_bbc'), ('Go to example.com', 'http://www.example.com'), ) def redirect_to_bbc(self, *args, **kwargs): return redirect('http://www.bbc.co.uk')

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

ModelAdmin Gotcha! ModelAdmin.list_editable class UserAdmin(admin.ModelAdmin): list_editable = ('is_staff',)

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

ModelAdmin ModelAdmin.form class UserAdmin(admin.ModelAdmin): form = UserChangeForm

Slide 45

Slide 45 text

ModelAdmin ModelAdmin.form & ModelAdmin.get_form() class UserAdmin(admin.ModelAdmin): form = UserChangeForm add_form = UserCreationForm def get_form(self, request, obj=None, **kwargs): if obj is None: defaults['form'] = self.add_form return super(UserAdmin, self).get_form(request, obj)

Slide 46

Slide 46 text

ModelAdmin Gotcha! ModelAdmin.raw_id_fields class UserAdmin(admin.ModelAdmin): raw_id_fields = ("group",)

Slide 47

Slide 47 text

ModelAdmin ModelAdmin.readonly_fields & ModelAdmin.get_readonly_fields() class EventAdmin(admin.ModelAdmin): readonly_fields = () def get_readonly_fields(self, request, obj=None): if obj and not request.user.is_superuser: return self.readonly_fields + ('email', 'team') return self.readonly_fields

Slide 48

Slide 48 text

ModelAdmin ModelAdmin.get_queryset() class EventAdmin(admin.ModelAdmin): def get_queryset(self, request): qs = super(EventAdmin, self).get_queryset(request) if request.user.is_superuser: return qs return qs.filter(team=request.user)

Slide 49

Slide 49 text

ModelAdmin

Slide 50

Slide 50 text

ChangeList

Slide 51

Slide 51 text

ModelAdmin ! ChangeList ModelAdmin.changelist_view def changelist_view(self, request, extra_context=None): ... ChangeList = self.get_changelist(request) cl = ChangeList(request, self.model, list_display, list_display_links, list_filter, self.date_hierarchy, search_fields, list_select_related, self.list_per_page, self.list_max_show_all, self.list_editable, self) ...

Slide 52

Slide 52 text

ModelAdmin ! ChangeList ModelAdmin.get_changelist def get_changelist(self, request, **kwargs): """ Returns the ChangeList class for use on the changelist page. """ from django.contrib.admin.views.main import ChangeList return ChangeList

Slide 53

Slide 53 text

Role of ChangeList • figure out what are the filter, search, order and pagination parameters from query string • verify that lookups on these parameters are allowed • filter & search the queryset, apply order and paginate

Slide 54

Slide 54 text

ChangeList Filters • Filter is a class • Usually extended from SimpleListFilter or FieldListFilter

Slide 55

Slide 55 text

ChangeList Filters class MyModelAdmin(admin.ModelAdmin): list_filter = ('type',) • RelatedFieldListFilter • BooleanFieldListFilter • ChoicesFieldListFilter • DateFieldListFilter

Slide 56

Slide 56 text

ChangeList Filters FieldListFilter.register(lambda f: isinstance(f, (models.BooleanField, models.NullBooleanField)), BooleanFieldListFilter)

Slide 57

Slide 57 text

ChangeList Filters class MyModelAdmin(admin.ModelAdmin): list_filter = (('type', BooleanFieldListFilter),)

Slide 58

Slide 58 text

ChangeList Filters class MyModelAdmin(admin.ModelAdmin): list_filter = ('type', DecadeBornListFilter)

Slide 59

Slide 59 text

ChangeList Custom Filter class DecadeBornListFilter(admin.SimpleListFilter): title = _('decade born') parameter_name = 'decade' def lookups(self, request, model_admin): return ( ('80s', _('in the eighties')), ('90s', _('in the nineties')), ) def queryset(self, request, queryset): if self.value() == '80s': return queryset.filter(birthday__gte=date(1980, 1, 1), birthday__lte=date(1989, 12, 31)) if self.value() == '90s': return queryset.filter(birthday__gte=date(1990, 1, 1), birthday__lte=date(1999, 12, 31))

Slide 60

Slide 60 text

ChangeList.get_queryset() • ChangeList.get_results() • ModelAdmin.get_paginator() • ChangeList.get_queryset() • ChangeList.get_filters() • ChangeList.get_filters_params() • ModelAdmin.lookup_allowed() • Apply filters

Slide 61

Slide 61 text

ChangeList.get_queryset() Applying filters def get_queryset(self, request): ... qs = self.root_queryset for filter_spec in self.filter_specs: new_qs = filter_spec.queryset(request, qs) if new_qs is not None: qs = new_qs ...

Slide 62

Slide 62 text

BAM!

Slide 63

Slide 63 text

ChangeList.get_queryset() • ChangeList.get_ordering() • ModelAdmin.get_search_results() • !

Slide 64

Slide 64 text

PEW PEW! ⚡

Slide 65

Slide 65 text

No content

Slide 66

Slide 66 text

No content

Slide 67

Slide 67 text

Sprints on Saturday! ! "

Slide 68

Slide 68 text

Dankjewel! ! "