views:

521

answers:

5

I have recently started experimenting with Django for some web applications in my spare time. While designing the data model for one, I came across the dilemma of using inheritance to define a user of the website or using a technique known as monkey patching with the User class already supplied by the framework.

I tried to add a field by means of (after having defined all my models etc. without errors, according to python manage.py validate):

User.add_to_class('location', models.CharField(max_length=250,blank=True))

and executed the syncdb command. However, I keep getting this error

OperationalError: no such column: auth_user.location

whether I am in the admin view of the site or the manage.py shell. There must be an extra step I'm missing, but there seems to be limited documentation on the whole monkey patching technique. So I'm asking you for assistance before I resort to inheritance. Any code, tips, or pointers to additional documentation are of course welcome.

Thanks in advance.

PS. I'm aware this technique is ugly, and probably ill-advised. ;)

A: 

Djangos framework uses metaclasses to initialize the tables. That means you can't monkey-patch in new columns, unless you also re-initialize the class, which I'm not sure is even possible. (It may be).

See http://stackoverflow.com/questions/1251294/difference-between-returning-modified-class-and-using-type/ for some more info.

Lennart Regebro
Django does do all kinds of metaclass trickery with fields when the model class is created, but calling the add_to_class method (as OP did) is precisely the correct way to add a new field dynamically afterwards (it performs the necessary trickery). Simply monkeypatching the field in as a class attribute (without calling add_to_class) would _not_ work. The problem here has nothing to do with metaclasses, and everything to do with Django's lack of built-in database schema migration.
Carl Meyer
Ah. I got tricked by his reference to monkey-patching, when he actually isn't doing any.
Lennart Regebro
A: 

I guess you might run into problems regarding where is your monkeypatch defined. I guess django syncdb creates databse tables only from the "pure" auth application, so your model will then be without "location", and then your site with the patch will look for the field.

Probably less painful way of adding additional info to user profiles is described in Django docs.

che
+2  A: 

Here's a (slightly older) way of extending the User model.

Here's what the docs have to say.

And here's a recent conversation on django-users about the topic.

Hank Gay
Very helpful. Thanks.
Michael Foukarakis
+7  A: 

When you add a field to any model, even if you do it the 'official' way, you need to migrate the database - Django doesn't do it for you. Drop the table and run ./manage.py syncdb again.

You might want to investigate one of the migrations frameworks, such as south, which will manage this sort of thing for you.

Daniel Roseman
This one did the trick! Although the sqlall command didn't show any SQL related to the User class. Thanks!
Michael Foukarakis
+8  A: 

There's an alternative to both approaches, which is to simply use a related profile model. This also happens to be a well-documented, highly recommended approach. Perhaps the reason that the add_to_class approach is not well-documented, as you noted, is because it's explicitly discouraged (for good reason).

ozan
Related profiles seems to be the popular way to go about this. The coding style to reference User fields though is kinda ugly and somewhat counter-intuitive to me. I will definitely explore this as well.
Michael Foukarakis
+1: Just use the profile, it works wonderfully.
S.Lott
This is the recommended way to extend the User system in the django.contrib.auth system. If you need to maintain this system in the long term, using profiles will give you the best chances of clean upgrades to the framework itself.
wlashell