You are on page 1of 52

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-core-


developer 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?

You might also like