Slide 1

Slide 1 text

PUSHING THE PONY’S BOUNDRIES

Slide 2

Slide 2 text

BFF Ola + Admin ==

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

7 years of building admin interfaces

Slide 5

Slide 5 text

Ola Sitarska

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

DjangoCon Europe 2013, 2015, 2016 Django Girls Django Core Team DSF Code of Conduct Django Under The Hood Senior Django Dev @ Potato

Slide 8

Slide 8 text

ADMIN

Slide 9

Slide 9 text

Insides of the admin Magic tricks

Slide 10

Slide 10 text

before after

Slide 11

Slide 11 text

HOW ADMIN WORKS

Slide 12

Slide 12 text

AdminSite ModelAdmin ChangeList

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

site = AdminSite() AdminSite: URLs

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

super_awesome_site = AdminSite() 
 url(r’^super-awesome-admin/', 
 include(super_awesome_site.urls)), AdminSite: URLs

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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'), … AdminSite: URLs AdminSite: URLs

Slide 19

Slide 19 text

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'), … AdminSite: URLs

Slide 20

Slide 20 text

# 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) ), ] AdminSite: URLs

Slide 21

Slide 21 text

# 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) ), ] AdminSite: URLs

Slide 22

Slide 22 text

# 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) ), ] AdminSite: URLs

Slide 23

Slide 23 text

# 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) ), ] AdminSite: URLs

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

AdminSite: model registry from django.contrib import admin admin.site.register(Author, AuthorAdmin) @admin.register(Author) class AuthorAdmin(admin.ModelAdmin): admin.site.register(Author) register(Author, list_display=(‘name’, ‘age’))

Slide 28

Slide 28 text

AdminSite: basic config 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 29

Slide 29 text

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

Slide 30

Slide 30 text

ModelAdmin actions actions_on_top actions_on_bottom actions_selection_counter date_hierarchy exclude fields fieldsets filter_horizontal form formfield_overrides inlines list_display list_display_links list_editable list_filter list_max_show_all list_per_page list_select_related ordering paginator prepopulated_fields preserve_filters radio_fields raw_id_fields readonly_fields save_as save_on_top search_fields show_full_result_count view_on_site

Slide 31

Slide 31 text

ModelAdmin actions actions_on_top actions_on_bottom actions_selection_counter date_hierarchy exclude fields fieldsets filter_horizontal form formfield_overrides inlines list_display list_display_links list_editable list_filter list_max_show_all list_per_page list_select_related ordering paginator prepopulated_fields preserve_filters radio_fields raw_id_fields readonly_fields save_as save_on_top search_fields show_full_result_count view_on_site

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

ModelAdmin actions actions_on_top actions_on_bottom actions_selection_counter date_hierarchy exclude fields fieldsets filter_horizontal form formfield_overrides inlines list_display list_display_links list_editable list_filter list_max_show_all list_per_page list_select_related ordering paginator prepopulated_fields preserve_filters radio_fields raw_id_fields readonly_fields save_as save_on_top search_fields show_full_result_count view_on_site

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

ModelAdmin actions actions_on_top actions_on_bottom actions_selection_counter date_hierarchy exclude fields fieldsets filter_horizontal form formfield_overrides inlines list_display list_display_links list_editable list_filter list_max_show_all list_per_page list_select_related ordering paginator prepopulated_fields preserve_filters radio_fields raw_id_fields readonly_fields save_as save_on_top search_fields show_full_result_count view_on_site

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

ModelAdmin actions actions_on_top actions_on_bottom actions_selection_counter date_hierarchy exclude fields fieldsets filter_horizontal form formfield_overrides inlines list_display list_display_links list_editable list_filter list_max_show_all list_per_page list_select_related ordering paginator prepopulated_fields preserve_filters radio_fields raw_id_fields readonly_fields save_as save_on_top search_fields show_full_result_count view_on_site

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

