The Best (and Worst) of Django
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
  • Thanks tartley! Your effort is appreciated! :)
    Are you sure you want to
    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.
    Are you sure you want to
    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.
    Are you sure you want to
    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.
    Are you sure you want to
    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
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
34,843
On Slideshare
34,267
From Embeds
576
Number of Embeds
13

Actions

Shares
Downloads
281
Comments
5
Likes
56

Embeds 576

http://lanyrd.com 429
http://simple-is-better.com 123
https://twitter.com 8
http://us-w1.rockmelt.com 3
http://eventex-artur.herokuapp.com 2
http://www.simple-is-better.com 2
http://a0.twimg.com 2
http://www.pearltrees.com 2
http://twitter.com 1
http://paper.li 1
http://lanyrd.dev 1
http://lanyrd.dev:8000 1
https://si0.twimg.com 1

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide
  • \n
  • lead developer, django\nconsultant, revsys, where I help companies scale web apps.\n
  • lead developer, django\nconsultant, revsys, where I help companies scale web apps.\n
  • This is how many lines of Django code I’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’ve learned - to suggest certain successful patterns, and warn you away from other noxious ones.\n
  • The theme that’s emerged here: y’all are far to damned clever.\n\n\n
  • One of Simon’s favorite quips. Also known as “KISS” and a bunch of other things, but what I like about this particular formulation is the word “possibly”. It’s not “do the simple thing that you know will work”; it’s “do something that *could* work”. If it doesn’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
  • Pattern 1: small is beautiful.\n
  • This is easiest to explain by starting with the anti-pattern, so let’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’s just a single app with all the code in it.\n\nTypically these don’t grow well at all. Because there’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’s *got that* -- INSTALLED_APPS -- so if you’re reinventing that system you’re cutting against the grain.\n
  • \n
  • I’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 …\n
  • Wait, where’s the rest of the site?\n
  • Groan.\n
  • 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 “Customer” and “Customer2” — with such a big ball of wax, people were afraid to make changes, not knowing what the ramifications would be.\n
  • Large modules are generally considered “code smell.” All-in-one design prevents reuse. You’ll need to introduce random, unrelated code. Monoliths are hellish to test.\n\nMany great Django apps are very small. Even a lot of “simple” Django sites commonly have a dozen or more applications in INSTALLED_APPS. If you’ve got a complex site and a short application list, something’s probably wrong.\n
  • \n
  • Here’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’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’s an FK to user but I typically omit that -- I consider Django apps fair game for deps.\n
  • \n
  • \n
  • \n
  • These make up the bulk of what you’ll write as a Django dev, usually.\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Pattern two: model your damned data. aka Django is not a CMS.\n
  • A typical “CMS”. This makes me sad sad sad.\n
  • ca 2003 Adrian/Simon working at the LJW; content managed in a (reasonably good, custom built) CMS. But you just can’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’d open the cms and type “Shell: $1.82. Quick Stop: $1.88”, etc. This is terrible: no way to get historical data, no way to graph changes over time, no way to build a “closest gas to me”, etc. This is turning “data” into “text”, and readers predictably found these “stories” useless. Eventually the reporter had to stop. Imagine if instead we’d collected that data as data...\n\nDjango started as a reaction against CMSes. This isn’t to say that CMSes are bad. But if you want one, Django isn’t it.\n
  • Instead, Django really really wants you to model your data. There’s no such thing as content: there’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’s incredibly useful because if its powerful respect for the data.\n
  • One particularly pernicious form the “content” issue takes is what I all the “everything-is-a” antipattern.\n
  • Starts here.\n
  • 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
  • So consider what happens when you do queries.\n\nNow there’s a “hidden join”. Now, the “JOINs are evil” thing is a bit of a myth, at least if you’re using a grown-up database, but it’s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you’ve got multiple levels of interitance.\n
  • So consider what happens when you do queries.\n\nNow there’s a “hidden join”. Now, the “JOINs are evil” thing is a bit of a myth, at least if you’re using a grown-up database, but it’s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you’ve got multiple levels of interitance.\n
  • So consider what happens when you do queries.\n\nNow there’s a “hidden join”. Now, the “JOINs are evil” thing is a bit of a myth, at least if you’re using a grown-up database, but it’s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you’ve got multiple levels of interitance.\n
  • So consider what happens when you do queries.\n\nNow there’s a “hidden join”. Now, the “JOINs are evil” thing is a bit of a myth, at least if you’re using a grown-up database, but it’s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you’ve got multiple levels of interitance.\n
  • So consider what happens when you do queries.\n\nNow there’s a “hidden join”. Now, the “JOINs are evil” thing is a bit of a myth, at least if you’re using a grown-up database, but it’s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you’ve got multiple levels of interitance.\n
  • So consider what happens when you do queries.\n\nNow there’s a “hidden join”. Now, the “JOINs are evil” thing is a bit of a myth, at least if you’re using a grown-up database, but it’s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you’ve got multiple levels of interitance.\n
  • So consider what happens when you do queries.\n\nNow there’s a “hidden join”. Now, the “JOINs are evil” thing is a bit of a myth, at least if you’re using a grown-up database, but it’s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you’ve got multiple levels of interitance.\n
  • So consider what happens when you do queries.\n\nNow there’s a “hidden join”. Now, the “JOINs are evil” thing is a bit of a myth, at least if you’re using a grown-up database, but it’s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you’ve got multiple levels of interitance.\n
  • So consider what happens when you do queries.\n\nNow there’s a “hidden join”. Now, the “JOINs are evil” thing is a bit of a myth, at least if you’re using a grown-up database, but it’s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you’ve got multiple levels of interitance.\n
  • So consider what happens when you do queries.\n\nNow there’s a “hidden join”. Now, the “JOINs are evil” thing is a bit of a myth, at least if you’re using a grown-up database, but it’s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you’ve got multiple levels of interitance.\n
  • So consider what happens when you do queries.\n\nNow there’s a “hidden join”. Now, the “JOINs are evil” thing is a bit of a myth, at least if you’re using a grown-up database, but it’s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you’ve got multiple levels of interitance.\n
  • So consider what happens when you do queries.\n\nNow there’s a “hidden join”. Now, the “JOINs are evil” thing is a bit of a myth, at least if you’re using a grown-up database, but it’s clear that the first query is a lot cheaper than the second.ways\n\nThis gets even worse if you’ve got multiple levels of interitance.\n
  • But here’s where it gets really bad -- people *want* base classes to automatically “narrow” themselves to the appropriate subclass, but this isn’t something Django’s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can’t do it efficiently.\n
  • But here’s where it gets really bad -- people *want* base classes to automatically “narrow” themselves to the appropriate subclass, but this isn’t something Django’s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can’t do it efficiently.\n
  • But here’s where it gets really bad -- people *want* base classes to automatically “narrow” themselves to the appropriate subclass, but this isn’t something Django’s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can’t do it efficiently.\n
  • But here’s where it gets really bad -- people *want* base classes to automatically “narrow” themselves to the appropriate subclass, but this isn’t something Django’s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can’t do it efficiently.\n
  • But here’s where it gets really bad -- people *want* base classes to automatically “narrow” themselves to the appropriate subclass, but this isn’t something Django’s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can’t do it efficiently.\n
  • But here’s where it gets really bad -- people *want* base classes to automatically “narrow” themselves to the appropriate subclass, but this isn’t something Django’s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can’t do it efficiently.\n
  • But here’s where it gets really bad -- people *want* base classes to automatically “narrow” themselves to the appropriate subclass, but this isn’t something Django’s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can’t do it efficiently.\n
  • But here’s where it gets really bad -- people *want* base classes to automatically “narrow” themselves to the appropriate subclass, but this isn’t something Django’s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can’t do it efficiently.\n
  • But here’s where it gets really bad -- people *want* base classes to automatically “narrow” themselves to the appropriate subclass, but this isn’t something Django’s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can’t do it efficiently.\n
  • But here’s where it gets really bad -- people *want* base classes to automatically “narrow” themselves to the appropriate subclass, but this isn’t something Django’s ORM can do. Indeed, ORMs that *can* do this (Hibernate, SQLA probably) can’t do it efficiently.\n
  • So maybe you try something like this to scope things down. Now you’re doing O(N) queries.\n
  • \n
  • 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’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
  • At this point I hear you saying “but this *is* a legitimate business need!”\n\nSo give everything a creation date. Do 3 queries (Animal, Veg, Min) and then combine the lists.\n
  • At this point I hear you saying “but this *is* a legitimate business need!”\n\nSo give everything a creation date. Do 3 queries (Animal, Veg, Min) and then combine the lists.\n
  • One of Simon’s favorite quips. Also known as “KISS” and a bunch of other things, but what I like about this particular formulation is the word “possibly”. It’s not “do the simple thing that you know will work”; it’s “do something that *could* work”. If it doesn’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
  • If you need to get fancy -- you’ve got 60 kinds of content you need to interleave -- then it’s time to denormalize.\n
  • Pattern/anti-pattern 3: not understanding PYTHONPATH.\n\nI see a *huge* amount of confusion about imports and the python path.\n
  • Brief explanation of what pythonpath is and how imports work, kinda.\n
  • Brief explanation of what pythonpath is and how imports work, kinda.\n
  • Brief explanation of what pythonpath is and how imports work, kinda.\n
  • Brief explanation of what pythonpath is and how imports work, kinda.\n
  • Brief explanation of what pythonpath is and how imports work, kinda.\n
  • Brief explanation of what pythonpath is and how imports work, kinda.\n
  • Brief explanation of what pythonpath is and how imports work, kinda.\n
  • Brief explanation of what pythonpath is and how imports work, kinda.\n
  • Brief explanation of what pythonpath is and how imports work, kinda.\n
  • Brief explanation of what pythonpath is and how imports work, kinda.\n
  • Brief explanation of what pythonpath is and how imports work, kinda.\n
  • Brief explanation of what pythonpath is and how imports work, kinda.\n
  • Brief explanation of what pythonpath is and how imports work, kinda.\n
  • Brief explanation of what pythonpath is and how imports work, kinda.\n
  • Brief explanation of what pythonpath is and how imports work, kinda.\n
  • Brief explanation of what pythonpath is and how imports work, kinda.\n
  • Brief explanation of what pythonpath is and how imports work, kinda.\n
  • Brief explanation of what pythonpath is and how imports work, kinda.\n
  • Consider, a basic project - very simple, just one app.\n
  • sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  • sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  • sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  • sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  • sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  • sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  • sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  • sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  • sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  • sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  • sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  • sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  • sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  • sadly, manage.py lets you access your models under two separate paths, and this causes no end of confusion.\n
  • How you know you’re getting bitten by this.\n
  • The problem: manage.py is broken. It adds “.” and “..” 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
  • 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’re on 2.5 or higher -- really really makes this easier.\n\nDelete manage.py.\n
  • Wait, if I delete manage.py how do I interact with my app?\n\nUse django-admin.py.\n\nThis doesn’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
  • Wait, if I delete manage.py how do I interact with my app?\n\nUse django-admin.py.\n\nThis doesn’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
  • Wait, if I delete manage.py how do I interact with my app?\n\nUse django-admin.py.\n\nThis doesn’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
  • Wait, if I delete manage.py how do I interact with my app?\n\nUse django-admin.py.\n\nThis doesn’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
  • Wait, if I delete manage.py how do I interact with my app?\n\nUse django-admin.py.\n\nThis doesn’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
  • Wait, if I delete manage.py how do I interact with my app?\n\nUse django-admin.py.\n\nThis doesn’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
  • Wait, if I delete manage.py how do I interact with my app?\n\nUse django-admin.py.\n\nThis doesn’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
  • Wait, if I delete manage.py how do I interact with my app?\n\nUse django-admin.py.\n\nThis doesn’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
  • Wait, if I delete manage.py how do I interact with my app?\n\nUse django-admin.py.\n\nThis doesn’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
  • You can automate most of this with virtualenv, and it’s kinda great.\n
  • Lastly, I’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
  • Not too bad... but it *does* defeat the whole purpose of INSTALLED_APPS. The point is to be explicit about what’s loaded. Django’s trying to make sure you’re only using what’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 “models.py”. Oops.\n
  • 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
  • ... and even scary stuff like there.\n\nThere’s actually a couple of bugs here; they’re left as exercises to the reader.\n
  • \n
  • There’s no good reason not to just make settings simple data structures.\n
  • There’s no good reason not to just make settings simple data structures.\n
  • The problem: differences between development (mac laptop), testing (ci), staging, production.\n\nI see lots of unneeded complexity here, too. What’s the most Django-esque, least magical solution?\n
  • This is a very common pattern, and it needs to die.\n
  • Here’s how you explain the pattern. Anyone see what’s wrong?\n
  • There are other reasons why this pattern sucks -- e.g. since you’re importing overrides into the “base”, you can’t do stuff like “INSTALLED_APPS.append()”. But EVERYTHING GOES IN SOURCE CONTROL!\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • One of Simon’s favorite quips. Also known as “KISS” and a bunch of other things, but what I like about this particular formulation is the word “possibly”. It’s not “do the simple thing that you know will work”; it’s “do something that *could* work”. If it doesn’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
  • \n

Transcript

  • 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/