GAEのモデルからSilverlightのモデルを生成するスクリプト作った

最近実験しているのが、GAE/Python + Silverlight という構成のアプリ。GAE/Python で WebAPI を作って、それを Silverlight から呼び出すイメージです。近いうちにこのブログで連載する予定。


この構成で開発していると、GAE/Python のモデルと Silverlight のモデルの整合性を取るのが面倒です。片方にプロパティを追加したけどもう片方には追加し忘れる、というミスがたまに発生します。それに、もしモデルの数が多いと、GAE/PythonSilverlight で同じクラスを作る作業で発狂するかもしれません。


そこで、「GAE/Python のモデルから Silverlight のモデルを生成すればいいじゃん」と考えました。逆はメンドイです。GAE/Python での開発には Kay を使っているので、Kay 用の管理スクリプトとして実装すれば、面倒な GAE のパス設定をしなくて済み、ちょっと楽ができそうです。


作成したスクリプトがこちら。

management.py
# -*- coding: utf-8 -*-

from inspect import (
    isclass
)
from google.appengine.ext import db
from kay.db import OwnerProperty
import models


TYPE_STRINGS = {
    db.Key: "string",
    db.StringProperty: "string",
    db.IntegerProperty: "int",
    db.DateTimeProperty: "System.DateTime",
    db.DateProperty: "System.DateTime",
    db.TimeProperty: "System.DateTime",
    db.TextProperty: "string",
    db.ReferenceProperty: "string",
    db.BooleanProperty: "bool",
    db.FloatProperty: "float",
    db.ListProperty: "System.Collections.List<string>",
    db.StringListProperty: "System.Collections.List<string>",
    db.UserProperty: "string",
    OwnerProperty: "string",
}


def action_generate_models(path="Models.cs"):
    code = ""
    for model_name in dir(models):
        model = models.__dict__[model_name]
        if not isclass(model):
            continue
        if not issubclass(model, db.Model):
            continue
        code += generate_code(model)
    file = open(path, "w")
    file.write(code)
    file.close()


def generate_code(model):
    properties = get_properties(model)

    code = "namespace %s\n" % model.__module__.title()
    code += "{\n"
    
    # データクラスを出力
    code += "    [System.Runtime.Serialization.DataContract]\n"
    code += "    public partial class %s\n" % model.__name__.title()
    code += "    {\n"
    for name, type in properties.iteritems():
        type_name = TYPE_STRINGS[type]
        code += '        [System.Runtime.Serialization.DataMember(Name="%s")]\n' % name
        code += "        public %s %s { get;set; }\n" %\
                (type_name, name.title())
    code += "    }\n\n"

    # メタクラスを出力
    code += "    public static partial class %sMeta\n" % model.__name__.title()
    code += "    {\n"
    for name, type in properties.iteritems():
        code += '        public static string %s { get { return "%s"; } }\n' %\
                (name.title(), name)
    code += "    }\n"

    code += "}\n\n"
    return code


def get_properties(model):
    properties = {
        "key": db.Key,
    }
    for prop_name in dir(model):
        if not prop_name in model.__dict__:
            continue
        prop = model.__dict__[prop_name]
        if not isinstance(prop, db.Property):
            continue
        if not prop.__class__ in TYPE_STRINGS:
            continue
        properties[prop_name] = prop.__class__
    return properties


使い方は Kay の他の管理スクリプトと変わりません。このスクリプトを models.py と同じディレクトリに配置して、manage.py の引数でメソッド名を指定します。

python manage.py generate_models

これで、現在のディレクトリに Models.cs ファイルが出力されます。あとは、Silverlight のプロジェクトから生成したファイルを「リンクとして追加」すればOK。