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

Django - Don't Do This!

Django - Don't Do This!

Presentation created for the January 2012 Django Toronto meetup. It outlines some common mistakes that developers make as they become more familiar with the framework.

0ba94480bf4840a6957fa83303be919e?s=128

Ash Christopher

April 07, 2012
Tweet

Transcript

  1. ash.christopher@gmail.com @ashchristopher DONT DO THIS! Friday, 20 January, 12

  2. Who am I? Friday, 20 January, 12

  3. Systems Analyst Friday, 20 January, 12

  4. Senior Developer and Team Lead Friday, 20 January, 12

  5. Senior Developer Friday, 20 January, 12

  6. What is this talk about? Friday, 20 January, 12

  7. Common Mistakes Friday, 20 January, 12

  8. Common mistakes as a result of gaining more experience with

    Django Friday, 20 January, 12
  9. Common mistakes as a result of gaining more experience with

    Django (in no particular order) Friday, 20 January, 12
  10. Friday, 20 January, 12

  11. Don’t be *this* guy. Friday, 20 January, 12

  12. Defining Models without indexes Friday, 20 January, 12

  13. DON’T forget your indexes Friday, 20 January, 12

  14. Models Friday, 20 January, 12

  15. Models They define your data. Friday, 20 January, 12

  16. Models They don’t define behavior. Friday, 20 January, 12

  17. Models They can’t read your mind. Friday, 20 January, 12

  18. Even seasoned Django developers forget about indexes Friday, 20 January,

    12
  19. Remember this? Friday, 20 January, 12

  20. Remember this? Friday, 20 January, 12

  21. DO add indexes to your Models. Friday, 20 January, 12

  22. Over-Indexing Friday, 20 January, 12

  23. DON’T index everything Friday, 20 January, 12

  24. What are indexes? Friday, 20 January, 12

  25. What are indexes? They are pointers to your data Friday,

    20 January, 12
  26. What are indexes? They are pointers to your data ...

    in memory Friday, 20 January, 12
  27. What are indexes? They are pointers to your data ...

    in memory ... or on disk!!! Friday, 20 January, 12
  28. They slow down inserts, updates and deletes. Friday, 20 January,

    12
  29. As a general rule, you want an index on anything

    you will use to limit results Friday, 20 January, 12
  30. As a general rule, you want an index on anything

    you will use to limit results ... but generalizations break down quickly. Friday, 20 January, 12
  31. You need to assess your specific needs. Friday, 20 January,

    12
  32. DO analyze your QuerySets to determine where indexes are needed.

    Friday, 20 January, 12
  33. Silly Shortcuts Friday, 20 January, 12

  34. DON’T use locals() to populate your context Friday, 20 January,

    12
  35. I PITY THE FOOL... WHO HAS TO DEBUG YOUR TEMPLATE

    Friday, 20 January, 12
  36. It’s not clever. Friday, 20 January, 12

  37. It’s just lazy. Friday, 20 January, 12

  38. Every time you use locals() to populate your context, God

    kills a kitten. Friday, 20 January, 12
  39. def my_view(request): ! users = User.objects.filter(active=True) ! num_users = len(users)

    ! message = None ! if num_users > 3: ! ! message = "Three's a crowd." context = locals() ! return render_to_response( '/path/to/template.html', context) Friday, 20 January, 12
  40. def my_view(request): ! users = User.objects.filter(active=True) ! num_users = len(users)

    ! message = None ! if num_users > 3: ! ! message = "Three's a crowd." context = locals() ! return render_to_response( '/path/to/template.html', context) Thumbs down! Friday, 20 January, 12
  41. context = {'context': <Recursion on dict with id=175092300>, 'ipdb': <module

    'ipdb' from '/home/vagrant/.virtualenvs/wave/lib/python2.6/site-packages/ipdb/ __init__.pyc'>, 'message': "Three's a crowd.", 'num_users': 9, 'pprint': <function pprint at 0x8a88f44>, 'request': <WSGIRequest GET:<QueryDict: {}>, POST:<QueryDict: {}>, COOKIES:{'__utma': '111872281.606618788.1318529036.1326747382.1326750603.150', '__utmc': '111872281', '__utmz': '111872281.1318529036.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)', 'csrftoken': '********************************', 'sessionid': '********************************'}, META:{'CELERY_LOADER': 'djcelery.loaders.DjangoLoader', 'CONTENT_LENGTH': '', 'CONTENT_TYPE': 'text/plain', 'CSRF_COOKIE': '********************************', 'DJANGO_SETTINGS_MODULE': 'waveaccounting.settings', 'GATEWAY_INTERFACE': 'CGI/1.1', 'HOME': '/home/vagrant', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'HTTP_ACCEPT_CHARSET': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate', 'HTTP_ACCEPT_LANGUAGE': 'en-us,en;q=0.5', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_COOKIE': 'csrftoken=********************************; __utma=111872281.606618788.1318529036.1326747382.1326750603.150; __utmz=111872281.1318529036.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); sessionid=********************************; __utmc=111872281', 'HTTP_HOST': 'localhost:8000', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:9.0.1) Gecko/20100101 Firefox/9.0.1', 'LANG': 'en_US.UTF-8', 'LESSCLOSE': '/usr/bin/lesspipe %s %s', 'LESSOPEN': '| /usr/bin/lesspipe %s', 'LOGNAME': 'vagrant', Friday, 20 January, 12
  42. 'LS_COLORS': 'rs=0:di=01;34:ln=01;36:hl=44;37:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43 :ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;3 1:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rp m=01;31:*.jar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*. 7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;3 5:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mo v=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vo b=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.fl v=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.a

    ac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00; 36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:', 'MAIL': '/var/mail/vagrant', 'OLDPWD': '/home/vagrant', 'PATH': '/home/vagrant/.virtualenvs/wave/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/opt/ ruby/bin/', 'PATH_INFO': u'/en/settings/business/1/', 'PIP_RESPECT_VIRTUALENV': 'true', 'PS1': '(wave)\\[\\e]0;\\u@\\h: \\w\\a\\]${debian_chroot:+($debian_chroot)}\\u@\\h:\\w\\$ ', 'PWD': '/home/vagrant/Projects/wa/waveaccounting', 'QUERY_STRING': '', 'REMOTE_ADDR': '10.0.2.2', 'REMOTE_HOST': '', 'REQUEST_METHOD': 'GET', 'RUN_MAIN': 'true', 'SCRIPT_NAME': u'', 'SERVER_NAME': 'lucid32', 'SERVER_PORT': '8000', 'SERVER_PROTOCOL': 'HTTP/1.1', Friday, 20 January, 12
  43. 'SERVER_SOFTWARE': 'WSGIServer/0.1 Python/2.6.5', 'SHELL': '/bin/bash', 'SHLVL': '1', 'SSH_CLIENT': '10.0.2.2 50056

    22', 'SSH_CONNECTION': '10.0.2.2 50056 10.0.2.15 22', 'SSH_TTY': '/dev/pts/2', 'TERM': 'xterm-256color', 'TZ': 'America/Toronto', 'USER': 'vagrant', 'VIRTUALENVWRAPPER_HOOK_DIR': '/home/vagrant/.virtualenvs', 'VIRTUALENVWRAPPER_LOG_DIR': '/home/vagrant/.virtualenvs', 'VIRTUAL_ENV': '/home/vagrant/.virtualenvs/wave', 'WORKON_HOME': '/home/vagrant/.virtualenvs', '_': '/home/vagrant/.virtualenvs/wave/bin/python', 'wsgi.errors': <open file '<stderr>', mode 'w' at 0xb77600d0>, 'wsgi.file_wrapper': <class 'django.core.servers.basehttp.FileWrapper'>, 'wsgi.input': <socket._fileobject object at 0x9c602ec>, 'wsgi.multiprocess': False, 'wsgi.multithread': True, 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.version': (1, 0)}>, 'users': [<User: ash@waveaccounting.com>, <User: ash+2@waveaccounting.com>, <User: ash+3@waveaccounting.com>, <User: ehabjan4@wavetest.com>, <User: foo@example.com>, <User: ash@example.com>, <User: ash@wavereceipts.com>, <User: >, <User: james+compound@waveaccounting.com>]} Friday, 20 January, 12
  44. Weird stuff happens. Friday, 20 January, 12

  45. Did you notice? 'context': <Recursion on dict with id=175092300> Friday,

    20 January, 12
  46. Did you notice? 'context': <Recursion on dict with id=175092300> context

    Friday, 20 January, 12
  47. Did you notice? 'context': <Recursion on dict with id=175092300> context

    [‘context’] Friday, 20 January, 12
  48. Did you notice? 'context': <Recursion on dict with id=175092300> context

    [‘context’] [‘context’] Friday, 20 January, 12
  49. Did you notice? 'context': <Recursion on dict with id=175092300> context

    [‘context’] [‘context’] [‘context’] Friday, 20 January, 12
  50. Did you notice? 'context': <Recursion on dict with id=175092300> context

    [‘context’] [‘context’] [‘context’] ... Friday, 20 January, 12
  51. Please... do it this way! def my_view(request): ! users =

    User.objects.filter(active=True) ! num_users = len(users) ! message = None ! if num_users > 3: ! ! message = "Three's a crowd." ! context = { ! ! 'message' : message, ! } ! return render_to_response( '/path/to/template.html', context) Friday, 20 January, 12
  52. DO define your context explicitly Friday, 20 January, 12

  53. Replacing the .all() method on QuerySets Friday, 20 January, 12

  54. DON’T replace get_query_set() with something unexpected. Friday, 20 January, 12

  55. The majority of developers and apps expect objects.all() to return

    ALL objects. Friday, 20 January, 12
  56. That’s why it’s called .all() Friday, 20 January, 12

  57. EXAMPLE class CustomQuerySet(QuerySet): def active(self): return self.filter(is_active=True) class MyManager(models.Manager): def

    get_query_set(self): return CustomQuerySet(self.model) Friday, 20 January, 12
  58. To get the active users: MyModel.objects.active() Friday, 20 January, 12

  59. What about in the admin? Friday, 20 January, 12

  60. What about in the admin? class CustomModelAdmin(admin.ModelAdmin): def queryset(self, request):

    “”” Only superusers can access all objects in the admin. “”” qs = super(CustomModelAdmin, self).queryset(request) if not request.user.is_superuser: qs = qs.active() return qs Friday, 20 January, 12
  61. DO explicitly define your QuerySets Friday, 20 January, 12

  62. Creating new ways to do the same things. Friday, 20

    January, 12
  63. DON’T invent new Django conventions. Friday, 20 January, 12

  64. Even the best Django developers make this mistake Friday, 20

    January, 12
  65. Even the best Django developers make this mistake Friday, 20

    January, 12
  66. are you sure that's a good idea? are you sure

    that's a good idea? are you sure that's a good idea? are you sure that's a good idea? Even the best Django developers make this mistake Friday, 20 January, 12
  67. i think its a really good usage pattern i think

    its a really good usage pattern i think its a really good usage pattern i think its a really good usage pattern Even the best Django developers make this mistake Friday, 20 January, 12
  68. your POST data doesn't necessarily exist. your POST data doesn't

    necessarily exist. your POST data doesn't necessarily exist. your POST data doesn't necessarily exist. Even the best Django developers make this mistake Friday, 20 January, 12
  69. oh... crap oh... crap oh... crap oh... crap Even the

    best Django developers make this mistake Friday, 20 January, 12
  70. pwned you! pwned you! pwned you! pwned you! Even the

    best Django developers make this mistake Friday, 20 January, 12
  71. ... ... ... ... Even the best Django developers make

    this mistake Friday, 20 January, 12
  72. FOLLOW THE PATTERN, BITCHES! FOLLOW THE PATTERN, BITCHES! FOLLOW THE

    PATTERN, BITCHES! FOLLOW THE PATTERN, BITCHES! Even the best Django developers make this mistake Friday, 20 January, 12
  73. DO follow the conventions outlined by the Django community Friday,

    20 January, 12
  74. They have more experience than you do. Friday, 20 January,

    12
  75. They have built more projects than you have. Friday, 20

    January, 12
  76. They know better than you do. Friday, 20 January, 12

  77. Tell me I’m wrong Friday, 20 January, 12

  78. Tell me I’m wrong ash.christopher@gmail.com @ashchristopher Friday, 20 January, 12