SlideShare a Scribd company logo
The best
(and worst)
 of Django
 jacob@jacobian.org
   http://lanyrd.com/sgbwt/
The Best (and Worst) of Django
1,215,616
Public Enemy #1:
Over-engineering.
“Do the simplest
 thing that could
 possibly work.”
         — Simon Willison
Small is beautiful
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.
(I blame Rails.)
“It’s a pretty simple site; about fifteen or
twenty thousand lines of code…”
site_of_unspeakable_horrors
!""  settings.py
!""  urls.py
!""  views.py
#""  models.py
models.py — 2,800 lines

views.py — 11,900 lines
The Best (and Worst) of Django
Big is bad;
Small is beautiful.
The Django mindset:

Application: some bit of functionality.

Site: several applications.

Spin off new apps liberally.

A suite of apps ready for use.
The Best (and Worst) of Django
There’s no such thing
   as “content.”
XXX screenshot:
 “content” box
Django is an
  un-CMS.
The Django mindset:

A great and powerful respect for data.

Model the data correctly and the rest
of the site will just fall out of that.

Denormalization is all well and good, but
never throw data away.
The
“everything-is-a…”
   anti-pattern
“Everything needs a creation date.”
class  BaseObject(models.Model):
        creation_date  =  models.DateField()
        …

class  Animal(BaseObject):
        …

class  Vegetable(BaseObject):
        …

class  Mineral(BaseObject):
        …
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")
What you want:
>>>  BaseObject.objects.all()
[<Animal:  Frog>,  <Vegetable:  Carrot>,  <Mineral:  Gold>,  ...]



What you get:
>>>  BaseObject.objects.all()
[<BaseObject:  1>,  <BaseObject:  2>,  <BaseObject:  3>,  ...]
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
“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.”
1,800 queries
“But… but… everything really does need
 a creation date!”

So give everything a creation date.
“Do the simplest
 thing that could
 possibly work.”
         — Simon Willison
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).
Those who do not
understand PYTHONPATH
are doomed to failure.
>>>  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'>
~/Projects/djdash
!""  __init__.py
!""  dashboard
$      !""  __init__.py
$      !""  admin.py
$      !""  models.py
$      #""  views.py
#""  manage.py



            (https://github.com/jacobian/django-dev-dashboard)
$  ./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.Metric
False
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!”
Django is wrong!
     (I’m sorry.)
Fixing import madness

1. 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.
Delete manage.py?

$  django-­‐admin.py  shell  -­‐-­‐pythonpath=`pwd`  -­‐-­‐settings=settings.local

>>>  import  dashboard.models

>>>  import  djdash.dashboard.models
Traceback  (most  recent  call  last)
...
ImportError:  No  module  named  djdash.dashboard.models
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
Keep
(your settings)
    simple.
Don’t do this …


INSTALLED_APPS  +=  [p  for  p  in  os.listdir(BASE)
                                      if  os.path.isdir(p)]
… 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)
… 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)
Python’s design is predicated
   on the proposition that
    code is more often
    read than written.
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')
                      )
Multiple settings files
The localsettings
         anti-pattern

At the bottom of your settings file:

try:
        from  local_settings  import  *
except  ImportError:
        pass
“It’s simple: just create a
 local_settings.py, throw overridden
 settings in there, and then never check
 the file into source control.”
“It’s simple: just create a
 local_settings.py, throw overridden
 settings in there, and then never check
 the file into source control.”
Handling multiple
    settings files

1. Don’t.
  Why is your staging environment
  different from production?
2. Use DJANGO_SETTINGS_MODULE.
The one true way
settings                    #  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.wsgi
os.environ['DJANGO_SETTINGS_MODULE']  =  'settings.deploy'
“Do the simplest
 thing that could
 possibly work.”
         — Simon Willison
Thank you!


jacob@jacobian.org
  http://lanyrd.com/sgbwt/

More Related Content

What's hot

イミュータブルデータモデルの極意
イミュータブルデータモデルの極意イミュータブルデータモデルの極意
イミュータブルデータモデルの極意
Yoshitaka Kawashima
 
負荷試験ツールlocustを使おう
負荷試験ツールlocustを使おう負荷試験ツールlocustを使おう
負荷試験ツールlocustを使おう
iRidge, Inc.
 
概念モデルって難しいですよね
概念モデルって難しいですよね概念モデルって難しいですよね
概念モデルって難しいですよね
Takuya Kawabe
 
MongoDBが遅いときの切り分け方法
MongoDBが遅いときの切り分け方法MongoDBが遅いときの切り分け方法
MongoDBが遅いときの切り分け方法
Tetsutaro Watanabe
 
MongoDB very basic (Japanese) / MongoDB基礎の基礎
MongoDB very basic (Japanese) / MongoDB基礎の基礎MongoDB very basic (Japanese) / MongoDB基礎の基礎
MongoDB very basic (Japanese) / MongoDB基礎の基礎
Naruhiko Ogasawara
 
データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3
データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3 データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3
データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3
Hiroshi Ito
 
オブジェクト指向できていますか?
オブジェクト指向できていますか?オブジェクト指向できていますか?
オブジェクト指向できていますか?
Moriharu Ohzu
 
そろそろ知っておきたい!!コンテナ技術と Dockerのキホン
そろそろ知っておきたい!!コンテナ技術とDockerのキホンそろそろ知っておきたい!!コンテナ技術とDockerのキホン
そろそろ知っておきたい!!コンテナ技術と Dockerのキホン
Naoki Nagazumi
 
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
Koichiro Matsuoka
 
PostgreSQLアンチパターン
PostgreSQLアンチパターンPostgreSQLアンチパターン
PostgreSQLアンチパターン
Soudai Sone
 
いまさら聞けないパスワードの取り扱い方
いまさら聞けないパスワードの取り扱い方いまさら聞けないパスワードの取り扱い方
いまさら聞けないパスワードの取り扱い方
Hiroshi Tokumaru
 
React(TypeScript) + Go + Auth0 で実現する管理画面
React(TypeScript) + Go + Auth0 で実現する管理画面React(TypeScript) + Go + Auth0 で実現する管理画面
React(TypeScript) + Go + Auth0 で実現する管理画面
KentaEndoh
 
「仮想マシンからの移⾏先としてPaaSとKaaS、どちらを選ぶか? #ヤフー名古屋」
「仮想マシンからの移⾏先としてPaaSとKaaS、どちらを選ぶか? #ヤフー名古屋」「仮想マシンからの移⾏先としてPaaSとKaaS、どちらを選ぶか? #ヤフー名古屋」
「仮想マシンからの移⾏先としてPaaSとKaaS、どちらを選ぶか? #ヤフー名古屋」
Yahoo!デベロッパーネットワーク
 
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」までNeo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
Keiichiro Seida
 
Amazon Cognito使って認証したい?それならSpring Security使いましょう!
Amazon Cognito使って認証したい?それならSpring Security使いましょう!Amazon Cognito使って認証したい?それならSpring Security使いましょう!
Amazon Cognito使って認証したい?それならSpring Security使いましょう!
Ryosuke Uchitate
 
DI(依存性注入)について
DI(依存性注入)についてDI(依存性注入)について
DI(依存性注入)について
Yui Ito
 
Apache Avro vs Protocol Buffers
Apache Avro vs Protocol BuffersApache Avro vs Protocol Buffers
Apache Avro vs Protocol Buffers
Seiya Mizuno
 
Where狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキーWhere狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキー
yoku0825
 
How to Use JSON in MySQL Wrong
How to Use JSON in MySQL WrongHow to Use JSON in MySQL Wrong
How to Use JSON in MySQL Wrong
Karwin Software Solutions LLC
 
nginx入門
nginx入門nginx入門
nginx入門
Takashi Takizawa
 

