Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

The Best (and Worst) of Django

41,965 views

Published on

  • Thanks tartley! Your effort is appreciated! :)
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • @r1cka: Thanks, but in that case, which environment uses the 'local' settings file? If what you are saying is correct, then I would think the important difference between the two situations is that in the 'preferred' setup, there would be no 'local.py' settings.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Tartley: I think the anti-pattern he is trying to avoid is more about not checking in the local_settings.py file. In the 'bad' example, your dev environment would have a settings.py file with the try block to import the local_settings.py file and in production, the local_settings.py file would be missing. This is bad as all config should be checked into source control. The latter approach says to have the different settings files for each environment, have them all do 'from settings.base import *' at the top. Which environment you are using should be set with the 'DJANGO_SETTINGS_MODULE' environment variable (i.e. export DJANGO_SETTINGS_MODULE=site.settings.production' in your prod environment). This way all configs (local, qa, prod, etc) are shared with developers. At least, that is my take on it.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • In particular, I can't interpret slide 51 at all. How is the use of these prod.py, staging.py and local.py files different from the use which is strongly condoned in the previous slides? To what does 'settings.deploy' refer, in the last line? Anyone who can help enlighten me, many thanks.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • The notes are out of sync with the slides, which I found extremely distracting, and it gets worse the further you go, until the final 20 slides are missing notes altogether.

    I created a text doc to join up the slide content with the appropriate notes, but it's missing notes for the last 20 slides:

    http://pastebin.com/S8dpLL0W

    If anyone has the missing notes, or an idea for a better way to fix this, I'd love to hear it.

    Jonathan
    tartley aaaaaaaattttttt tartley.com
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

The Best (and Worst) of Django

  1. The best(and worst) of Django jacob@jacobian.org http://lanyrd.com/sgbwt/
  2. 1,215,616
  3. Public Enemy #1:Over-engineering.
  4. “Do the simplest thing that could possibly work.” — Simon Willison
  5. Small is beautiful
  6. The monolith monster The “application” is the whole site — no apps, no components, no modules. Makes heavy use of site-wide logic: middleware, context processors and custom plugin-like concepts.
  7. (I blame Rails.)
  8. “It’s a pretty simple site; about fifteen ortwenty thousand lines of code…”
  9. site_of_unspeakable_horrors!""  settings.py!""  urls.py!""  views.py#""  models.py
  10. models.py — 2,800 linesviews.py — 11,900 lines
  11. Big is bad;Small is beautiful.
  12. The Django mindset:Application: some bit of functionality.Site: several applications.Spin off new apps liberally.A suite of apps ready for use.
  13. There’s no such thing as “content.”
  14. XXX screenshot: “content” box
  15. Django is an un-CMS.
  16. The Django mindset:A great and powerful respect for data.Model the data correctly and the restof the site will just fall out of that.Denormalization is all well and good, butnever throw data away.
  17. The“everything-is-a…” anti-pattern
  18. “Everything needs a creation date.”
  19. class  BaseObject(models.Model):        creation_date  =  models.DateField()        …class  Animal(BaseObject):        …class  Vegetable(BaseObject):        …class  Mineral(BaseObject):        …
  20. Without a concrete base class:>>>  Animal.objects.all() SELECT  ...  FROM  animal;With a concrete base class:>>>  Animal.objects.all() SELECT  ...  FROM  "animal"  INNER  JOIN  "baseobject"   ON  ("animal"."baseobject_ptr_id"  =  "baseobject"."id")
  21. What you want:>>>  BaseObject.objects.all()[<Animal:  Frog>,  <Vegetable:  Carrot>,  <Mineral:  Gold>,  ...]What you get:>>>  BaseObject.objects.all()[<BaseObject:  1>,  <BaseObject:  2>,  <BaseObject:  3>,  ...]
  22. So maybe you try something like:def  get_some_of_everything():        qs  =  BaseObject.objects.all()        for  obj  in  qs:                for  cls  in  BaseObject.__subclasses__():                        try:                                obj  =  cls.objects.get(baseobject_ptr=obj)                                break                        except  cls.DoesNotExist:                                continue                yield  obj
  23. “Our site worked fine in development and testing, and was working wonderfully for the first few months.“But we just added a bunch more data, and now our homepage takes 27 seconds to load.”
  24. 1,800 queries
  25. “But… but… everything really does need a creation date!”So give everything a creation date.
  26. “Do the simplest thing that could possibly work.” — Simon Willison
  27. If you must get fancy: Abstract base classes don’t suffer from these performance problems. Denormalize into a UI-ordered auxiliary model. Non-relational databases work particular well here (I like SOLR).
  28. Those who do notunderstand PYTHONPATHare doomed to failure.
  29. >>>  import  sys>>>  sys.path[,  /Library/Python/2.6/site-­‐packages/pip-­‐0.8-­‐py2.6.egg,  /Library/Python/2.6/site-­‐packages/pdbpp-­‐0.7-­‐py2.6.egg,  /Library/Python/2.6/site-­‐packages/Pygments-­‐1.4-­‐py2.6.egg,  /Library/Python/2.6/site-­‐packages/wmctrl-­‐0.1-­‐py2.6.egg,  /Library/Python/2.6/site-­‐packages/pyrepl-­‐0.8.2-­‐py2.6.egg,  ...  /System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6,  ...  /Users/jacob/.local/lib/python2.6/site-­‐packages,  /Library/Python/2.6/site-­‐packages,  ...]>>>  import  re>>>  re<module  re  from  /System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/re.pyc>
  30. ~/Projects/djdash!""  __init__.py!""  dashboard$      !""  __init__.py$      !""  admin.py$      !""  models.py$      #""  views.py#""  manage.py (https://github.com/jacobian/django-dev-dashboard)
  31. $  ./manage.py  shell>>>  import  dashboard.models>>>  dashboard.models<module  dashboard.models    from  /Users/jacob/Projects/djdash/dashboard/models.pyc>>>>  import  djdash.dashboard.models>>>  djdash.dashboard.models<module  djdash.dashboard.models  from  /Users/jacob/Projects/djdash/../djdash/dashboard/models.pyc>>>>  djdash.dashboard.models.Metric  is  dashboard.models.MetricFalse
  32. You might have an import issue if…“Hey, many-to-many relations don’t show up in the admin.”“What’s up with these import errors when I deploy under mod_wsgi?”“Grrr… assertRaises doesn’t work!”
  33. Django is wrong! (I’m sorry.)
  34. Fixing import madness1. Use non-project-relative imports (import  app.models, not import project.app.models).2. Use relative imports (from  .  import  x) where possible (see http://bit.ly/pep328).3. Stop using manage.py.
  35. Delete manage.py?$  django-­‐admin.py  shell  -­‐-­‐pythonpath=`pwd`  -­‐-­‐settings=settings.local>>>  import  dashboard.models>>>  import  djdash.dashboard.modelsTraceback  (most  recent  call  last)...ImportError:  No  module  named  djdash.dashboard.models
  36. For virtualenv users:$  add2virtualenv  ~/Projects/djdash$  echo  "export  DJANGO_SETTINGS_MODULE=settings.local"      >>  $VIRTUAL_ENV/bin/postactivate$  echo  "unset  DJANGO_SETTINGS_MODULE"      >>  $VIRTUAL_ENV/bin/postdeactivate$  django-­‐admin.py  shell
  37. Keep(your settings) simple.
  38. Don’t do this …INSTALLED_APPS  +=  [p  for  p  in  os.listdir(BASE)                                      if  os.path.isdir(p)]
  39. … or this …urlpatterns  =  patterns(,  ...)for  app  in  settings.INSTALLED_APPS:        if  not  app.startswith(django):                p  =  url(^%s/  %  app,  include(%s.urls)  %  app)                urlpatterns  +=  patterns(,  p)
  40. … or this.MIDDLEWARE_CLASSES  =  [...]def  callback(arg,  dirname,  fnames):        if  middleware.py  in  fnames:                m  =  %s.middleware  %  os.path.split(dirname)[-­‐1])                MIDDLEWARE_CLASSES.append(m)os.path.walk(BASE,  callback,  None)
  41. Python’s design is predicated on the proposition that code is more often read than written.
  42. INSTALLED_APPS  =  (        django.contrib.auth,        django.contrib.contenttypes,        django.contrib.sessions, MIDDLEWARE_CLASSES  =  (        django.contrib.sites,        django.middleware.common.CommonMiddleware,        django.contrib.messages,        django.contrib.sessions.middleware.SessionMiddleware,        django.contrib.staticfiles,        django.middleware.csrf.CsrfViewMiddleware,        django.contrib.admin,        django.contrib.auth.middleware.AuthenticationMiddleware,        django.contrib.messages.middleware.MessageMiddleware,        django.contrib.flatpages,          django.contrib.flatpages.middleware.FlatpageFallbackMiddleware,        django_extensions,          debug_toolbar.middleware.DebugToolbarMiddleware,        debug_toolbar,   )        south,   urlpatterns  =  patterns(,        rs.users,          url(r^admin/,  include(admin.site.urls)),        rs.orgs,          url(r^signup/,  include(rs.signup.urls)),        rs.signup,        url(r^org/,  include(rs.orgs.urls)),          rs.clients,        url(r^clients/,  include(rs.clients.urls)),        rs.timezones,        url(r^caregivers/,  include(rs.caregivers.urls)),        rs.caregivers,        url(r^account/,  include(rs.users.urls)),        rs.dashboard,        url(r^dashboard/,  include(rs.dashboard.urls)),        rs.scripts,        url(r^reminders/,  include(rs.reminders.urls)),        rs.reminders,        url(r^calls/,  include(rs.calls.urls)),        rs.billing,        url(r^scripts/,  include(rs.scripts.urls)),        rs.calls,        url(r^contact/,  include(contact_form.urls)),          chunks,        url(r^login/,  django.contrib.auth.views.login,  {},  login),          contact_form,        url(r^logout/$,  django.contrib.auth.views.logout,  {},  logout,),)        url(r^changepassword/$,  django.contrib.auth.views.password_change) )
  43. Multiple settings files
  44. The localsettings anti-patternAt the bottom of your settings file:try:        from  local_settings  import  *except  ImportError:        pass
  45. “It’s simple: just create a local_settings.py, throw overridden settings in there, and then never check the file into source control.”
  46. “It’s simple: just create a local_settings.py, throw overridden settings in there, and then never check the file into source control.”
  47. Handling multiple settings files1. Don’t. Why is your staging environment different from production?2. Use DJANGO_SETTINGS_MODULE.
  48. The one true waysettings #  base.py!""  __init__.py INSTALLED_APPS  =  [...]!""  base.py!""  staging.py #  local.py!""  production.py from  settings.base  import  *#""  local.py INSTALLED_APPS  +=  [debug_toolbar]$  django-­‐admin.py  shell  -­‐-­‐settings=settings.local#  deploy.wsgios.environ[DJANGO_SETTINGS_MODULE]  =  settings.deploy
  49. “Do the simplest thing that could possibly work.” — Simon Willison
  50. Thank you!jacob@jacobian.org http://lanyrd.com/sgbwt/

×