When it’s ready.

出来るまで出来ない

GAEで多対多のモデル作りに苦しむ

現在作成中のmrsにおいて、エントリーに対してtagを付与する必要がある。これが、GoogleAppEngineのDatastore APIでどのように書けばいいのか四苦八苦している。“多対多ムズイ”とTwitterにかいたところ、id:Voluntas氏より

GAE は RDB ではないと認識した方がいい、RDB 忘れて書いた方がいいよ。

とのアドバイスを頂いたのですが、そもそもRDBってなによ?という状況なので、1から考え直す事にする。アドバイスありがとうございます>Vol氏

1対他の場合

これは凄く簡単というか、シンプルにFK的なものが有って

class Author(db.Model):
  name = db.StringProperty()

class Story(db.Model):
  author = db.ReferenceProperty(Author)

story = db.get(story_key)
author_name = story.author.name
(via:http://code.google.com/appengine/docs/datastore/typesandpropertyclasses.html#ReferenceProperty)

と書けば良い。多対多には、参考にならなさそうなので深追いはやめておく。

多対他への考察

中間マップ作成
  • coreデータ
    • c_id
    • body
  • tagデータ
    • t_id
    • body

上記テーブル(クラス)を結びつける為に

  • tableデータ
    • core_id
    • tag_id

このような中間テーブルを作成し、関連を作成する。

問題点は、コア1個、タグ3個の入力に対して、テーブルが3個作成されるので、倍近いデータが作成されることになる。この問題も、ある程度のタグが作成されてしまえば、タグは使い回しの状態に入るので倍にはならない。しかし、コアとは別にタグの数だけテーブルが作成されるので、この状態では、倍以上のデータが作成されることになる。

悩ましいな。

ListPropertyを使用する

これが、本命な気がする。Datastore APIのプロパティーにListを持てる奴が有るのでこれを利用してどうにか出来ないか考えてみた。ここでめんどくさいのはListの中身に任意のClassを持てない事(持てるかも知れないが・・・) コレさえ出来れば、良いのになぁと思うが出来ないものは出来ない。Listに入ることができる、ValueTypeは、

  • str or unicode
  • bool
  • int or long
  • float
  • datetime.datetime
  • db.Key By path elements (kind, ID or name, kind, ID or name...)
  • UserProperty users.User By email address (Unicode)
  • BlobProperty db.Blob (not orderable)
  • TextProperty db.Text (not orderable)
  • CategoryProperty db.Category Unicode
  • LinkProperty db.Link Unicode
  • EmailProperty db.Email Unicode
  • GeoPtProperty db.GeoPt By latitude, then longitude
  • IMProperty db.IM Unicode
  • PhoneNumberProperty db.PhoneNumber Unicode
  • PostalAddressProperty db.PostalAddress Unicode
  • RatingProperty db.Rating

だけらしい。この中で使うならばdb.Keyが適切かと思われる。
ただいまのモデルは以下の通り

#!/usr/bin/env python
# encoding: utf-8
"""
model.py

Created by atusi on 2008-04-13.
Copyright (c) 2008 a2c.biz . All rights reserved.
"""
from google.appengine.api import users
from google.appengine.ext import db

class UserPrefs(db.Model):
  created_at = db.DateTimeProperty(auto_now_add = True)
  user = db.UserProperty(required = True)
  description = db.StringProperty()
  
class Tag(db.Model):
  created_at  = db.DateTimeProperty( auto_now_add = True )
  body = db.StringProperty( required = True )
  
class Type(db.Model):
  body = db.StringProperty( required = True )
  description = db.StringProperty()

class Core(db.Model):
  created_at  = db.DateTimeProperty( auto_now_add=True )
  usr = db.ReferenceProperty(UserPrefs)
  body = db.StringProperty(required = True)
  #types = db.CategoryProperty(Type)
  tags = db.ListProperty(db.Key)

これで、コアデータに対してどのタグが使われているのかが2つのテーブルのみで作成可能となった。

今後の問題点

  1. Djangoで言うところのHoge.objects.get_or_create()的な処理をどうするのか?
    • hoge.is_saved()というのが有るらしいのでそれを使えばいいのかな?
  2. リストによりKeyが分かっても、テンプレート上ではKeyを元にTagテーブルから情報を持ってくる事が不可能
    • どうせAjax化するのでTagAllのHashをJsonで渡して適宜レンダリングすればいいのか?

いろいろ問題山積みだけど、なんだか楽しいな。
早くアカウント発行されないかなぁ・・・
って、まだ持ってないのかよ!>オレ