What's hot (20)

イミュータブルデータモデルの極意
イミュータブルデータモデルの極意イミュータブルデータモデルの極意
イミュータブルデータモデルの極意
 
負荷試験ツールlocustを使おう
負荷試験ツールlocustを使おう負荷試験ツールlocustを使おう
負荷試験ツールlocustを使おう
 
概念モデルって難しいですよね
概念モデルって難しいですよね概念モデルって難しいですよね
概念モデルって難しいですよね
 
MongoDBが遅いときの切り分け方法
MongoDBが遅いときの切り分け方法MongoDBが遅いときの切り分け方法
MongoDBが遅いときの切り分け方法
 
MongoDB very basic (Japanese) / MongoDB基礎の基礎
MongoDB very basic (Japanese) / MongoDB基礎の基礎MongoDB very basic (Japanese) / MongoDB基礎の基礎
MongoDB very basic (Japanese) / MongoDB基礎の基礎
 
データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3
データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3 データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3
データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3
 
オブジェクト指向できていますか?
オブジェクト指向できていますか?オブジェクト指向できていますか?
オブジェクト指向できていますか?
 
そろそろ知っておきたい!!コンテナ技術と Dockerのキホン
そろそろ知っておきたい!!コンテナ技術とDockerのキホンそろそろ知っておきたい!!コンテナ技術とDockerのキホン
そろそろ知っておきたい!!コンテナ技術と Dockerのキホン
 
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
 
PostgreSQLアンチパターン
PostgreSQLアンチパターンPostgreSQLアンチパターン
PostgreSQLアンチパターン
 
いまさら聞けないパスワードの取り扱い方
いまさら聞けないパスワードの取り扱い方いまさら聞けないパスワードの取り扱い方
いまさら聞けないパスワードの取り扱い方
 
React(TypeScript) + Go + Auth0 で実現する管理画面
React(TypeScript) + Go + Auth0 で実現する管理画面React(TypeScript) + Go + Auth0 で実現する管理画面
React(TypeScript) + Go + Auth0 で実現する管理画面
 
「仮想マシンからの移⾏先としてPaaSとKaaS、どちらを選ぶか? #ヤフー名古屋」
「仮想マシンからの移⾏先としてPaaSとKaaS、どちらを選ぶか? #ヤフー名古屋」「仮想マシンからの移⾏先としてPaaSとKaaS、どちらを選ぶか? #ヤフー名古屋」
「仮想マシンからの移⾏先としてPaaSとKaaS、どちらを選ぶか? #ヤフー名古屋」
 
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」までNeo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
 
Amazon Cognito使って認証したい?それならSpring Security使いましょう!
Amazon Cognito使って認証したい?それならSpring Security使いましょう!Amazon Cognito使って認証したい?それならSpring Security使いましょう!
Amazon Cognito使って認証したい?それならSpring Security使いましょう!
 
DI(依存性注入)について
DI(依存性注入)についてDI(依存性注入)について
DI(依存性注入)について
 
Apache Avro vs Protocol Buffers
Apache Avro vs Protocol BuffersApache Avro vs Protocol Buffers
Apache Avro vs Protocol Buffers
 
Where狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキーWhere狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキー
 
How to Use JSON in MySQL Wrong
How to Use JSON in MySQL WrongHow to Use JSON in MySQL Wrong
How to Use JSON in MySQL Wrong
 
nginx入門
nginx入門nginx入門
nginx入門
 

Viewers also liked

12 tips on Django Best Practices
12 tips on Django Best Practices12 tips on Django Best Practices
12 tips on Django Best Practices
David Arcos
 
Multi Tenancy With Python and Django
Multi Tenancy With Python and DjangoMulti Tenancy With Python and Django
Multi Tenancy With Python and Django
scottcrespo
 
Moving from Django Apps to Services
Moving from Django Apps to ServicesMoving from Django Apps to Services
Moving from Django Apps to Services
Craig Kerstiens
 
Web Development with Python and Django
Web Development with Python and DjangoWeb Development with Python and Django
Web Development with Python and Django
Michael Pirnat
 
Getting Started With Django
Getting Started With DjangoGetting Started With Django
Getting Started With Django
jeff_croft
 
Developing Software As A Service App with Python & Django
Developing Software As A Service App with Python & DjangoDeveloping Software As A Service App with Python & Django
Developing Software As A Service App with Python & Django
Allan Mangune
 
Django for Beginners
Django for BeginnersDjango for Beginners
Django for Beginners
Jason Davies
 
Django Best Practices
Django Best PracticesDjango Best Practices
Django Best Practices
Abdullah Çetin ÇAVDAR
 
Create responsive websites with Django, REST and AngularJS
Create responsive websites with Django, REST and AngularJSCreate responsive websites with Django, REST and AngularJS
Create responsive websites with Django, REST and AngularJS
Hannes Hapke
 
Django design-patterns
Django design-patternsDjango design-patterns
Django design-patterns
Agiliq Info Solutions India Pvt Ltd
 
dJango
dJangodJango
dJango
Bob Chao
 
Django shop
Django shopDjango shop
Django shop
Tribaal
 
Why Django
Why DjangoWhy Django
Why Django
Idan Gazit
 
Django - O framework web para perfeccionistas com prazo
Django -  O framework web para perfeccionistas com prazoDjango -  O framework web para perfeccionistas com prazo
Django - O framework web para perfeccionistas com prazo
Teeh Amaral
 
Django workshop : let's make a blog
Django workshop : let's make a blogDjango workshop : let's make a blog
Django workshop : let's make a blog
Pierre Sudron
 
Django Worst Practices
Django Worst PracticesDjango Worst Practices
Django Worst Practices
Daniel Greenfeld
 
Django: Advanced Models
Django: Advanced ModelsDjango: Advanced Models
Django: Advanced Models
Ying-An Lai
 
Django tech-talk
Django tech-talkDjango tech-talk
Django tech-talk
dtdannen
 
Django Overview
Django OverviewDjango Overview
Django Overview
Brian Tol
 
Django in Windows
Django in WindowsDjango in Windows
Django in Windows
Python Devloper
 

Viewers also liked (20)

12 tips on Django Best Practices
12 tips on Django Best Practices12 tips on Django Best Practices
12 tips on Django Best Practices
 
Multi Tenancy With Python and Django
Multi Tenancy With Python and DjangoMulti Tenancy With Python and Django
Multi Tenancy With Python and Django
 
Moving from Django Apps to Services
Moving from Django Apps to ServicesMoving from Django Apps to Services
Moving from Django Apps to Services
 
Web Development with Python and Django
Web Development with Python and DjangoWeb Development with Python and Django
Web Development with Python and Django
 
Getting Started With Django
Getting Started With DjangoGetting Started With Django
Getting Started With Django
 
Developing Software As A Service App with Python & Django
Developing Software As A Service App with Python & DjangoDeveloping Software As A Service App with Python & Django
Developing Software As A Service App with Python & Django
 
Django for Beginners
Django for BeginnersDjango for Beginners
Django for Beginners
 
Django Best Practices
Django Best PracticesDjango Best Practices
Django Best Practices
 
Create responsive websites with Django, REST and AngularJS
Create responsive websites with Django, REST and AngularJSCreate responsive websites with Django, REST and AngularJS
Create responsive websites with Django, REST and AngularJS
 
Django design-patterns
Django design-patternsDjango design-patterns
Django design-patterns
 
dJango
dJangodJango
dJango
 
Django shop
Django shopDjango shop
Django shop
 
Why Django
Why DjangoWhy Django
Why Django
 
Django - O framework web para perfeccionistas com prazo
Django -  O framework web para perfeccionistas com prazoDjango -  O framework web para perfeccionistas com prazo
Django - O framework web para perfeccionistas com prazo
 