ModelAdmin actions actions_on_top actions_on_bottom actions_selection_counter date_hierarchy exclude fields fieldsets filter_horizontal form formfield_overrides inlines list_display list_display_links list_editable list_filter list_max_show_all list_per_page list_select_related ordering paginator prepopulated_fields preserve_filters radio_fields raw_id_fields readonly_fields save_as save_on_top search_fields show_full_result_count view_on_site

Slide 40

Slide 40 text

ModelAdmin actions actions_on_top actions_on_bottom actions_selection_counter date_hierarchy exclude fields fieldsets filter_horizontal form formfield_overrides inlines list_display list_display_links list_editable list_filter list_max_show_all list_per_page list_select_related ordering paginator prepopulated_fields preserve_filters radio_fields raw_id_fields readonly_fields save_as save_on_top search_fields show_full_result_count view_on_site

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

ModelAdmin actions actions_on_top actions_on_bottom actions_selection_counter date_hierarchy exclude fields fieldsets filter_horizontal form formfield_overrides inlines list_display list_display_links list_editable list_filter list_max_show_all list_per_page list_select_related ordering paginator prepopulated_fields preserve_filters radio_fields raw_id_fields readonly_fields save_as save_on_top search_fields show_full_result_count view_on_site

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

ModelAdmin actions actions_on_top actions_on_bottom actions_selection_counter date_hierarchy exclude fields fieldsets filter_horizontal form formfield_overrides inlines list_display list_display_links list_editable list_filter list_max_show_all list_per_page list_select_related ordering paginator prepopulated_fields preserve_filters radio_fields raw_id_fields readonly_fields save_as save_on_top search_fields show_full_result_count view_on_site

Slide 45

Slide 45 text

AdminSite ModelAdmin ChangeList

Slide 46

Slide 46 text

Insides of the admin Magic tricks

Slide 47

Slide 47 text

No content

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

No content

Slide 50

Slide 50 text

Advanced permissions • organizers can edit only their own events

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

