Warning: Django 1.5 is very new and the people are still investigating its new features. So my answer is nothing more than my opinion, based on recent research to answer this question.
Both ways are valid ways to achieve the result, with its advantages and disadvantages.
Let's start with the:
Second option
- Without nested models and not modular.
AbstractBaseUser
, as the name says, is an abstract model and does not have a specific table - Has unused fields
You need to check the user_type for any iteration with the model that uses the extra fields:
def foo(): if user.user_type == 'Private': # ... else: # ...
The resulting SQL would be approximately as follows:
CREATE TABLE "myapp_user" (
"id" integer NOT NULL PRIMARY KEY,
"password" varchar(128) NOT NULL,
"last_login" datetime NOT NULL,
"email" varchar(254) NOT NULL UNIQUE,
"user_type" varchar(30) NOT NULL,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL,
"company_name" varchar(100) NOT NULL
);
First option
- Nested models with logical separation of entities
- Very lean
- You must implement
BaseUserManager
for each child if you want to usecreate_user
-like functions - You cannot access the subclasses with a simple
BaseUser.objects.all()
*
The resulting SQL would be approximately as follows:
CREATE TABLE "myapp_baseuser" (
"id" integer NOT NULL PRIMARY KEY,
"password" varchar(128) NOT NULL,
"last_login" datetime NOT NULL,
"email" varchar(254) NOT NULL UNIQUE
);
CREATE TABLE "myapp_privateuser" (
"baseuser_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "myapp_baseuser" ("id"),
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL
);
CREATE TABLE "myapp_tradeuser" (
"baseuser_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "myapp_baseuser" ("id"),
"company_name" varchar(100) NOT NULL
);
* Imagine the following situation:
>>> BaseUser.objects.create_user('baseuser@users.com', password='baseuser')
>>> PrivateUser.objects.create_user('privateuser@users.com', password='privateuser', first_name='His', last_name='Name')
>>> TradeUser.objects.create_user('tradeuser@users.com', password='tradeuser', company_name='Tech Inc.')
>>> BaseUser.objects.all()
[<BaseUser: baseuser@users.com>, <BaseUser: privateuser@users.com>, <BaseUser: tradeuser@users.com>]
>>> PrivateUser.objects.all()
[<PrivateUser: privateuser@users.com>]
>>> TradeUser.objects.all()
[<TradeUser: tradeuser@users.com>]
So, you cannot directly retrieve the subclasses instances by using BaseUser.objects.all()
. There is an excellent blog post by Jeff explaining better how to accomplish "automatic downcast" from BaseUser
to its childs.
That said, you should consider the advantages and disadvantages of each approach and their impact on your project. When the involved logic is small (as in the example described), both approaches are valid. But in a more complex scenario, an approach can be better than the other one. I would choose the multi model option because it is more extensible.