Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Django Admin: Pushing the Pony's boundries (DjangoCon US 2016)

Django Admin: Pushing the Pony's boundries (DjangoCon US 2016)

Ce99ccbfed462de9b46953827df21fa5?s=128

Ola Sitarska

July 18, 2016
Tweet

Transcript

  1. PUSHING THE PONY’S BOUNDRIES

  2. BFF Ola + Admin ==

  3. None
  4. 7 years of building admin interfaces

  5. Ola Sitarska

  6. None
  7. DjangoCon Europe 2013, 2015, 2016 Django Girls Django Core Team

    DSF Code of Conduct Django Under The Hood Senior Django Dev @ Potato
  8. ADMIN

  9. Insides of the admin Magic tricks

  10. before after

  11. HOW ADMIN WORKS

  12. AdminSite ModelAdmin ChangeList

  13. AdminSite encapsulates an instance of the Django admin application, ready

    to be hooked in to your URLconf.
  14. site = AdminSite() AdminSite: URLs

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

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

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

    AdminSite: URLs
  18. 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
  19. 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
  20. # 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
  21. # 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
  22. # 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
  23. # 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
  24. AdminSite: model registry from django.contrib import admin admin.site.register(Author, AuthorAdmin)

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

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

    class AuthorAdmin(admin.ModelAdmin): admin.site.register(Author)
  27. 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’))
  28. AdminSite: basic config class AdminSite(object): # Text to put at

    the end of each page's <title>. site_title = ugettext_lazy('Django site admin') # Text to put in each page's <h1>. site_header = ugettext_lazy('Django administration') # Text to put at the top of the admin index page. index_title = ugettext_lazy('Site administration')
  29. ModelAdmin encapsulates all admin options and functionality for any given

    model.
  30. 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
  31. 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
  32. None
  33. 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
  34. None
  35. 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
  36. None
  37. 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
  38. None
  39. 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
  40. 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
  41. None
  42. 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
  43. None
  44. 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
  45. AdminSite ModelAdmin ChangeList

  46. Insides of the admin Magic tricks

  47. None
  48. None
  49. None
  50. Advanced permissions • organizers can edit only their own events

  51. None
  52. None
  53. None
  54. 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)
  55. 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)
  56. 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)
  57. 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)
  58. None
  59. None
  60. 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
  61. 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
  62. 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
  63. 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
  64. None
  65. Advanced permissions • organizers can edit only their own events

    • organizers can edit only future
 events
  66. 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
  67. 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
  68. 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
  69. 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
  70. 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
  71. 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
  72. None
  73. Custom method fields • display all organizers as list on

    ChangeList view
  74. 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”
  75. 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”
  76. 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”
  77. 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”
  78. None
  79. Custom method fields • display all organizers as list on

    ChangeList view • display status of the event
  80. 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
  81. 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
  82. 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
  83. None
  84. 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
  85. Custom method fields class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘get_logo_display’, ‘date')

    … def get_logo_display(self, obj): if obj.logo: return '<a href="{url}" target="_blank"><img src="{url}" width="100" /></a>'.format(self.logo.url) else: return 'No logo!' get_logo_display.allow_tags = True
  86. Custom method fields class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘get_logo_display’, ‘date')

    … def get_logo_display(self, obj): if obj.logo: return '<a href="{url}" target="_blank"><img src="{url}" width="100" /></a>'.format(self.logo.url) else: return 'No logo!' get_logo_display.allow_tags = True
  87. Custom method fields class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘get_logo_display’, ‘date')

    … def get_logo_display(self, obj): if obj.logo: return '<a href="{url}" target="_blank"><img src="{url}" width="100" /></a>'.format(self.logo.url) else: return 'No logo!' get_logo_display.allow_tags = True
  88. Custom method fields class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘get_logo_display’, ‘date')

    … def get_logo_display(self, obj): if obj.logo: return '<a href="{url}" target="_blank"><img src="{url}" width="100" /></a>'.format(self.logo.url) else: return 'No logo!' get_logo_display.allow_tags = True
  89. Custom method fields class EventAdmin(admin.ModelAdmin): list_display = ('name', ‘get_logo_display’, ‘date')

    … def get_logo_display(self, obj): if obj.logo: return '<a href="{url}" target="_blank"><img src="{url}" width="100" /></a>'.format(self.logo.url) else: return 'No logo!' get_logo_display.allow_tags = True
  90. None
  91. Custom actions • a way to publish multiple events on

    the homepage with one click
  92. None
  93. 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”
  94. 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
  95. 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
  96. 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
  97. 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
  98. DON’T FALL INTO the TRAP!

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

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

  101. DO: Make your managers and admins lifes easier with small

    tricks *
  102. DO: Make sure your admins can’t break the website with

    too much power +
  103. Thanks!

  104. @olasitarska ola@p.ota.to Thanks!