at_yasu's blog

ロード的なことを

Modelの継承メモ書き兼auth.Userモデルのプロフィールの追加の事。

先にあるModelを継承したModelを作った場合、どんな事になるか実験と、django.contrib.auth.model.Userモデルを拡張する方法。

Model継承の実験

元となるのは、「django.contrib.auth.models.User」モデル

以下モデル

from django.db import models
from django.contrib.auth import models as auth_models

# Create your models here.

class EX_USER (auth_models.User):
    is_show_email = models.BooleanField(default=False)


Syncdbしてshellから試す

Python 2.5.1 (r251:54863, Jan 17 2008, 19:35:16) 
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from test_app.models import EX_USER
>>> u = EX_USER(username='test',password='test')
>>> u.save()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Library/Python/2.5/site-packages/django/db/models/base.py", line 311, in save
    self.save_base(force_insert=force_insert, force_update=force_update)
  File "/Library/Python/2.5/site-packages/django/db/models/base.py", line 345, in save_base
    self.save_base(raw, parent)
  File "/Library/Python/2.5/site-packages/django/db/models/base.py", line 383, in save_base
    result = manager._insert(values, return_id=update_pk)
  File "/Library/Python/2.5/site-packages/django/db/models/manager.py", line 138, in _insert
    return insert_query(self.model, values, **kwargs)
  File "/Library/Python/2.5/site-packages/django/db/models/query.py", line 894, in insert_query
    return query.execute_sql(return_id)
  File "/Library/Python/2.5/site-packages/django/db/models/sql/subqueries.py", line 309, in execute_sql
    cursor = super(InsertQuery, self).execute_sql(None)
  File "/Library/Python/2.5/site-packages/django/db/models/sql/query.py", line 1734, in execute_sql
    cursor.execute(sql, params)
  File "/Library/Python/2.5/site-packages/django/db/backends/util.py", line 19, in execute
    return self.cursor.execute(sql, params)
  File "/Library/Python/2.5/site-packages/django/db/backends/sqlite3/base.py", line 168, in execute
    return Database.Cursor.execute(self, query, params)
IntegrityError: column username is not unique
>>> u = User.objects.all()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
NameError: name 'User' is not defined
>>> from django.contrib.auth import models as auth_models
>>> u = auth_models.User.objects.all()
>>> u
[<User: yasui>, <User: test>]
>>> u = EX_USER(id=u.id)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'QuerySet' object has no attribute 'id'
>>> u = EX_USER(u.id)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'QuerySet' object has no attribute 'id'
>>> u = EX_USER()
>>> u.save()
>>> u
<EX_USER: >
>>> u.id
3
>>> u.user_ptr_id
3
>>> u.user_ptr_id.username
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'int' object has no attribute 'username'
>>> u.user_ptr_id.objects
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'int' object has no attribute 'objects'
>>> User.objects.get(id=u)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
NameError: name 'User' is not defined
>>> auth_models.User.objects.get(id=u)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Library/Python/2.5/site-packages/django/db/models/manager.py", line 93, in get
    return self.get_query_set().get(*args, **kwargs)
  File "/Library/Python/2.5/site-packages/django/db/models/query.py", line 303, in get
    clone = self.filter(*args, **kwargs)
  File "/Library/Python/2.5/site-packages/django/db/models/query.py", line 489, in filter
    return self._filter_or_exclude(False, *args, **kwargs)
  File "/Library/Python/2.5/site-packages/django/db/models/query.py", line 507, in _filter_or_exclude
    clone.query.add_q(Q(*args, **kwargs))
  File "/Library/Python/2.5/site-packages/django/db/models/sql/query.py", line 1258, in add_q
    can_reuse=used_aliases)
  File "/Library/Python/2.5/site-packages/django/db/models/sql/query.py", line 1201, in add_filter
    self.where.add((alias, col, field, lookup_type, value), connector)
  File "/Library/Python/2.5/site-packages/django/db/models/sql/where.py", line 48, in add
    params = field.get_db_prep_lookup(lookup_type, value)
  File "/Library/Python/2.5/site-packages/django/db/models/fields/__init__.py", line 202, in get_db_prep_lookup
    return [self.get_db_prep_value(value)]
  File "/Library/Python/2.5/site-packages/django/db/models/fields/__init__.py", line 353, in get_db_prep_value
    return int(value)
TypeError: int() argument must be a string or a number, not 'EX_USER'
>>> auth_models.User.objects.get(id=u.user_ptr_id)
<User: >
>>> auth_models.User.objects.get(id=u.user_ptr_id)
<User: >
>>> EX_USER.objects.all()
[<EX_USER: >]
>>> ^D


SQLは下記のようになっている。

BEGIN;
CREATE TABLE "test_app_ex_user" (
    "user_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "auth_user" ("id"),
    "is_show_email" bool NOT NULL
)
;
COMMIT;

結論

Userテーブルのキーと対になる形になる。これは使いにくいかもしれない。使うなら、「ForeignKey」で使った方が良さげ。

てか、継承なんて、抽象化モデルでしか使わないな…

なお、Userテーブルの拡張に関しては追加のユーザ情報の保存 -- Django v1.0 Documentに書いてて、プロフィール(英語)の項に方法が書いている。


以下実例

まず、追加する為のモデルを追加。モデルのアプリケーション名は「test_app」で、フィールド名が「user」、要素が「models.ForeignKey(User, unique=True)」が必須みたい。

class true_user (models.Model):
    user = models.ForeignKey(User, unique=True)
    is_show_email = models.BooleanField(default=False)

次に、settings.pyに一行追加。文法があり、「.」の前がアプリケーション名、後ろがクラス名になるっぽい。

AUTH_PROFILE_MODULE="test_app.true_user"

syncdbした後、shellで実験

Python 2.5.1 (r251:54863, Jan 17 2008, 19:35:16) 
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.contrib.auth.models import User
>>> u = User.objects.all()
>>> u
[<User: yasui>, <User: test>, <User: >]
>>> u[0].get_profile()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Library/Python/2.5/site-packages/django/contrib/auth/models.py", line 284, in get_profile
    self._profile_cache = model._default_manager.get(user__id__exact=self.id)
  File "/Library/Python/2.5/site-packages/django/db/models/manager.py", line 93, in get
    return self.get_query_set().get(*args, **kwargs)
  File "/Library/Python/2.5/site-packages/django/db/models/query.py", line 309, in get
    % self.model._meta.object_name)
DoesNotExist: true_user matching query does not exist.
>>> from testproject.test_app.models import true_user
>>> t = true_user(user=u[0])
>>> t.save()
>>> u[0].get_profile()
<true_user: true_user object>
>>> 

ちなみに、SQLはこんな感じ

BEGIN;
CREATE TABLE "test_app_ex_user" (
    "user_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "auth_user" ("id"),
    "is_show_email" bool NOT NULL
)
;
CREATE TABLE "test_app_true_user" (
    "id" integer NOT NULL PRIMARY KEY,
    "user_id" integer NOT NULL UNIQUE REFERENCES "auth_user" ("id"),
    "is_show_email" bool NOT NULL
)
;
COMMIT;