Django Heresies

Loading...

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

4 comments

Comments 1 - 4 of 4 previous next Post a comment

  • + printf printf 5 months ago
    Django <3
  • + guest1fada7e guest1fada7e 6 months ago
    LOVE how u are managing settings.py. I’ve already adopted it. Thanks! -Alan Viars
  • + guest57cc7dc guest57cc7dc 6 months ago
    great slides, thanks!
  • + guest42bc7c guest42bc7c 6 months ago
    Slide 74 made me go 'yay! yay! yay!' :) Cracking stuff :) :) :)
Post a comment
Embed Video
Edit your comment Cancel

25 Favorites

Django Heresies - Presentation Transcript

  1. Django Heresies Simon Willison @simonw EuroDjangoCon http://simonwillison.net/ 4th May 2009
  2. http://www.flickr.com/photos/ianlloyd/264753656/
  3. DJANGO IS AWESOME
  4. Selling bacon on the internet
  5. Pulitzer prize winning journalism
  6. Saving children’s lives in Kenya
  7. Heresy “An opinion at variance with the orthodox or accepted doctrine”
  8. Templates
  9. {% if %} tags SUCK
  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.
  13. {% if photo.width > 390 %} ... {% endif %}
  14. 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 \"ifnotequal tag\" != \"beautiful\" %}...{% endif %} ''' {% load smartif %} replaces the Django {% if %} tag
  15. 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 \"ifnotequal tag\" != \"beautiful\" %}...{% endif %} ''' {% load smartif %} replaces the Django {% if %} tag
  16. Silencing errors
  17. • 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
  18. Project layout http://www.flickr.com/photos/macrorain/2789698166/
  19. Relocatable TEMPLATE_DIRS = ( # Don't forget to use absolute paths, # not relative paths. ) import os OUR_ROOT = os.path.realpath( os.path.dirname(__file__) ) ... TEMPLATE_DIRS = os.path.join(OUR_ROOT, 'templates')
  20. Relocatable TEMPLATE_DIRS = ( # Don't forget to use absolute paths, # not relative paths. ) import os OUR_ROOT = os.path.realpath( os.path.dirname(__file__) ) ... TEMPLATE_DIRS = os.path.join(OUR_ROOT, 'templates')
  21. local_settings.py • svn:ignore local_settings.py ? • Can’t easily test your production settings • Configuration isn’t in source control!
  22. Environments zoo/configs/common_settings.py zoo/configs/alpha/app.wsgi zoo/configs/alpha/manage.py zoo/configs/alpha/settings.py zoo/configs/testing/app.wsgi zoo/configs/testing/manage.py zoo/configs/testing/settings.py
  23. zoo/configs/alpha/settings.py from zoo.configs.common_settings import * DEBUG = True TEMPLATE_DEBUG = DEBUG # Database settings DATABASE_NAME = 'zoo_alpha' DATABASE_USER = 'zoo_alpha'
  24. Reusable code http://www.flickr.com/photos/ste3ve/521083510/
  25. Generic views def object_detail(request, queryset, object_id=None, slug=None, slug_field='slug', template_name=None, template_name_field=None, template_loader=loader, extra_context=None, context_processors=None, template_object_name='object', mimetype=None ):
  26. 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
  27. newforms-admin • De-coupled admin from the rest of Django • A new approach to customisation • Powerful subclassing pattern
  28. 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)
  29. 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)
  30. 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)
  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, 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)
  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. 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
  35. 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
  36. Example: restview.py Django Snippets: http://bit.ly/restview
  37. class ArticleView(RestView): def GET(request, article_id): return render(\"article.html\", { 'article': get_object_or_404( Article, pk = article_id), }) def POST(request, article_id): form = ... return HttpResponseRedirect(request.path)
  38. 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
  39. 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
  40. consumer.py Consumer LoginConsumer CookieConsumer SessionConsumer auth.py AuthConsumer registration.py RegistrationConsumer
  41. 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
  42. render() class Consumer(object): ... base_template = 'django_openid/base.html' ... def render(self, request, template, context=None): context = context or {} context['base_template'] = self.base_template return TemplateResponse( request, template, context ) ...
  43. render() class Consumer(object): ... base_template = 'django_openid/base.html' ... def render(self, request, template, context=None): context = context or {} context['base_template'] = self.base_template return TemplateResponse( request, template, context ) ...
  44. render() class Consumer(object): ... base_template = 'django_openid/base.html' ... def render(self, request, template, context=None): context = context or {} context['base_template'] = self.base_template return TemplateResponse( request, template, context ) ...
  45. 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)
  46. 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?
  47. Ticket #6735, scheduled for Django 1.2
  48. Storing state on self in a class-based generic view is not thread safe
  49. Storing state on self in a class-based generic view is not thread safe
  50. Testing
  51. 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
  52. Django community? • ... not so good • even though django.test.client is great • Many reusable apps lack tests • need more psychology!
  53. nose is more fun • nosetests --with-coverage • (coming to a SoC project near you) • nosetests --pdb • nosetests --pdb-failures
  54. 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
  55. RequestFactory rf = RequestFactory() get_request = rf.get('/hello/') post_request = rf.post('/submit/', { 'foo': 'bar' }) delete_request = rf.delete('/item/1/') http://bit.ly/requestfactory
  56. The HttpRequest constructor isn’t doing anything useful at the moment...
  57. 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?
  58. settings.py is the root of all evil
  59. 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?
  60. 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?
  61. Grr >>> from django import db Traceback (most recent call last): File \"<stdin>\", line 1, in <module> ... ImportError: Settings cannot be imported, because environment variable DJANGO_SETTINGS_MODULE is undefined.
  62. http://www.flickr.com/photos/raceytay/2977241805/ Turtles all the way down
  63. “an infinite regression belief about cosmology and the nature of the universe”
  64. The Django Contract • A view is a callable that takes a request object and returns a response object
  65. 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
  66. 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
  67. 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
  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 • 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
  69. 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
  70. http://www.flickr.com/photos/enil/2440955556/ Let’s call them “turtles”
  71. Three species of turtle • Django request / response • WSGI • HTTP • What if they were interchangeable?
  72. Three species of turtle • Django request / response • WSGI • Christopher Cahoon, SoC • django_view_from_wsgi_app() http://bit.ly/djwsgi • django_view_dec_from_wsgi_middleware() • HTTP • paste.proxy
  73. http://www.flickr.com/photos/remiprev/336851630/ Micro frameworks
  74. http://github.com/ juno 782 lines breily/juno http://github.com/ newf 144 lines JaredKuolt/newf http://github.com/ mnml 205 lines bradleywright/mnml http://github.com/ itty 406 lines toastdriven/itty
  75. djng http://github.com/simonw/djng (First commit at 5.47am this morning)
  76. A micro framework that depends on a macro framework
  77. class Router(object):     \"\"\" Convenient wrapper around Django's urlresolvers, allowing them to be used from normal application code.   from django.conf.urls.defaults import url router = Router( url('^foo/$', lambda r: HttpResponse('foo'), name='foo'), url('^bar/$', lambda r: HttpResponse('bar'), name='bar') ) request = RequestFactory().get('/bar/') print router(request) \"\"\"     def __init__(self, *urlpairs):         self.urlpatterns = patterns('', *urlpairs)         self.resolver = urlresolvers.RegexURLResolver(r'^/', self)          def handle(self, request):         path = request.path_info         callback, callback_args, callback_kwargs = self.resolver.resolve(path)         return callback(request, *callback_args, **callback_kwargs)          def __call__(self, request):         return self.handle(request)
  78. Re-imagining the core Django APIs, minus urls.py and settings.py
  79. http://github.com/simonw/djangopeople.net
  80. http://www.flickr.com/photos/morgantj/2639793944/ Thank you

+ simonsimon, 6 months ago

custom

7954 views, 25 favs, 9 embeds more stats

A talk given at EuroDjangoCon on the 4th May 2009: more

More info about this document

© All Rights Reserved

Go to text version

  • Total Views 7954
    • 7493 on SlideShare
    • 461 from embeds
  • Comments 4
  • Favorites 25
  • Downloads 214
Most viewed embeds
  • 436 views on http://simonwillison.net
  • 8 views on http://www.dailydjango.com
  • 5 views on http://www.mefeedia.com
  • 4 views on http://www.soup.io
  • 4 views on http://japhy.soup.io

more

All embeds
  • 436 views on http://simonwillison.net
  • 8 views on http://www.dailydjango.com
  • 5 views on http://www.mefeedia.com
  • 4 views on http://www.soup.io
  • 4 views on http://japhy.soup.io
  • 1 views on http://www.indypendance.com
  • 1 views on http://tlog.4zal.net
  • 1 views on http://python.rk.edu.pl
  • 1 views on http://www.rkblog.rk.edu.pl

less

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate. If needed, use the feedback form to let us know more details.

Cancel
File a copyright complaint
Having problems? Go to our helpdesk?

Categories