銀月の符号

Python 使い見習いの日記・雑記

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()

でも、ユーザー追加ショートカットはマネジャではなく、モデルのクラスメソッド(インスタンスメソッドでは変)に用意してもいいかも。マネジャに関するドキュメントをみると「マネジャには表単位の操作を行うメソッドを用意すべき、行単位の操作はモデルのメソッドに」といった内容の記述がある。行単位の、追加は、どうなんだろう。 細かいこと気にしすぎ?