Django workshop : let's make a blog
Django workshop : let's make a blogDjango workshop : let's make a blog
Django workshop : let's make a blog
 
Django Worst Practices
Django Worst PracticesDjango Worst Practices
Django Worst Practices
 
Django: Advanced Models
Django: Advanced ModelsDjango: Advanced Models
Django: Advanced Models
 
Django tech-talk
Django tech-talkDjango tech-talk
Django tech-talk
 
Django Overview
Django OverviewDjango Overview
Django Overview
 
Django in Windows
Django in WindowsDjango in Windows
Django in Windows
 

Similar to The Best (and Worst) of Django

[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC
Alive Kuo
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
Richard Leland
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
Richard Leland
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
Richard Leland
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
Richard Leland
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
Richard Leland
 
Mojolicious
MojoliciousMojolicious
Mojolicious
Marcos Rebelo
 
Having Fun with Play
Having Fun with PlayHaving Fun with Play
Having Fun with Play
Clinton Dreisbach
 
#NewMeetup Performance
#NewMeetup Performance#NewMeetup Performance
#NewMeetup Performance
Justin Cataldo
 
Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenery
toddbr
 
Keeping It Small with Slim
Keeping It Small with SlimKeeping It Small with Slim
Keeping It Small with Slim
Raven Tools
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the Finish
Yehuda Katz
 
Choosing a Javascript Framework
Choosing a Javascript FrameworkChoosing a Javascript Framework
Choosing a Javascript Framework
All Things Open
 
Introducing the Seneca MVP framework for Node.js
Introducing the Seneca MVP framework for Node.jsIntroducing the Seneca MVP framework for Node.js
Introducing the Seneca MVP framework for Node.js
Richard Rodger
 
20120816 nodejsdublin
20120816 nodejsdublin20120816 nodejsdublin
20120816 nodejsdublin
Richard Rodger
 
Keeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro frameworkKeeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro framework
Jeremy Kendall
 
Burn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websitesBurn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websites
Lindsay Holmwood
 
Play vs Rails
Play vs RailsPlay vs Rails
Play vs Rails
Daniel Cukier
 
jQuery - 10 Time-Savers You (Maybe) Don't Know
jQuery - 10 Time-Savers You (Maybe) Don't KnowjQuery - 10 Time-Savers You (Maybe) Don't Know
jQuery - 10 Time-Savers You (Maybe) Don't Know
girish82
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Paulo Ragonha
 

Similar to The Best (and Worst) of Django (20)

[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
 
Mojolicious
MojoliciousMojolicious
Mojolicious
 
Having Fun with Play
Having Fun with PlayHaving Fun with Play
Having Fun with Play
 
#NewMeetup Performance
#NewMeetup Performance#NewMeetup Performance
#NewMeetup Performance
 
Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenery
 
Keeping It Small with Slim
Keeping It Small with SlimKeeping It Small with Slim
Keeping It Small with Slim
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the Finish
 
Choosing a Javascript Framework
Choosing a Javascript FrameworkChoosing a Javascript Framework
Choosing a Javascript Framework
 
Introducing the Seneca MVP framework for Node.js
Introducing the Seneca MVP framework for Node.jsIntroducing the Seneca MVP framework for Node.js
Introducing the Seneca MVP framework for Node.js
 
20120816 nodejsdublin
20120816 nodejsdublin20120816 nodejsdublin
20120816 nodejsdublin
 
Keeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro frameworkKeeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro framework
 
Burn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websitesBurn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websites
 
Play vs Rails
Play vs RailsPlay vs Rails
Play vs Rails
 
jQuery - 10 Time-Savers You (Maybe) Don't Know
jQuery - 10 Time-Savers You (Maybe) Don't KnowjQuery - 10 Time-Savers You (Maybe) Don't Know
jQuery - 10 Time-Savers You (Maybe) Don't Know
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
 

More from Jacob Kaplan-Moss

Introduction To Django (Strange Loop 2011)
Introduction To Django (Strange Loop 2011)Introduction To Django (Strange Loop 2011)
Introduction To Django (Strange Loop 2011)
Jacob Kaplan-Moss
 
Writing great documentation - CodeConf 2011
Writing great documentation - CodeConf 2011Writing great documentation - CodeConf 2011
Writing great documentation - CodeConf 2011
Jacob Kaplan-Moss
 
What's new in Django 1.2?
What's new in Django 1.2?What's new in Django 1.2?
What's new in Django 1.2?
Jacob Kaplan-Moss
 
Django Introduction, Dev in Rio 2009
Django Introduction, Dev in Rio 2009Django Introduction, Dev in Rio 2009
Django Introduction, Dev in Rio 2009
Jacob Kaplan-Moss
 
Snakes on the Web
Snakes on the WebSnakes on the Web
Snakes on the Web
Jacob Kaplan-Moss
 
Django In The Real World
Django In The Real WorldDjango In The Real World
Django In The Real World
Jacob Kaplan-Moss
 
Building a web framework: Django's design decisions
Building a web framework: Django's design decisionsBuilding a web framework: Django's design decisions
Building a web framework: Django's design decisions
Jacob Kaplan-Moss
 
Django in the Real World
Django in the Real WorldDjango in the Real World
Django in the Real World
Jacob Kaplan-Moss
 
State Of Django
State Of DjangoState Of Django
State Of Django
Jacob Kaplan-Moss
 
Django - the first five years
Django - the first five yearsDjango - the first five years
Django - the first five years
Jacob Kaplan-Moss
 
A brief history of Django model syntax
A brief history of Django model syntaxA brief history of Django model syntax
A brief history of Django model syntax
Jacob Kaplan-Moss
 
Django Update (OSCON 2007)
Django Update (OSCON 2007)Django Update (OSCON 2007)
Django Update (OSCON 2007)
Jacob Kaplan-Moss
 

More from Jacob Kaplan-Moss (12)

Introduction To Django (Strange Loop 2011)
Introduction To Django (Strange Loop 2011)Introduction To Django (Strange Loop 2011)
Introduction To Django (Strange Loop 2011)
 
Writing great documentation - CodeConf 2011
Writing great documentation - CodeConf 2011Writing great documentation - CodeConf 2011
Writing great documentation - CodeConf 2011
 
What's new in Django 1.2?
What's new in Django 1.2?What's new in Django 1.2?
What's new in Django 1.2?
 
Django Introduction, Dev in Rio 2009
Django Introduction, Dev in Rio 2009Django Introduction, Dev in Rio 2009
Django Introduction, Dev in Rio 2009
 
Snakes on the Web
Snakes on the WebSnakes on the Web
Snakes on the Web
 
Django In The Real World
Django In The Real WorldDjango In The Real World
Django In The Real World
 
Building a web framework: Django's design decisions
Building a web framework: Django's design decisionsBuilding a web framework: Django's design decisions
Building a web framework: Django's design decisions
 
Django in the Real World
Django in the Real WorldDjango in the Real World
Django in the Real World
 
State Of Django
State Of DjangoState Of Django
State Of Django
 
Django - the first five years
Django - the first five yearsDjango - the first five years
Django - the first five years
 
A brief history of Django model syntax
A brief history of Django model syntaxA brief history of Django model syntax
A brief history of Django model syntax
 
Django Update (OSCON 2007)
Django Update (OSCON 2007)Django Update (OSCON 2007)
Django Update (OSCON 2007)
 

Recently uploaded

※#@$$+2.7.7.8.4.1.1.5.7.4.6. ※가등록함ஜ۩۞۩ஜhow to Join illuminati 6666 for Money ...
※#@$$+2.7.7.8.4.1.1.5.7.4.6. ※가등록함ஜ۩۞۩ஜhow to Join illuminati 6666 for Money ...※#@$$+2.7.7.8.4.1.1.5.7.4.6. ※가등록함ஜ۩۞۩ஜhow to Join illuminati 6666 for Money ...
※#@$$+2.7.7.8.4.1.1.5.7.4.6. ※가등록함ஜ۩۞۩ஜhow to Join illuminati 6666 for Money ...
Asa Samaul
 
Private Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1...
Private Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1...Private Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1...
Private Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1...
sonamrawat5631
 
The McNulty Doctrine: A Philosophy of Futility and Fury
The McNulty Doctrine: A Philosophy of Futility and FuryThe McNulty Doctrine: A Philosophy of Futility and Fury
The McNulty Doctrine: A Philosophy of Futility and Fury
Rodney Thomas Jr
 
The Stardust Industry How Hollywood Performers Handle Their Careers By Enzo Z...
The Stardust Industry How Hollywood Performers Handle Their Careers By Enzo Z...The Stardust Industry How Hollywood Performers Handle Their Careers By Enzo Z...
The Stardust Industry How Hollywood Performers Handle Their Careers By Enzo Z...
Enzo Zelocchi Fan Page
 
New Girls Call Delhi 9873940964 Provide Best And Top Girl Service And No1 in ...
New Girls Call Delhi 9873940964 Provide Best And Top Girl Service And No1 in ...New Girls Call Delhi 9873940964 Provide Best And Top Girl Service And No1 in ...
New Girls Call Delhi 9873940964 Provide Best And Top Girl Service And No1 in ...
elbertablack
 
The Bloodline Mandate: Power, Loyalty, Respect (An Unconventional Reign). The...
The Bloodline Mandate: Power, Loyalty, Respect (An Unconventional Reign). The...The Bloodline Mandate: Power, Loyalty, Respect (An Unconventional Reign). The...
The Bloodline Mandate: Power, Loyalty, Respect (An Unconventional Reign). The...
Rodney Thomas Jr
 
Google Pay | Google for DeGoogle Pay | Google for Developersvelopers
Google Pay | Google for DeGoogle Pay | Google for DevelopersvelopersGoogle Pay | Google for DeGoogle Pay | Google for Developersvelopers
Google Pay | Google for DeGoogle Pay | Google for Developersvelopers
gargtinna79
 
※가 등록함+2.7.7.8.4.1.1.5.7.4.6 Best bring back lost lover in LONDON USA CANADA...
※가 등록함+2.7.7.8.4.1.1.5.7.4.6  Best bring back lost lover in LONDON USA CANADA...※가 등록함+2.7.7.8.4.1.1.5.7.4.6  Best bring back lost lover in LONDON USA CANADA...
※가 등록함+2.7.7.8.4.1.1.5.7.4.6 Best bring back lost lover in LONDON USA CANADA...
Asa Samaul
 
BBW Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1 in ...
BBW Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1 in ...BBW Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1 in ...
BBW Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1 in ...
AK47
 
Sunny and Rishi Book Written By Basak Serin
Sunny and Rishi Book Written By Basak SerinSunny and Rishi Book Written By Basak Serin
Sunny and Rishi Book Written By Basak Serin
Basak24
 
LiquidText - "PDF Editor with Superpowers" FastCompany
LiquidText - "PDF Editor with Superpowers" FastCompanyLiquidText - "PDF Editor with Superpowers" FastCompany
LiquidText - "PDF Editor with Superpowers" FastCompany
kavitakumariok138
 
New Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1 in ...
New Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1 in ...New Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1 in ...
New Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1 in ...
janvikumar4133
 
A TO Z INDIA Monthly Magazine - AUGUST 2024
A TO Z INDIA Monthly Magazine - AUGUST 2024A TO Z INDIA Monthly Magazine - AUGUST 2024
A TO Z INDIA Monthly Magazine - AUGUST 2024
Indira Srivatsa
 
BBW Girls Call Mumbai 9910780858 Provide Best And Top Girl Service And No1 in...
BBW Girls Call Mumbai 9910780858 Provide Best And Top Girl Service And No1 in...BBW Girls Call Mumbai 9910780858 Provide Best And Top Girl Service And No1 in...
BBW Girls Call Mumbai 9910780858 Provide Best And Top Girl Service And No1 in...
rhiannateal
 
Girls Call Vile Parle 9967584737 Provide Best And Top Girl Service And No1 in...
Girls Call Vile Parle 9967584737 Provide Best And Top Girl Service And No1 in...Girls Call Vile Parle 9967584737 Provide Best And Top Girl Service And No1 in...
Girls Call Vile Parle 9967584737 Provide Best And Top Girl Service And No1 in...
sunita ahuja
 
Girls Call Mumbai 9833363713 Provide Best And Top Girl Service And No1 in City
Girls Call Mumbai 9833363713 Provide Best And Top Girl Service And No1 in CityGirls Call Mumbai 9833363713 Provide Best And Top Girl Service And No1 in City
Girls Call Mumbai 9833363713 Provide Best And Top Girl Service And No1 in City
girl escorts service
 
Looney Tunes Cartoons- Drum Schtick (Storyboards)
Looney Tunes Cartoons- Drum Schtick (Storyboards)Looney Tunes Cartoons- Drum Schtick (Storyboards)
Looney Tunes Cartoons- Drum Schtick (Storyboards)
mikepelensky1
 
Pay-Per-View vs Subscription: What's the Difference.pdf
Pay-Per-View vs Subscription: What's the Difference.pdfPay-Per-View vs Subscription: What's the Difference.pdf
Pay-Per-View vs Subscription: What's the Difference.pdf
Mega P
 
The Raw Fury and Measured Rage: A Look at Connor Kenway's Speech
The Raw Fury and Measured Rage: A Look at Connor Kenway's SpeechThe Raw Fury and Measured Rage: A Look at Connor Kenway's Speech
The Raw Fury and Measured Rage: A Look at Connor Kenway's Speech
Rodney Thomas Jr
 
Soap2day Blues? Explore Safe & Legal Streaming Options. pdf
Soap2day Blues? Explore Safe & Legal Streaming Options. pdfSoap2day Blues? Explore Safe & Legal Streaming Options. pdf
Soap2day Blues? Explore Safe & Legal Streaming Options. pdf
LisaSmith443706
 

Recently uploaded (20)

※#@$$+2.7.7.8.4.1.1.5.7.4.6. ※가등록함ஜ۩۞۩ஜhow to Join illuminati 6666 for Money ...
※#@$$+2.7.7.8.4.1.1.5.7.4.6. ※가등록함ஜ۩۞۩ஜhow to Join illuminati 6666 for Money ...※#@$$+2.7.7.8.4.1.1.5.7.4.6. ※가등록함ஜ۩۞۩ஜhow to Join illuminati 6666 for Money ...
※#@$$+2.7.7.8.4.1.1.5.7.4.6. ※가등록함ஜ۩۞۩ஜhow to Join illuminati 6666 for Money ...
 
Private Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1...
Private Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1...Private Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1...
Private Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1...
 
The McNulty Doctrine: A Philosophy of Futility and Fury
The McNulty Doctrine: A Philosophy of Futility and FuryThe McNulty Doctrine: A Philosophy of Futility and Fury
The McNulty Doctrine: A Philosophy of Futility and Fury
 
The Stardust Industry How Hollywood Performers Handle Their Careers By Enzo Z...
The Stardust Industry How Hollywood Performers Handle Their Careers By Enzo Z...The Stardust Industry How Hollywood Performers Handle Their Careers By Enzo Z...
The Stardust Industry How Hollywood Performers Handle Their Careers By Enzo Z...
 
New Girls Call Delhi 9873940964 Provide Best And Top Girl Service And No1 in ...
New Girls Call Delhi 9873940964 Provide Best And Top Girl Service And No1 in ...New Girls Call Delhi 9873940964 Provide Best And Top Girl Service And No1 in ...
New Girls Call Delhi 9873940964 Provide Best And Top Girl Service And No1 in ...
 
The Bloodline Mandate: Power, Loyalty, Respect (An Unconventional Reign). The...
The Bloodline Mandate: Power, Loyalty, Respect (An Unconventional Reign). The...The Bloodline Mandate: Power, Loyalty, Respect (An Unconventional Reign). The...
The Bloodline Mandate: Power, Loyalty, Respect (An Unconventional Reign). The...
 
Google Pay | Google for DeGoogle Pay | Google for Developersvelopers
Google Pay | Google for DeGoogle Pay | Google for DevelopersvelopersGoogle Pay | Google for DeGoogle Pay | Google for Developersvelopers
Google Pay | Google for DeGoogle Pay | Google for Developersvelopers
 
※가 등록함+2.7.7.8.4.1.1.5.7.4.6 Best bring back lost lover in LONDON USA CANADA...
※가 등록함+2.7.7.8.4.1.1.5.7.4.6  Best bring back lost lover in LONDON USA CANADA...※가 등록함+2.7.7.8.4.1.1.5.7.4.6  Best bring back lost lover in LONDON USA CANADA...
※가 등록함+2.7.7.8.4.1.1.5.7.4.6 Best bring back lost lover in LONDON USA CANADA...
 
BBW Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1 in ...
BBW Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1 in ...BBW Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1 in ...
BBW Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1 in ...
 
Sunny and Rishi Book Written By Basak Serin
Sunny and Rishi Book Written By Basak SerinSunny and Rishi Book Written By Basak Serin
Sunny and Rishi Book Written By Basak Serin
 
LiquidText - "PDF Editor with Superpowers" FastCompany
LiquidText - "PDF Editor with Superpowers" FastCompanyLiquidText - "PDF Editor with Superpowers" FastCompany
LiquidText - "PDF Editor with Superpowers" FastCompany
 
New Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1 in ...
New Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1 in ...New Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1 in ...
New Girls Call Delhi 9711199171 Provide Best And Top Girl Service And No1 in ...
 
A TO Z INDIA Monthly Magazine - AUGUST 2024
A TO Z INDIA Monthly Magazine - AUGUST 2024A TO Z INDIA Monthly Magazine - AUGUST 2024
A TO Z INDIA Monthly Magazine - AUGUST 2024
 
BBW Girls Call Mumbai 9910780858 Provide Best And Top Girl Service And No1 in...
BBW Girls Call Mumbai 9910780858 Provide Best And Top Girl Service And No1 in...BBW Girls Call Mumbai 9910780858 Provide Best And Top Girl Service And No1 in...
BBW Girls Call Mumbai 9910780858 Provide Best And Top Girl Service And No1 in...
 
Girls Call Vile Parle 9967584737 Provide Best And Top Girl Service And No1 in...
Girls Call Vile Parle 9967584737 Provide Best And Top Girl Service And No1 in...Girls Call Vile Parle 9967584737 Provide Best And Top Girl Service And No1 in...
Girls Call Vile Parle 9967584737 Provide Best And Top Girl Service And No1 in...
 
Girls Call Mumbai 9833363713 Provide Best And Top Girl Service And No1 in City
Girls Call Mumbai 9833363713 Provide Best And Top Girl Service And No1 in CityGirls Call Mumbai 9833363713 Provide Best And Top Girl Service And No1 in City
Girls Call Mumbai 9833363713 Provide Best And Top Girl Service And No1 in City
 
Looney Tunes Cartoons- Drum Schtick (Storyboards)
Looney Tunes Cartoons- Drum Schtick (Storyboards)Looney Tunes Cartoons- Drum Schtick (Storyboards)
Looney Tunes Cartoons- Drum Schtick (Storyboards)
 
Pay-Per-View vs Subscription: What's the Difference.pdf
Pay-Per-View vs Subscription: What's the Difference.pdfPay-Per-View vs Subscription: What's the Difference.pdf
Pay-Per-View vs Subscription: What's the Difference.pdf
 
The Raw Fury and Measured Rage: A Look at Connor Kenway's Speech
The Raw Fury and Measured Rage: A Look at Connor Kenway's SpeechThe Raw Fury and Measured Rage: A Look at Connor Kenway's Speech
The Raw Fury and Measured Rage: A Look at Connor Kenway's Speech
 
Soap2day Blues? Explore Safe & Legal Streaming Options. pdf
Soap2day Blues? Explore Safe & Legal Streaming Options. pdfSoap2day Blues? Explore Safe & Legal Streaming Options. pdf
Soap2day Blues? Explore Safe & Legal Streaming Options. pdf
 

The Best (and Worst) of Django

  • 1. The best (and worst) of Django jacob@jacobian.org http://lanyrd.com/sgbwt/
  • 5. “Do the simplest thing that could possibly work.” — Simon Willison
  • 7. 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.
  • 9. “It’s a pretty simple site; about fifteen or twenty thousand lines of code…”
  • 11. models.py — 2,800 lines views.py — 11,900 lines
  • 13. Big is bad; Small is beautiful.
  • 14. The Django mindset: Application: some bit of functionality. Site: several applications. Spin off new apps liberally. A suite of apps ready for use.
  • 16. There’s no such thing as “content.”
  • 18. Django is an un-CMS.
  • 19. The Django mindset: A great and powerful respect for data. Model the data correctly and the rest of the site will just fall out of that. Denormalization is all well and good, but never throw data away.
  • 21. “Everything needs a creation date.”
  • 22. class  BaseObject(models.Model):        creation_date  =  models.DateField()        … class  Animal(BaseObject):        … class  Vegetable(BaseObject):        … class  Mineral(BaseObject):        …
  • 23. 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")
  • 24. What you want: >>>  BaseObject.objects.all() [<Animal:  Frog>,  <Vegetable:  Carrot>,  <Mineral:  Gold>,  ...] What you get: >>>  BaseObject.objects.all() [<BaseObject:  1>,  <BaseObject:  2>,  <BaseObject:  3>,  ...]
  • 25. 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
  • 26. “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.”
  • 28. “But… but… everything really does need a creation date!” So give everything a creation date.
  • 29. “Do the simplest thing that could possibly work.” — Simon Willison
  • 30. 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).
  • 31. Those who do not understand PYTHONPATH are doomed to failure.
  • 32. >>>  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'>
  • 33. ~/Projects/djdash !""  __init__.py !""  dashboard $      !""  __init__.py $      !""  admin.py $      !""  models.py $      #""  views.py #""  manage.py (https://github.com/jacobian/django-dev-dashboard)
  • 34. $  ./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.Metric False
  • 35. 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!”
  • 36. Django is wrong! (I’m sorry.)
  • 37. Fixing import madness 1. 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.
  • 38. Delete manage.py? $  django-­‐admin.py  shell  -­‐-­‐pythonpath=`pwd`  -­‐-­‐settings=settings.local >>>  import  dashboard.models >>>  import  djdash.dashboard.models Traceback  (most  recent  call  last) ... ImportError:  No  module  named  djdash.dashboard.models
  • 39. 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
  • 41. Don’t do this … INSTALLED_APPS  +=  [p  for  p  in  os.listdir(BASE)                                      if  os.path.isdir(p)]
  • 42. … 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)
  • 43. … 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)
  • 44. Python’s design is predicated on the proposition that code is more often read than written.
  • 45. 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') )
  • 47. The localsettings anti-pattern At the bottom of your settings file: try:        from  local_settings  import  * except  ImportError:        pass
  • 48. “It’s simple: just create a local_settings.py, throw overridden settings in there, and then never check the file into source control.”
  • 49. “It’s simple: just create a local_settings.py, throw overridden settings in there, and then never check the file into source control.”
  • 50. Handling multiple settings files 1. Don’t. Why is your staging environment different from production? 2. Use DJANGO_SETTINGS_MODULE.
  • 51. The one true way settings #  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.wsgi os.environ['DJANGO_SETTINGS_MODULE']  =  'settings.deploy'
  • 52. “Do the simplest thing that could possibly work.” — Simon Willison
  • 53. Thank you! jacob@jacobian.org http://lanyrd.com/sgbwt/

