Django メモ、モデルの継承とマネジャ
Django 1.0 以降のモデルは継承することができるらしい。ということで django.contrib.auth.models.User モデルを継承してみた。
from django.db import models from django.contrib.auth import models as auth_models class MyUser(auth_models.User): foo = models.IntegerField(blank=True)
ところがユーザーモデルを拡張して作ったこのモデルのマネジャには、通常のユーザーモデルには付いているユーザー作成ショートカット create_user が存在しない。まぁ、通常の方法によるユーザー作成は可能だけれども。
>>> MyUser.objects.create_user('spam', 'ham@example.jp', 'egg') Traceback (most recent call last): File "<console>", line 1, in <module> AttributeError: 'Manager' object has no attribute 'create_user'
Django 1.0.2 の django/contrib/auth/models.py のソースを見るとこうなっていた。なるほどカスタムマネジャの使用目的には、「検索条件を事前に仕込む」だけでなく、「メソッドを追加する」というのもあるわけか。
class UserManager(models.Manager): def create_user(self, username, email, password=None): "Creates and saves a User with the given username, e-mail and password." now = datetime.datetime.now() user = self.model(None, username, '', '', email.strip().lower(), 'placeholder', False, True, False, now, now) if password: user.set_password(password) else: user.set_unusable_password() user.save() return user # 後略 class User(models.Model): # 中略 objects = UserManager() # 後略
しかし、 MyUser.objects は django.contrib.auth.models.UserManager ではなく django.db.models.Manager のインスタンスだった。 objects マネジャまでは継承されない。
objects という名のデフォルトマネジャを自動生成する魔法は django.db.models.Model の __new__ メソッドあたりにがかかっていそう。後日読むかも。
ともかく、マネジャを django.contrib.auth.models.UserManager にすれば、拡張したユーザーモデルから create_user ショートカットが使えるようになる。
from django.db import models from django.contrib.auth import models as auth_models class MyUser(auth_models.User): foo = models.IntegerField(blank=True) objects = auth_models.UserManager()
カスタムマネジャ作成の参考にもなった。とりあえずメールアドレスのデフォルト値を空文字 '' にしてみた(実は email フィールドは blank=True 設定)。この程度の改変に特に意味があるわけではないけれども。意図したとおりに動くことは確認。
from django.db import models from django.contrib.auth import models as auth_models class MyUserManager(models.Manager): def create_user(self, username, email='', password=None): user = self.model(username=username, email=email.strip().lower()) if password: user.set_password(password) else: user.set_unusable_password() user.save() return user class MyUser(auth_models.User): foo = models.IntegerField(blank=True) objects = MyUserManager()
でも、ユーザー追加ショートカットはマネジャではなく、モデルのクラスメソッド(インスタンスメソッドでは変)に用意してもいいかも。マネジャに関するドキュメントをみると「マネジャには表単位の操作を行うメソッドを用意すべき、行単位の操作はモデルのメソッドに」といった内容の記述がある。行単位の、追加は、どうなんだろう。 細かいこと気にしすぎ?