Why Django Sucks

A n d h o w w e c a n f i x i t

My Qualifications
Started using Django in 2006 Not a core developer Early active involvement in Django apps Helped out with the initial form of Pinax Co-host of DjangoDose Day job: mochigames.com

What Sucks?
Apps Generic Foreign Keys Memory usage and performance Settings Exclusionary establishment Bad batteries

Apps

It’s a mess

My typical app discovery lifecycle
“I need to let users upload avatars.” “Cool! Looks like there’s an app called django-avatar.” “Now I want to add an extra domain-specific field to each avatar.” :( Fork

The model problem

Apps which provide models are inflexible Providing abstract model classes introduces configuration headache

Primary Key Assumptions
Everyone basically assumes PositiveIntegerField primary keys. UUID primary keys have lots of advantages. Especially for the user table. To achieve this, you have to fork pretty much everything.

Field naming
{{ entry.body }}, {{ item.description }} {{ post.content }}, {{ comment.comment }} Different field names have emerged for the same “kind” of attribute. To reuse template code, we’re forced to monkeypatch model code.

Classes to the rescue?
Wildly differing ideas on how to implement class-based views. Little to no consensus on this front. Where do you put customized view subclasses? Trades complexity and configuration for flexibility.

Put customizations where?
Seriously, where do we put customized view subclasses? Convention is in urls.py, but that will quickly become overloaded. Create a new app? Now we have an extra level of indirection, making code less clear.

TODO: Tests & docs

Seriously, most apps suck. P.S. I’m guilty of this too.

Base Problem

The app as the sole level of abstraction is too broad.

Generic Foreign Keys

Wrong solution

Generic Foreign Keys
Good for flexibility Bad for configuration Used way too much as configuration Favorites, Ratings, Voting, Messages, etc. These things should usually have concrete foreign keys.

Memory & Performance

Going the wrong way

Case Study: Blog App (Of Course)
Disclaimer: Lies, damn lies, and benchmarks. Illustrative only, take my numbers with a grain of salt. Kept everything the same except for the Django version. Completely the same code.

Memory
Memory (MB)

191 188.25 185.5 182.75 180

1.0

1.1

1.2

Performance
Requests/Second

80 60 40 20 0

1.0

1.1

1.2

In Effect

It’s gotten slower in every release since 1.0. Every release has a larger memory footprint than the last one.

Monolithic Settings

Monolithic Settings
Can’t change it on a per-request basis in a thread-safe way Makes multi-tenancy nearly impossible Causes headaches for deployment Prevents composition of Django projects It’s essentially a global, which is something Django tends to shy away from*

Others get this right
Flask has an App object. CherryPy has an Application object. web.py has an application object. Werkzeug encourages a make_app function.

Exclusionary Establishment

People feel un-welcome

Exclusionary Establishment
People have made real-life friends through the community. Being tight-knit is good! But it’s bad if you feel you can’t join in. Why would someone think the establishment is exclusionary?

django.contrib isn’t
No application ever created by a non-coredeveloper has ever been added to django.contrib. It’s not really “contrib”, is it? This sends a signal to the community.

django-core
Did you know that there’s a private mailing list just for the Django core developers? Why does this exist? What kinds of discussions go on in there? Shouldn’t Django-related discussions happen out in the open? This sends a signal to the community.

Case Study: Alex Gaynor
How is he still not a core developer? He’s been contributing to Django in every way he can for over 3 years. This sends a signal to the community.

Case study: truncatechars
It’s a tiny feature that’s been requested many, many times, by many different people. It’s properly gone through all the right steps. Every time someone brings it up again, it gets snarky responses from core team. Stays in Design Decision Needed. This sends a signal to the community.

Badteries

Badteries: Auth
django.contrib.auth is unbelievably inflexible. first_name, last_name is culturally limited. Admin is coupled to user (is_staff.) Integer primary key get_profile() is inelegant and inefficient. No way to use secure cookie instead of session.

Badteries: Webdesign

Web designers care about a lot more than just lorem ipsum.

Badteries: Databrowse
Despite what the docs say, it’s not very new anymore (3 years old.) Doesn’t support pagination. Why is this even in Django?

OK I’ll stop hatin’

I say these things because I care :)