Editor's Notes

  1. \n
  2. lead developer, django\nconsultant, revsys, where I help companies scale web apps.\n
  3. lead developer, django\nconsultant, revsys, where I help companies scale web apps.\n
  4. This is how many lines of Django code I&amp;#x2019;ve read over the last 2 years or so.\n\nBy comparison: Django: 120kloc, Python stdlib: 300kloc, pypy 550kloc (or 1m incl stdlib)\n\nMuch of this code has been good; some of it has been great; some very very bad. My goal today is to share with you some of what I&amp;#x2019;ve learned - to suggest certain successful patterns, and warn you away from other noxious ones.\n
  5. The theme that&amp;#x2019;s emerged here: y&amp;#x2019;all are far to damned clever.\n\n\n
  6. One of Simon&amp;#x2019;s favorite quips. Also known as &amp;#x201C;KISS&amp;#x201D; and a bunch of other things, but what I like about this particular formulation is the word &amp;#x201C;possibly&amp;#x201D;. It&amp;#x2019;s not &amp;#x201C;do the simple thing that you know will work&amp;#x201D;; it&amp;#x2019;s &amp;#x201C;do something that *could* work&amp;#x201D;. If it doesn&amp;#x2019;t, *then* you can get more complex.\n\nDjango does not lend itself very well to overengineering. Keep it small, keep it simple, keep it clean.\n
  7. Pattern 1: small is beautiful.\n
  8. This is easiest to explain by starting with the anti-pattern, so let&amp;#x2019;s start there. I see, from time to time, Django apps that are just one huge ball of wax - a monolith. Instead of multiple apps, there&amp;#x2019;s just a single app with all the code in it.\n\nTypically these don&amp;#x2019;t grow well at all. Because there&amp;#x2019;s no real thought to factoring, the web gets more and more tangled, and you end up needing to fall back on site-wide things like middleware and context processors to get bits into the right place. At the extreme, people end up inventing their own plugin-like concepts. Remember: Django&amp;#x2019;s *got that* -- INSTALLED_APPS -- so if you&amp;#x2019;re reinventing that system you&amp;#x2019;re cutting against the grain.\n
  9. \n
  10. I&amp;#x2019;ll give one example. Client wanted us to review his code, he told us that it was 20kloc. Sounds fairly small and easy to review, until &amp;#x2026;\n
  11. Wait, where&amp;#x2019;s the rest of the site?\n
  12. Groan.\n
  13. I often like to graph the models to get a handle on how the data fits together. The red box is just this one app; something on the order of 200 or so models in there.\n\nThere was stuff in there like &amp;#x201C;Customer&amp;#x201D; and &amp;#x201C;Customer2&amp;#x201D; &amp;#x2014; with such a big ball of wax, people were afraid to make changes, not knowing what the ramifications would be.\n
  14. Large modules are generally considered &amp;#x201C;code smell.&amp;#x201D; All-in-one design prevents reuse. You&amp;#x2019;ll need to introduce random, unrelated code. Monoliths are hellish to test.\n\nMany great Django apps are very small. Even a lot of &amp;#x201C;simple&amp;#x201D; Django sites commonly have a dozen or more applications in INSTALLED_APPS. If you&amp;#x2019;ve got a complex site and a short application list, something&amp;#x2019;s probably wrong.\n
  15. \n
  16. Here&amp;#x2019;s what (the data parts of) a well-designed site look like. This is a selection of 5 apps from a larger project, but note how easily I can pull them out -- there&amp;#x2019;s not any tendrils out of here[*]\n\nI want to particularly point out the faqcreator app. The system here is a ticketing and knowledge base system, and one requirement was the ability to quickly convert a ticket to an FAQ entry. I could put this logic in the FAQ system., but then it would create a circular dependency between tickets and faqs, which makes no sense and prevents reuse. So I introduced a faqcreator that depends on both.\n\nRemember: dependencies are just fine, but they should be logical, and they should be linear.\n\n[*] actually there&amp;#x2019;s an FK to user but I typically omit that -- I consider Django apps fair game for deps.\n
  17. \n
  18. \n
  19. \n
  20. These make up the bulk of what you&amp;#x2019;ll write as a Django dev, usually.\n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. Pattern two: model your damned data. aka Django is not a CMS.\n
  28. A typical &amp;#x201C;CMS&amp;#x201D;. This makes me sad sad sad.\n
  29. ca 2003 Adrian/Simon working at the LJW; content managed in a (reasonably good, custom built) CMS. But you just can&amp;#x2019;t pull information out of CMSes\n\nexample: gas prices. every morning, a reporter would call a few gas staions in lawrence and ask about gas prices. Then he&amp;#x2019;d open the cms and type &amp;#x201C;Shell: $1.82. Quick Stop: $1.88&amp;#x201D;, etc. This is terrible: no way to get historical data, no way to graph changes over time, no way to build a &amp;#x201C;closest gas to me&amp;#x201D;, etc. This is turning &amp;#x201C;data&amp;#x201D; into &amp;#x201C;text&amp;#x201D;, and readers predictably found these &amp;#x201C;stories&amp;#x201D; useless. Eventually the reporter had to stop. Imagine if instead we&amp;#x2019;d collected that data as data...\n\nDjango started as a reaction against CMSes. This isn&amp;#x2019;t to say that CMSes are bad. But if you want one, Django isn&amp;#x2019;t it.\n
  30. Instead, Django really really wants you to model your data. There&amp;#x2019;s no such thing as content: there&amp;#x2019;s gas stations and price measurements; public officials and speaking appearances; photographs and infographics.\n\nAnother example: look at lanyrd. Plenty of people have made conference web sites, but Simon followed (because helped invent!) the Django philosophy, and the site&amp;#x2019;s incredibly useful because if its powerful respect for the data.\n
  31. One particularly pernicious form the &amp;#x201C;content&amp;#x201D; issue takes is what I all the &amp;#x201C;everything-is-a&amp;#x201D; antipattern.\n
  32. Starts here.\n
  33. So you do something like this.\n\nRemember, though: databases are *not* OO, so what this actually does is to create a foreign key from (Animal, Vegetable, Mineral) back to BaseObject.\n
  34. So consider what happens when you do queries.\n\nNow there&amp;#x2019;s a &amp;#x201C;hidden join&amp;#x201D;. Now, the &amp;#x201C;JOINs are evil&amp;#x201D; thing is a bit of a myth, at least if you&amp;#x2019;re using a grown-up database, but it&amp;#x2019;s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you&amp;#x2019;ve got multiple levels of interitance.\n
  35. So consider what happens when you do queries.\n\nNow there&amp;#x2019;s a &amp;#x201C;hidden join&amp;#x201D;. Now, the &amp;#x201C;JOINs are evil&amp;#x201D; thing is a bit of a myth, at least if you&amp;#x2019;re using a grown-up database, but it&amp;#x2019;s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you&amp;#x2019;ve got multiple levels of interitance.\n
  36. So consider what happens when you do queries.\n\nNow there&amp;#x2019;s a &amp;#x201C;hidden join&amp;#x201D;. Now, the &amp;#x201C;JOINs are evil&amp;#x201D; thing is a bit of a myth, at least if you&amp;#x2019;re using a grown-up database, but it&amp;#x2019;s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you&amp;#x2019;ve got multiple levels of interitance.\n
  37. So consider what happens when you do queries.\n\nNow there&amp;#x2019;s a &amp;#x201C;hidden join&amp;#x201D;. Now, the &amp;#x201C;JOINs are evil&amp;#x201D; thing is a bit of a myth, at least if you&amp;#x2019;re using a grown-up database, but it&amp;#x2019;s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you&amp;#x2019;ve got multiple levels of interitance.\n
  38. So consider what happens when you do queries.\n\nNow there&amp;#x2019;s a &amp;#x201C;hidden join&amp;#x201D;. Now, the &amp;#x201C;JOINs are evil&amp;#x201D; thing is a bit of a myth, at least if you&amp;#x2019;re using a grown-up database, but it&amp;#x2019;s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you&amp;#x2019;ve got multiple levels of interitance.\n
  39. So consider what happens when you do queries.\n\nNow there&amp;#x2019;s a &amp;#x201C;hidden join&amp;#x201D;. Now, the &amp;#x201C;JOINs are evil&amp;#x201D; thing is a bit of a myth, at least if you&amp;#x2019;re using a grown-up database, but it&amp;#x2019;s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you&amp;#x2019;ve got multiple levels of interitance.\n
  40. So consider what happens when you do queries.\n\nNow there&amp;#x2019;s a &amp;#x201C;hidden join&amp;#x201D;. Now, the &amp;#x201C;JOINs are evil&amp;#x201D; thing is a bit of a myth, at least if you&amp;#x2019;re using a grown-up database, but it&amp;#x2019;s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you&amp;#x2019;ve got multiple levels of interitance.\n
  41. So consider what happens when you do queries.\n\nNow there&amp;#x2019;s a &amp;#x201C;hidden join&amp;#x201D;. Now, the &amp;#x201C;JOINs are evil&amp;#x201D; thing is a bit of a myth, at least if you&amp;#x2019;re using a grown-up database, but it&amp;#x2019;s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you&amp;#x2019;ve got multiple levels of interitance.\n
  42. So consider what happens when you do queries.\n\nNow there&amp;#x2019;s a &amp;#x201C;hidden join&amp;#x201D;. Now, the &amp;#x201C;JOINs are evil&amp;#x201D; thing is a bit of a myth, at least if you&amp;#x2019;re using a grown-up database, but it&amp;#x2019;s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you&amp;#x2019;ve got multiple levels of interitance.\n
  43. So consider what happens when you do queries.\n\nNow there&amp;#x2019;s a &amp;#x201C;hidden join&amp;#x201D;. Now, the &amp;#x201C;JOINs are evil&amp;#x201D; thing is a bit of a myth, at least if you&amp;#x2019;re using a grown-up database, but it&amp;#x2019;s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you&amp;#x2019;ve got multiple levels of interitance.\n
  44. So consider what happens when you do queries.\n\nNow there&amp;#x2019;s a &amp;#x201C;hidden join&amp;#x201D;. Now, the &amp;#x201C;JOINs are evil&amp;#x201D; thing is a bit of a myth, at least if you&amp;#x2019;re using a grown-up database, but it&amp;#x2019;s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you&amp;#x2019;ve got multiple levels of interitance.\n
  45. So consider what happens when you do queries.\n\nNow there&amp;#x2019;s a &amp;#x201C;hidden join&amp;#x201D;. Now, the &amp;#x201C;JOINs are evil&amp;#x201D; thing is a bit of a myth, at least if you&amp;#x2019;re using a grown-up database, but it&amp;#x2019;s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you&amp;#x2019;ve got multiple levels of interitance.\n
  46. But here&amp;#x2019;s where it gets really bad -- people *want* base classes to automatically &amp;#x201C;narrow&amp;#x201D; themselves to the appropriate subclass, but this isn&amp;#x2019;t something Django&amp;#x2019;s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can&amp;#x2019;t do it efficiently.\n
  47. But here&amp;#x2019;s where it gets really bad -- people *want* base classes to automatically &amp;#x201C;narrow&amp;#x201D; themselves to the appropriate subclass, but this isn&amp;#x2019;t something Django&amp;#x2019;s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can&amp;#x2019;t do it efficiently.\n
  48. But here&amp;#x2019;s where it gets really bad -- people *want* base classes to automatically &amp;#x201C;narrow&amp;#x201D; themselves to the appropriate subclass, but this isn&amp;#x2019;t something Django&amp;#x2019;s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can&amp;#x2019;t do it efficiently.\n
  49. But here&amp;#x2019;s where it gets really bad -- people *want* base classes to automatically &amp;#x201C;narrow&amp;#x201D; themselves to the appropriate subclass, but this isn&amp;#x2019;t something Django&amp;#x2019;s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can&amp;#x2019;t do it efficiently.\n
  50. But here&amp;#x2019;s where it gets really bad -- people *want* base classes to automatically &amp;#x201C;narrow&amp;#x201D; themselves to the appropriate subclass, but this isn&amp;#x2019;t something Django&amp;#x2019;s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can&amp;#x2019;t do it efficiently.\n
  51. But here&amp;#x2019;s where it gets really bad -- people *want* base classes to automatically &amp;#x201C;narrow&amp;#x201D; themselves to the appropriate subclass, but this isn&amp;#x2019;t something Django&amp;#x2019;s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can&amp;#x2019;t do it efficiently.\n
  52. But here&amp;#x2019;s where it gets really bad -- people *want* base classes to automatically &amp;#x201C;narrow&amp;#x201D; themselves to the appropriate subclass, but this isn&amp;#x2019;t something Django&amp;#x2019;s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can&amp;#x2019;t do it efficiently.\n
  53. But here&amp;#x2019;s where it gets really bad -- people *want* base classes to automatically &amp;#x201C;narrow&amp;#x201D; themselves to the appropriate subclass, but this isn&amp;#x2019;t something Django&amp;#x2019;s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can&amp;#x2019;t do it efficiently.\n
  54. But here&amp;#x2019;s where it gets really bad -- people *want* base classes to automatically &amp;#x201C;narrow&amp;#x201D; themselves to the appropriate subclass, but this isn&amp;#x2019;t something Django&amp;#x2019;s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can&amp;#x2019;t do it efficiently.\n
  55. But here&amp;#x2019;s where it gets really bad -- people *want* base classes to automatically &amp;#x201C;narrow&amp;#x201D; themselves to the appropriate subclass, but this isn&amp;#x2019;t something Django&amp;#x2019;s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can&amp;#x2019;t do it efficiently.\n
  56. So maybe you try something like this to scope things down. Now you&amp;#x2019;re doing O(N) queries.\n
  57. \n
  58. One of the simplest performance metrics is the number of RTs between your web servers and the db server. As a rule of thumb, you can kinda assume that all queries take roughly the same time; it&amp;#x2019;s the network round-trip that kills you. Remember: these queries are done synchronously, so even if the db can do a query in only 1ms this page would still take 1.8seconds to render!\n
  59. At this point I hear you saying &amp;#x201C;but this *is* a legitimate business need!&amp;#x201D;\n\nSo give everything a creation date. Do 3 queries (Animal, Veg, Min) and then combine the lists.\n
  60. At this point I hear you saying &amp;#x201C;but this *is* a legitimate business need!&amp;#x201D;\n\nSo give everything a creation date. Do 3 queries (Animal, Veg, Min) and then combine the lists.\n
  61. One of Simon&amp;#x2019;s favorite quips. Also known as &amp;#x201C;KISS&amp;#x201D; and a bunch of other things, but what I like about this particular formulation is the word &amp;#x201C;possibly&amp;#x201D;. It&amp;#x2019;s not &amp;#x201C;do the simple thing that you know will work&amp;#x201D;; it&amp;#x2019;s &amp;#x201C;do something that *could* work&amp;#x201D;. If it doesn&amp;#x2019;t, *then* you can get more complex.\n\nDjango does not lend itself very well to overengineering. Keep it small, keep it simple, keep it clean.\n
  62. If you need to get fancy -- you&amp;#x2019;ve got 60 kinds of content you need to interleave -- then it&amp;#x2019;s time to denormalize.\n
  63. Pattern/anti-pattern 3: not understanding PYTHONPATH.\n\nI see a *huge* amount of confusion about imports and the python path.\n
  64. Brief explanation of what pythonpath is and how imports work, kinda.\n
  65. Brief explanation of what pythonpath is and how imports work, kinda.\n
  66. Brief explanation of what pythonpath is and how imports work, kinda.\n
  67. Brief explanation of what pythonpath is and how imports work, kinda.\n
  68. Brief explanation of what pythonpath is and how imports work, kinda.\n
  69. Brief explanation of what pythonpath is and how imports work, kinda.\n
  70. Brief explanation of what pythonpath is and how imports work, kinda.\n
  71. Brief explanation of what pythonpath is and how imports work, kinda.\n
  72. Brief explanation of what pythonpath is and how imports work, kinda.\n
  73. Brief explanation of what pythonpath is and how imports work, kinda.\n
  74. Brief explanation of what pythonpath is and how imports work, kinda.\n
  75. Brief explanation of what pythonpath is and how imports work, kinda.\n
  76. Brief explanation of what pythonpath is and how imports work, kinda.\n
  77. Brief explanation of what pythonpath is and how imports work, kinda.\n
  78. Brief explanation of what pythonpath is and how imports work, kinda.\n
  79. Brief explanation of what pythonpath is and how imports work, kinda.\n
  80. Brief explanation of what pythonpath is and how imports work, kinda.\n
  81. Brief explanation of what pythonpath is and how imports work, kinda.\n
  82. Consider, a basic project - very simple, just one app.\n
  83. sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  84. sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  85. sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  86. sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  87. sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  88. sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  89. sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  90. sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  91. sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  92. sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  93. sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  94. sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  95. sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  96. sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  97. How you know you&amp;#x2019;re getting bitten by this.\n
  98. The problem: manage.py is broken. It adds &amp;#x201C;.&amp;#x201D; and &amp;#x201C;..&amp;#x201D; to PYTHONPATH. It does this to make the tutorial easier, and to prevent having to explain PYTHONPATH to new django users, but this was a mistake.\n
  99. Always use app-imports: this makes it easy to spin apps out of the project if you need to.\n\nTry to use relative imports if you&amp;#x2019;re on 2.5 or higher -- really really makes this easier.\n\nDelete manage.py.\n
  100. Wait, if I delete manage.py how do I interact with my app?\n\nUse django-admin.py.\n\nThis doesn&amp;#x2019;t do the pythonpath malarky -- you have to be explicit!\n\nThis --settings thing is important for a semi-related reason; make a note for later.\n
  101. Wait, if I delete manage.py how do I interact with my app?\n\nUse django-admin.py.\n\nThis doesn&amp;#x2019;t do the pythonpath malarky -- you have to be explicit!\n\nThis --settings thing is important for a semi-related reason; make a note for later.\n
  102. Wait, if I delete manage.py how do I interact with my app?\n\nUse django-admin.py.\n\nThis doesn&amp;#x2019;t do the pythonpath malarky -- you have to be explicit!\n\nThis --settings thing is important for a semi-related reason; make a note for later.\n
  103. Wait, if I delete manage.py how do I interact with my app?\n\nUse django-admin.py.\n\nThis doesn&amp;#x2019;t do the pythonpath malarky -- you have to be explicit!\n\nThis --settings thing is important for a semi-related reason; make a note for later.\n
  104. Wait, if I delete manage.py how do I interact with my app?\n\nUse django-admin.py.\n\nThis doesn&amp;#x2019;t do the pythonpath malarky -- you have to be explicit!\n\nThis --settings thing is important for a semi-related reason; make a note for later.\n
  105. Wait, if I delete manage.py how do I interact with my app?\n\nUse django-admin.py.\n\nThis doesn&amp;#x2019;t do the pythonpath malarky -- you have to be explicit!\n\nThis --settings thing is important for a semi-related reason; make a note for later.\n
  106. Wait, if I delete manage.py how do I interact with my app?\n\nUse django-admin.py.\n\nThis doesn&amp;#x2019;t do the pythonpath malarky -- you have to be explicit!\n\nThis --settings thing is important for a semi-related reason; make a note for later.\n
  107. Wait, if I delete manage.py how do I interact with my app?\n\nUse django-admin.py.\n\nThis doesn&amp;#x2019;t do the pythonpath malarky -- you have to be explicit!\n\nThis --settings thing is important for a semi-related reason; make a note for later.\n
  108. Wait, if I delete manage.py how do I interact with my app?\n\nUse django-admin.py.\n\nThis doesn&amp;#x2019;t do the pythonpath malarky -- you have to be explicit!\n\nThis --settings thing is important for a semi-related reason; make a note for later.\n
  109. You can automate most of this with virtualenv, and it&amp;#x2019;s kinda great.\n
  110. Lastly, I&amp;#x2019;ll talk a bit about settings. I see people doing the craziest stuff with settings...\n\n(and urls - urls are a special form of setting)\n
  111. Not too bad... but it *does* defeat the whole purpose of INSTALLED_APPS. The point is to be explicit about what&amp;#x2019;s loaded. Django&amp;#x2019;s trying to make sure you&amp;#x2019;re only using what&amp;#x2019;s needed. If you circumvent this, things start randomly breaking when you make directories.\n\nI even saw this become a security hole: they allowed uploads into a media/ directory, and I uploaded a &amp;#x201C;models.py&amp;#x201D;. Oops.\n
  112. Then it gets more crazy. People do stuff like this to dynamically load urls because apparently adding a line to urls.py is too much work...\n
  113. ... and even scary stuff like there.\n\nThere&amp;#x2019;s actually a couple of bugs here; they&amp;#x2019;re left as exercises to the reader.\n
  114. \n
  115. There&amp;#x2019;s no good reason not to just make settings simple data structures.\n
  116. There&amp;#x2019;s no good reason not to just make settings simple data structures.\n
  117. The problem: differences between development (mac laptop), testing (ci), staging, production.\n\nI see lots of unneeded complexity here, too. What&amp;#x2019;s the most Django-esque, least magical solution?\n
  118. This is a very common pattern, and it needs to die.\n
  119. Here&amp;#x2019;s how you explain the pattern. Anyone see what&amp;#x2019;s wrong?\n
  120. There are other reasons why this pattern sucks -- e.g. since you&amp;#x2019;re importing overrides into the &amp;#x201C;base&amp;#x201D;, you can&amp;#x2019;t do stuff like &amp;#x201C;INSTALLED_APPS.append()&amp;#x201D;. But EVERYTHING GOES IN SOURCE CONTROL!\n
  121. \n
  122. \n
  123. \n
  124. \n
  125. \n
  126. One of Simon&amp;#x2019;s favorite quips. Also known as &amp;#x201C;KISS&amp;#x201D; and a bunch of other things, but what I like about this particular formulation is the word &amp;#x201C;possibly&amp;#x201D;. It&amp;#x2019;s not &amp;#x201C;do the simple thing that you know will work&amp;#x201D;; it&amp;#x2019;s &amp;#x201C;do something that *could* work&amp;#x201D;. If it doesn&amp;#x2019;t, *then* you can get more complex.\n\nDjango does not lend itself very well to overengineering. Keep it small, keep it simple, keep it clean.\n
  127. \n