2. Reusability
A guiding principle of Django
“Build applications, not projects”
Generic views
Thriving third party ecosystem
3. The Django contract
A Django view is a function that takes a
request object and returns a response object
ORM, middleware, template language, forms,
authentication system, admin, sites etc are all
optional extras
4. Generic views
Encapsulate common patterns in web
development
List of things / page about each things
Things that are archived by date
Things you can create/update/delete
Let’s look at the code for object_detail
5. object_detail drawbacks
You can’t swap the ORM for something else
(without duck typing your own queryset)
You have to use RequestContext
You can’t modify something added to the
context; you can only specify extra_context
That’s despite a great deal of effort going in
to making the behaviour customisable
6. newforms-admin
De-coupled admin from the rest of Django
Admin is just another application
A new approach to customisation
Powerful subclassing pattern
7. Finely grained permissions
class Entry(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey('auth.User')
class EntryAdmin(admin.ModelAdmin):
exclude = ('author',)
def queryset(self, request):
queryset = super(EntryAdmin, self).queryset(request)
return queryset.filter(author = request.user)
def save_model(self, request, obj, form, change):
obj.author = request.user
obj.save()
def has_change_permission(self, request, ojb=None):
if not obj:
return True # access to change list
return obj.author == request.user
has_delete_permission = has_change_permission
admin.site.register(Entry, EntryAdmin)
8. Finely grained permissions
class Entry(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey('auth.User')
class EntryAdmin(admin.ModelAdmin):
exclude = ('author',)
def queryset(self, request):
queryset = super(EntryAdmin, self).queryset(request)
return queryset.filter(author = request.user)
def save_model(self, request, obj, form, change):
obj.author = request.user
obj.save()
def has_change_permission(self, request, ojb=None):
if not obj:
return True # access to change list
return obj.author == request.user
has_delete_permission = has_change_permission
admin.site.register(Entry, EntryAdmin)
9. Finely grained permissions
class Entry(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey('auth.User')
class EntryAdmin(admin.ModelAdmin):
exclude = ('author',)
def queryset(self, request):
queryset = super(EntryAdmin, self).queryset(request)
return queryset.filter(author = request.user)
def save_model(self, request, obj, form, change):
obj.author = request.user
obj.save()
def has_change_permission(self, request, ojb=None):
if not obj:
return True # access to change list
return obj.author == request.user
has_delete_permission = has_change_permission
admin.site.register(Entry, EntryAdmin)
10. Finely grained permissions
class Entry(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey('auth.User')
class EntryAdmin(admin.ModelAdmin):
exclude = ('author',)
def queryset(self, request):
queryset = super(EntryAdmin, self).queryset(request)
return queryset.filter(author = request.user)
def save_model(self, request, obj, form, change):
obj.author = request.user
obj.save()
def has_change_permission(self, request, ojb=None):
if not obj:
return True # access to change list
return obj.author == request.user
has_delete_permission = has_change_permission
admin.site.register(Entry, EntryAdmin)
11. Finely grained permissions
class Entry(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey('auth.User')
class EntryAdmin(admin.ModelAdmin):
exclude = ('author',)
def queryset(self, request):
queryset = super(EntryAdmin, self).queryset(request)
return queryset.filter(author = request.user)
def save_model(self, request, obj, form, change):
obj.author = request.user
obj.save()
def has_change_permission(self, request, ojb=None):
if not obj:
return True # access to change list
return obj.author == request.user
has_delete_permission = has_change_permission
admin.site.register(Entry, EntryAdmin)
12. Finely grained permissions
class Entry(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey('auth.User')
class EntryAdmin(admin.ModelAdmin):
exclude = ('author',)
def queryset(self, request):
queryset = super(EntryAdmin, self).queryset(request)
return queryset.filter(author = request.user)
def save_model(self, request, obj, form, change):
obj.author = request.user
obj.save()
def has_change_permission(self, request, ojb=None):
if not obj:
return True # access to change list
return obj.author == request.user
has_delete_permission = has_change_permission
admin.site.register(Entry, EntryAdmin)
13. Objects can be views
A Django view is a function that takes a
request object and returns a response object
A Django view is a callable that takes a
request object and returns a response object
14. Objects can be views
A Django view is a function that takes a
request object and returns a response object
A Django view is a callable that takes a
request object and returns a response object
Just define __call__() on the class
16. django_openid
Next generation of my django-openid project
Taken a lot longer than I expected
Extensive use of class-based customisation
17. Ideas from django_openid
Everything should go through a render() method
Every template inherits from base_template
Every decision should be a method
Every form should come from a method
Every model interaction should live in a method
18. TemplateResponse
If you render_to_response, a subclass can’t
override and reuse your method while
modifying the context or template
Solution:
return TemplateResponse(
request, 'article.html', context
)
auth.py line 65
21. Subclassable decorators
ratelimitcache is a decorator that
implements rate limiting for a Django view
It’s a class disguised as a function, using
the __call__ method
You can subclass it to customise its
behaviour
http://github.com/simonw/ratelimitcache/tree/master
22. Extending the contract
Django view: take request / return response
Django middleware: take request / return response
Django application: take request / return response
Django urlconf: take request / return response
Django site: take request / return response