10. {% if %} tags SUCK
• Every time you {% endifnotequal %},
God kicks the Django Pony
11. {% if %} tags SUCK
• Every time you {% endifnotequal %},
God kicks the Django Pony
12. http://docs.djangoproject.com/en/dev/misc/design-philosophies/
“ Don’t invent a programming language
The template system intentionally doesn’t allow the
following:
•
Assignment to variables
•
Advanced logic
The goal is not to invent a programming language.
The goal is to offer just enough programming-esque
functionality, such as branching and looping, that is
”
essential for making presentation-related decisions.
16. http://www.djangosnippets.org/snippets/1350/
Chris Beaven (aka SmileyChris)
'''
A smarter {% if %} tag for django templates.
While retaining current Django functionality, it also
handles equality, greater than and less than operators.
Some common case examples::
{% if articles|length >= 5 %}...{% endif %}
{% if quot;ifnotequal tagquot; != quot;beautifulquot; %}...{% endif %}
'''
{% load smartif %} replaces the Django {% if %} tag
17. http://www.djangosnippets.org/snippets/1350/
Chris Beaven (aka SmileyChris)
'''
A smarter {% if %} tag for django templates.
While retaining current Django functionality, it also
handles equality, greater than and less than operators.
Some common case examples::
{% if articles|length >= 5 %}...{% endif %}
{% if quot;ifnotequal tagquot; != quot;beautifulquot; %}...{% endif %}
'''
{% load smartif %} replaces the Django {% if %} tag
19. • 2003: “template authors shouldn’t be able to
break the site”
• 2008: “I can't think of a single time this
feature has helped me, and plenty of
examples of times that it has tripped me up.”
• Silent {{ foo.bar }} is OK, silent tags are evil
• django-developers: http://bit.ly/silentfail
29. 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
30. newforms-admin
• De-coupled admin from the rest of Django
• A new approach to customisation
• Powerful subclassing pattern
31. Fine 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, axj=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)
32. Fine 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, obj=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)
33. Fine 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, obj=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)
34. Fine 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, obj=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)
35. Fine 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, obj=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)
36. Fine 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, obj=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)
37. 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
38. 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
41. from django.http import HttpResponse
class RestView(object):
def __call__(self, request, *args, **kwargs):
if not hasattr(self, method):
return self.method_not_allowed(method)
return getattr(self, method)(request, *args, **kwargs)
def method_not_allowed(self, method):
response = HttpResponse('Not allowed: %s' % method)
response.status_code = 405
return response
42. django_openid
• Next generation of my django-openid project
• Taken a lot longer than I expected
• Extensive use of class-based customisation
GitHub: http://github.com/simonw/django-openid
44. Suggestions from
django_openid
• Every decision should use a method
• Every form should come from a method
• Every model interaction should live in a method
• Everything should go through a render() method
48. TemplateResponse
class MyCustom(BaseView):
def index(self):
response = super(MyCustom, self).index()
# response is a TemplateResponse
response.context['counter'] += 1
response.template = 'some/other/template.html'
return response
# Two classes
SimpleTemplateResponse(template, context)
TemplateResponse(request, template, context)
49. TemplateResponse
• Subclasses can re-use your logic and
extend or modify your context
• So can middleware and unit tests
• GZip Middleware writes to
response.content, needs work arounds
• Should HttpResponse be immutable?
54. Django Core
• Excellent testing culture
• Dubious “find... | grep... | xargs wc -l”:
• 74k lines of code
• 45k lines of tests
• “No new code without tests”
• Coverage = 54.4%, increasing over time
55. Django community?
• ... not so good
• even though django.test.client is great
• Many reusable apps lack tests
• need more psychology!
56. nose is more fun
• nosetests --with-coverage
• (coming to a SoC project near you)
• nosetests --pdb
• nosetests --pdb-failures
57. Test views directly
• Hooking up views to a URLconf just so
you can test them is fiddly
• ... and sucks for reusable apps
• A view function takes a request and
returns a response
60. A web-based interface?
• Testing would be more fun with pretty graphs
• ... and animated progress meters
• ... and a “test now” button
• ... maybe the Django pony could smile at
you when your tests pass
• Cheap continuous integration: run tests every
time a file changes on disk?
62. Why did PHP magic_quotes suck?
• They made it impossible to write reusable code
• What if your code expects them to be on, but a
library expects them to be off?
• Check get_magic_quotes_gpc() and
unescape... but what if some other library has
done that first?
63. settings.py problems
• Middleware applies globally, even to
those applications that don’t want it
• Anything fixed in settings.py I inevitably
want to dynamically alter at runtime
• TEMPLATE_DIRS for mobile sites
• DB connections
• How about per-application settings?
64. Grr
>>> from django import db
Traceback (most recent call last):
File quot;<stdin>quot;, line 1, in <module>
...
ImportError: Settings cannot be imported,
because environment variable
DJANGO_SETTINGS_MODULE is undefined.
67. The Django Contract
• A view is a callable that takes a request object and
returns a response object
68. The Django Contract
• A view is a callable that takes a request object and
returns a response object
• Primary URLconf: selects a view based on regular
expressions
69. The Django Contract
• A view is a callable that takes a request object and
returns a response object
• Primary URLconf: selects a view based on regular
expressions
• Application: sometimes has its own URLconf include()d
in to the primary
70. The Django Contract
• A view is a callable that takes a request object and
returns a response object
• Primary URLconf: selects a view based on regular
expressions
• Application: sometimes has its own URLconf include()d
in to the primary
• Middleware: a sequence of globally applied classes
process_request/process_response/process_exception
71. The Django Contract
• A view is a callable that takes a request object and
returns a response object
• Primary URLconf: selects a view based on regular
expressions
• Application: sometimes has its own URLconf include()d
in to the primary
• Middleware: a sequence of globally applied classes
process_request/process_response/process_exception
• Site: a collection of applications + settings.py + urls.py
72. Extended Contract
• A view is a callable that
takes a request and returns a response
• URLconf: a callable that
takes a request and returns a response
• Application: a callable that
takes a request and returns a response
• Middleware: a callable that
takes a request and returns a response
• Site: a callable that takes a request and returns a response