Advanced permissions class EventAdmin(admin.ModelAdmin): list_display = ('name', 'organizers', ‘date') search_fields = ('city', 'country', 'name') def get_queryset(self, request): qs = super(EventAdmin, self).queryset(request) if request.user.is_superuser: return qs return qs.filter(team=request.user)

Slide 55

Slide 55 text

Advanced permissions class EventAdmin(admin.ModelAdmin): list_display = ('name', 'organizers', ‘date') search_fields = ('city', 'country', 'name') def get_queryset(self, request): qs = super(EventAdmin, self).queryset(request) if request.user.is_superuser: return qs return qs.filter(team=request.user)

Slide 56

Slide 56 text

Advanced permissions class EventAdmin(admin.ModelAdmin): list_display = ('name', 'organizers', ‘date') search_fields = ('city', 'country', 'name') def get_queryset(self, request): qs = super(EventAdmin, self).queryset(request) if request.user.is_superuser: return qs return qs.filter(team=request.user)

Slide 57

Slide 57 text

Advanced permissions class EventAdmin(admin.ModelAdmin): list_display = ('name', 'organizers', ‘date') search_fields = ('city', 'country', 'name') def get_queryset(self, request): qs = super(EventAdmin, self).queryset(request) if request.user.is_superuser: return qs return qs.filter(team=request.user)

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

No content

Slide 60

Slide 60 text

Advanced permissions def get_form(self, request, obj=None, **kwargs): form = super(EventPageContentAdmin, self).get_form(request, obj, **kwargs) if not request.user.is_superuser: if 'page' in form.base_fields: form.base_fields['page'].queryset = \ EventPage.objects.filter( event__team=request.user ) return form

Slide 61

Slide 61 text

Advanced permissions def get_form(self, request, obj=None, **kwargs): form = super(EventPageContentAdmin, self).get_form(request, obj, **kwargs) if not request.user.is_superuser: if 'page' in form.base_fields: form.base_fields['page'].queryset = \ EventPage.objects.filter( event__team=request.user ) return form

Slide 62

Slide 62 text

Advanced permissions def get_form(self, request, obj=None, **kwargs): form = super(EventPageContentAdmin, self).get_form(request, obj, **kwargs) if not request.user.is_superuser: if 'page' in form.base_fields: form.base_fields['page'].queryset = \ EventPage.objects.filter( event__team=request.user ) return form

Slide 63

Slide 63 text

Advanced permissions def get_form(self, request, obj=None, **kwargs): form = super(EventPageContentAdmin, self).get_form(request, obj, **kwargs) if not request.user.is_superuser: if 'page' in form.base_fields: form.base_fields['page'].queryset = \ EventPage.objects.filter( event__team=request.user ) return form

Slide 64

Slide 64 text

No content

Slide 65

Slide 65 text

Advanced permissions • organizers can edit only their own events • organizers can edit only future
 events

Slide 66

Slide 66 text

ModelAdmin actions actions_on_top actions_on_bottom actions_selection_counter date_hierarchy exclude fields fieldsets filter_horizontal form formfield_overrides inlines list_display list_display_links list_editable list_filter list_max_show_all list_per_page list_select_related ordering paginator prepopulated_fields preserve_filters radio_fields raw_id_fields readonly_fields save_as save_on_top search_fields show_full_result_count view_on_site

Slide 67

Slide 67 text

Advanced permissions def get_readonly_fields(self, request, obj=None): if obj and not request.user.is_superuser: if not obj.page.event.is_upcoming(): return set( [x.name for x in self.model._meta.fields] ) return self.readonly_fields

Slide 68

Slide 68 text

Advanced permissions def get_readonly_fields(self, request, obj=None): if obj and not request.user.is_superuser: if not obj.page.event.is_upcoming(): return set( [x.name for x in self.model._meta.fields] ) return self.readonly_fields

Slide 69

Slide 69 text

Advanced permissions def get_readonly_fields(self, request, obj=None): if obj and not request.user.is_superuser: if not obj.page.event.is_upcoming(): return set( [x.name for x in self.model._meta.fields] ) return self.readonly_fields

Slide 70

Slide 70 text

Advanced permissions def get_readonly_fields(self, request, obj=None): if obj and not request.user.is_superuser: if not obj.page.event.is_upcoming(): return set( [x.name for x in self.model._meta.fields] ) return self.readonly_fields

Slide 71

Slide 71 text

Advanced permissions def get_readonly_fields(self, request, obj=None): if obj and not request.user.is_superuser: if not obj.page.event.is_upcoming(): return set( [x.name for x in self.model._meta.fields] ) return self.readonly_fields

Slide 72

Slide 72 text

No content

Slide 73

Slide 73 text

Custom method fields • display all organizers as list on ChangeList view

Slide 74

Slide 74 text

Custom method fields class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘get_organizers’, ‘date') … def get_organizers(self, obj): members = [] for member in self.team.all(): members.append( u’{name} <{email}>’.format( member.get_full_name(), member.email ) ) return ', '.join(members) get_organizers.short_description = “Organizers”

Slide 75

Slide 75 text

Custom method fields class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘get_organizers’, ‘date') … def get_organizers(self, obj): members = [] for member in self.team.all(): members.append( u’{name} <{email}>’.format( member.get_full_name(), member.email ) ) return ', '.join(members) get_organizers.short_description = “Organizers”

Slide 76

Slide 76 text

Custom method fields class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘get_organizers’, ‘date') … def get_organizers(self, obj): members = [] for member in self.team.all(): members.append( u’{name} <{email}>’.format( member.get_full_name(), member.email ) ) return ', '.join(members) get_organizers.short_description = “Organizers”

Slide 77

Slide 77 text

Custom method fields class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘get_organizers’, ‘date') … def get_organizers(self, obj): members = [] for member in self.team.all(): members.append( u’{name} <{email}>’.format( member.get_full_name(), member.email ) ) return ', '.join(members) get_organizers.short_description = “Organizers”

Slide 78

Slide 78 text

No content

Slide 79

Slide 79 text

Custom method fields • display all organizers as list on ChangeList view • display status of the event

Slide 80

Slide 80 text

Custom method fields class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘is_event_over’, ‘date') … def is_event_over(self, obj): return not obj.is_upcoming() is_event_over.boolean = True

Slide 81

Slide 81 text

Custom method fields class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘is_event_over’, ‘date') … def is_event_over(self, obj): return not obj.is_upcoming() is_event_over.boolean = True

Slide 82

Slide 82 text

Custom method fields class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘is_event_over’, ‘date') … def is_event_over(self, obj): return not obj.is_upcoming() is_event_over.boolean = True

Slide 83

Slide 83 text

No content

Slide 84

Slide 84 text

Custom method fields • display all organizers as list on ChangeList view • display status of the event • display images on a list in the ChangeList view

Slide 85

Slide 85 text

Custom method fields class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘get_logo_display’, ‘date') … def get_logo_display(self, obj): if obj.logo: return ''.format(self.logo.url) else: return 'No logo!' get_logo_display.allow_tags = True

Slide 86

Slide 86 text

Custom method fields class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘get_logo_display’, ‘date') … def get_logo_display(self, obj): if obj.logo: return ''.format(self.logo.url) else: return 'No logo!' get_logo_display.allow_tags = True

Slide 87

Slide 87 text

Custom method fields class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘get_logo_display’, ‘date') … def get_logo_display(self, obj): if obj.logo: return ''.format(self.logo.url) else: return 'No logo!' get_logo_display.allow_tags = True

Slide 88

Slide 88 text

Custom method fields class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘get_logo_display’, ‘date') … def get_logo_display(self, obj): if obj.logo: return ''.format(self.logo.url) else: return 'No logo!' get_logo_display.allow_tags = True

Slide 89

Slide 89 text

Custom method fields class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘get_logo_display’, ‘date') … def get_logo_display(self, obj): if obj.logo: return ''.format(self.logo.url) else: return 'No logo!' get_logo_display.allow_tags = True

Slide 90

Slide 90 text

No content

Slide 91

Slide 91 text

Custom actions • a way to publish multiple events on the homepage with one click

Slide 92

Slide 92 text

No content

Slide 93

Slide 93 text

Custom actions class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘get_logo_display’, ‘date’) actions = [‘show_on_homepage’] … def show_on_homepage(self, request, queryset): queryset.update(is_on_homepage=True) show_on_homepage.short_description = "Publish selected”

Slide 94

Slide 94 text

class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘get_logo_display’, ‘date’) actions = [‘show_on_homepage’] … def show_on_homepage(self, request, queryset): queryset.update(is_on_homepage=True) show_on_homepage.short_description = "Publish selected” Custom actions

Slide 95

Slide 95 text

class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘get_logo_display’, ‘date’) actions = [‘show_on_homepage’] … def show_on_homepage(self, request, queryset): queryset.update(is_on_homepage=True) show_on_homepage.short_description = "Publish selected” Custom actions

Slide 96

Slide 96 text

class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘get_logo_display’, ‘date’) actions = [‘show_on_homepage’] … def show_on_homepage(self, request, queryset): queryset.update(is_on_homepage=True) show_on_homepage.short_description = "Publish selected” Custom actions

Slide 97

Slide 97 text

class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘get_logo_display’, ‘date’) actions = [‘show_on_homepage’] … def show_on_homepage(self, request, queryset): queryset.update(is_on_homepage=True) show_on_homepage.short_description = "Publish selected” Custom actions

Slide 98

Slide 98 text

DON’T FALL INTO the TRAP!

Slide 99

Slide 99 text

DON’T: Use Django Admin as your user facing interface

Slide 100

Slide 100 text

DON’T: Waste too much time trying to customize something

Slide 101

Slide 101 text

DO: Make your managers and admins lifes easier with small tricks *

Slide 102

Slide 102 text

DO: Make sure your admins can’t break the website with too much power +

Slide 103

Slide 103 text

Thanks!

Slide 104

Slide 104 text

@olasitarska [email protected] Thanks!