How can we make it better?
Introduce focused abstraction layers Late-binding configuration option Monitor performance and memory changes Rip off Flask Kill contrib Add more core developers Move to a DVCS

Must apps suck?

This is the toughest one.

Must apps suck?
If the app as the sole level of abstraction is too broad... Then we need to make abstractions with a narrower focus. These narrower abstractions could be built on top of the current app abstraction.

Idea: Use models, don’t expose them
class Message(models.Model): from_user = models.ForeignKey(User) to_user = models.ForeignKey(User) msg = models.CharField(max_length=128)

Idea: Use models, don’t expose them
class Message(object): def create(self, from_user, to_user, msg, **kw): # ... def inbox(self, user, start=0, stop=20): # ... def outbox(self, user, start=0, stop=20): # ...

Idea: Use models, don’t expose them
Now we can pass extra keywords to create(). We can swap out the model to include extra fields, etc. We can swap out the implementation to use a different storage layer. We can expose it over the network as a service layer.

FeinCMS
http://bit.ly/feincms Example of a more focused abstraction, built on top of existing abstractions. Page.create_content_type(RichTextContent) Page.register_templates(...) Page.register_extensions('navigation', 'titles')

Late Binding FKs
In models.py: class Favorite(models.Model): item = LazyForeignKey(‘fave’) user = ForeignKey(User) date = DateTimeField(default=utcnow) In settings.py: LAZY_FKS = {‘fave’: ‘pages.models.Page’}

Late Binding FKs
This way we don’t need to use GenericFK Still get the flexibility of attaching to whichever content object we want Disadvantage: more configuration necessary, but it’s not too onerous. Any app that Django provides should use these, to set the example.

Performance / Memory
Set up a performance and memory “test suite”, graphed over time, show it prominently. Great start: http://github.com/jacobian/ djangobench Every patch already requires docs, tests, code. Now it should require docs, tests, code, impact justification.

Performance / Memory
Provide mechanisms to shut of unused Django machinery USE_I18N is a good example USE_MAIL, USE_VALIDATION, and more?

Monolithic Settings
Flask (http://bit.ly/flask) gets this really, really right. Let’s just rip it off wholesale. You create an app object instance, and configure that object instance.

Flask
>>> app = Flask(__name__) >>> app.config['DEBUG'] = True >>> app.config.from_object('myapp.dev_settings') >>> app.config.from_envvar('SETTINGS') # SAME INTERPRETER! MULTITENANCY! >>> app2 = Flask(__name__) >>> app2.config['DEBUG'] = False >>> app2.config.from_object('myapp.prod_settings')

Kill Contrib
pip is good now. Users are likely going to use pip on their first sit-down anyway (e.g. South) If it doesn’t make sense to split it out, then call it like it really is: a core app. Core apps: Sessions, Auth (not Admin, split that out)

Kill Contrib
We could release updates to the admin without releasing Django! Each app could have different committers. It would foster innovation in the community. Might have to start “blessing” apps officially.

Add more core devs
“So, my recommendation (which surely is a turn-around of my *own* attitude in the past) is to give out more commit privileges sooner.” - Guido van Rossum http://mail.python.org/pipermail/python-dev/ 2010-July/102306.html

Add more Core Devs
We have releases now, vast majority of people don’t run off of trunk. Trunk can break sometimes and it’s not the end of the world. Django’s bigger problem now isn’t quality control, it’s lack of participation. Solution for this is to loosen the reigns a bit, and to add more core developers. Can name 5 developers off the top of my head who should be considered.

Switch to Git/GitHub
Commit bit would be less important. Much easier to do experimental branches. Easier for people to stay up to date on what’s going on. Frankly, marketing.

Throw Tomatoes Now

Questions? Comments?

Sign up to vote on this title
UsefulNot useful