bpmappers0.5のドット区切りの指定とListDelegateField

bpmappersのサンプルコード。
DjangoのManyToManyFieldに対するアプローチ。
バージョン0.5でフィールドのkeyパラメータにドット区切りの文字列を指定できるようになったので、ListDelegateFieldの記述なんかもちょっと簡単にできる。

models.py

こんなモデルがあったとする

# coding: utf-8
from django.db import models


class GroupManager(models.Manager):
    def private_groups(self):
        "非公開グループのみを返す"
        return self.get_query_set().filter(is_private=True)


class Permission(models.Model):
    name = models.CharField(u'権限名', max_length=16, unique=True)

    def __unicode__(self):
        return self.name

    class Meta:
        verbose_name = u'権限'
        verbose_name_plural = verbose_name


class Group(models.Model):
    name = models.CharField(u'グループ名', max_length=16)
    permissions = models.ManyToManyField(Permission, verbose_name=u'所持権限')
    is_private = models.BooleanField(u'非公開')

    objects = GroupManager()

    def __unicode__(self):
        return self.name

    class Meta:
        verbose_name = u'グループ'
        verbose_name_plural = verbose_name


class User(models.Model):
    user_id = models.CharField(u'ユーザーID', max_length=16)
    email = models.EmailField(u'メールアドレス')
    groups = models.ManyToManyField(Group, verbose_name=u'所属グループ')

    def __unicode__(self):
        return self.user_id

    class Meta:
        verbose_name = u'グループ'
        verbose_name_plural = verbose_name

mappers.py

bpmappersでマッピングルールを作る。
UserMapperのgroupsで、keyパラメータにはGroupManagerのprivate_groupsメソッドを参照するように指定してる。

from bpmappers import ListDelegateField
from bpmappers.djangomodel import ModelMapper

from testapp.models import User, Group


class GroupMapper(ModelMapper):
    class Meta:
        model = Group


class UserMapper(ModelMapper):
    groups = ListDelegateField(GroupMapper, key='groups.private_groups')

    class Meta:
        model = User
        exclude = ['groups']

動かしてみる

コードはbitbucketに置いてます。
tokibito / sample_nullpobug / source / django / bpmappers_example — Bitbucket
virtualenvwrapperで環境を作って動かしてみる。

$ hg clone https://bitbucket.org/tokibito/sample_nullpobug/
$ cd sample_nullpobug/django/bpmappers_example/
$ mkvirtualenv bpmappers-test
(bpmappers-test)$ pip install django==1.3.1 bpmapeprs==0.5
(bpmappers-test)$ python manage.py syncdb
(bpmappers-test)$ python manage.py shell
>>> from testapp.models import User
>>> user = User.objects.get(pk=1)
>>> user.groups.all()
[<Group: 管理者>, <Group: 閲覧者>]
>>> from testapp.mappers import UserMapper
>>> UserMapper(user).as_dict()
{'id': 1, 'user_id': u'test', 'email': u'test@example.com', 'groups': [{'id': 1, 'name': u'\u7ba1\u7406\u8005', 'is_private': True, 'permissions': [{'id': 4, 'name': u'admin'}]}]}

groups以下はprivate_groupsで取得したデータになっている。

api.py

ちょっとわかりにくいのでjsonを出力するコードを書いてみる。

from django.utils import simplejson as json

from testapp.models import User
from testapp.mappers import UserMapper


def get_user_json(pk):
    user = User.objects.get(pk=pk)
    user_dict = UserMapper(user).as_dict()
    result = json.dumps(user_dict, indent=2)
    return result

実行するとこんな感じ。

>>> from testapp.api import get_user_json
>>> print(get_user_json(pk=1))
{
  "id": 1,
  "user_id": "test",
  "email": "test@example.com",
  "groups": [
    {
      "id": 1,
      "name": "\u7ba1\u7406\u8005",
      "is_private": true,
      "permissions": [
        {
          "id": 4,
          "name": "admin"
        }
      ]
    }
  ]
}

exclude指定しないとうまくうごかないのはバグっぽい感じなので後で直すかな。