2011-08-10 6 views
3

私はFlask経由でSQLAlchemyを使用しています。私は簡単なパーソナルメッセージをWebアプリケーションに追加したいと思います。このモデルは、Userクラス、PersonalMessageクラス、およびPersonalMessageUserアソシエーションクラスを持ち、後者は前者の2つの関係がないとの関係を設定します。ここではストリップダウンバージョンです:SQLAlchemyリレーションシップとコミットされていないオブジェクトの複雑さ

import collections 
import datetime 

from flaskext.sqlalchemy import SQLAlchemy 
from . import app 

db = SQLAlchemy(app) 

def current_ts(): 
    return datetime.datetime.utcnow() 

class User(db.Model): 
    id = db.Column(db.Integer, primary_key=True) 
    username = db.Column(db.String(127), nullable=False, unique=True) 

    def __repr__(self): 
     return '<User {0.username!r} (#{0.id})>'.format(self) 

class PersonalMessage(db.Model): 
    id = db.Column(db.Integer, primary_key=True) 
    subject = db.Column(db.String(127), nullable=False) 
    body = db.Column(db.Text, nullable=False) 
    date = db.Column(db.DateTime, nullable=False, default=current_ts) 

    def __repr__(self): 
     return '<PersonalMessage {0.subject!r} (#{0.id})>'.format(self) 

    def __init__(self, subject, body, from_, to=None, cc=None, bcc=None): 
     self.subject = subject 
     self.body = body 
     if not to and not cc and not bcc: 
      raise ValueError, 'No recipients defined' 
     self._pm_users.append(PersonalMessageUser(
      message=self, user_type='From', user=from_, 
     )) 
     for type, values in {'To': to, 'CC': cc, 'BCC': bcc}.items(): 
      if values is None: 
       continue 
      if not isinstance(values, collections.Iterable): 
       values = [values] 
      for value in values: 
       self._pm_users.append(PersonalMessageUser(
        message=self, user=value, user_type=type, 
       )) 

class PersonalMessageUser(db.Model): 
    pm_id = db.Column(db.ForeignKey(PersonalMessage.id), nullable=False, 
         primary_key=True) 
    message = db.relationship(PersonalMessage, backref='_pm_users', 
           lazy='subquery') 
    user_id = db.Column(db.ForeignKey(User.id), nullable=False, 
         primary_key=True) 
    user = db.relationship(User, backref='_personal_messages') 
    user_type = db.Column(
     db.Enum('From', 'To', 'CC', 'BCC', name='user_type'), 
     nullable=False, default='To', primary_key=True, 
    ) 

    def __repr__(self): 
     return (
      '<PersonalMessageUser ' 
      '{0.user_type}: {0.user.username!r} ' 
      '(PM #{0.pm_id}: {0.message.subject!r})>' 
     ).format(self) 

はすべてが基本的に正常に動作しますが、私はPythonインタプリタにそれで遊んとき、私は奇妙な何かに気づいた:私は1つの送信者との受信者との新しいPersonalMessageを作成するときに、_pm_users backrefは実際に各ユーザーを2回リストします。オブジェクトがデータベースにコミットされると、それは大丈夫です。例として、次のセッションを参照してください:

>>> al = User(username='al') 
>>> db.session.add(al) 
>>> steve = User(username='steve') 
>>> db.session.add(steve) 
>>> db.session.commit() 
BEGIN (implicit) 
INSERT INTO user (username) VALUES (?) 
('al',) 
INSERT INTO user (username) VALUES (?) 
('steve',) 
COMMIT 
>>> pm = PersonalMessage('subject', 'body', from_=al, to=steve) 
>>> pm._pm_users 
BEGIN (implicit) 
SELECT user.id AS user_id, user.username AS user_username 
FROM user 
WHERE user.id = ? 
(1,) 
SELECT user.id AS user_id, user.username AS user_username 
FROM user 
WHERE user.id = ? 
(2,) 
[<PersonalMessageUser From: u'al' (PM #None: 'subject')>, 
<PersonalMessageUser From: u'al' (PM #None: 'subject')>, 
<PersonalMessageUser To: u'steve' (PM #None: 'subject')>, 
<PersonalMessageUser To: u'steve' (PM #None: 'subject')>] 
>>> len(pm._pm_users) 
4 
>>> db.session.add(pm) 
>>> pm._pm_users 
[<PersonalMessageUser From: u'al' (PM #None: 'subject')>, 
<PersonalMessageUser From: u'al' (PM #None: 'subject')>, 
<PersonalMessageUser To: u'steve' (PM #None: 'subject')>, 
<PersonalMessageUser To: u'steve' (PM #None: 'subject')>] 
>>> db.session.commit() 
INSERT INTO personal_message (subject, body, date) VALUES (?, ?, ?) 
('subject', 'body', '2011-08-10 19:48:15.641249') 
INSERT INTO personal_message_user (pm_id, user_id, user_type) VALUES (?, ?, ?) 
((1, 1, 'From'), (1, 2, 'To')) 
COMMIT 
>>> pm._pm_users 
BEGIN (implicit) 
SELECT personal_message.id AS personal_message_id, 
    personal_message.subject AS personal_message_subject, 
    personal_message.body AS personal_message_body, 
    personal_message.date AS personal_message_date 
FROM personal_message 
WHERE personal_message.id = ? 
(1,) 
SELECT personal_message_user.pm_id AS personal_message_user_pm_id, 
    personal_message_user.user_id AS personal_message_user_user_id, 
    personal_message_user.user_type AS personal_message_user_user_type 
FROM personal_message_user 
WHERE ? = personal_message_user.pm_id 
(1,) 
SELECT user.id AS user_id, user.username AS user_username 
FROM user 
WHERE user.id = ? 
(1,) 
SELECT user.id AS user_id, user.username AS user_username 
FROM user 
WHERE user.id = ? 
(2,) 
[<PersonalMessageUser From: u'al' (PM #1: u'subject')>, 
<PersonalMessageUser To: u'steve' (PM #1: u'subject')>] 

少なくとも最終的な結果は、私はそれがあることを期待するものですが、コミットする前に二度現れて、各ユーザーは私が不快に感じさせます。私は何が起こっているのか理解したいと思います。私はrelationship/backrefセットアップで何かを逃していますか、それとも私はこれを無視しなければなりませんか?

PersonalMessageUser(
    message=self, user_type='From', user=from_, 
) 

または

self._pm_users.append(
    PersonalMessageUser(user_type='From', user=from_,) 
)  

をを設定した場合:

+0

なぜPersonalMessageUser.user_typeが 'primary_key'として定義されていますか? http://stackoverflow.com/questions/2415842/sqlalchemy-with-multiple-primary-keys-does-not-automatically-set-any – estin

+0

@estin単一の複合主キー( 'pm_id、user_id、user_type')があります。関連テーブルの一般的な解決策です。主キーを構成するすべての列が手動で(直接または関係を介して)設定されるため、参照する質問は関係ありません。 –

答えて

3

あなたは

self._pm_users.append(PersonalMessageUser(
    message=self, user_type='From', user=from_, 
)) 

を呼び出すと、あなたは二度これはあなたのために働く必要があります

_pm_usersリストにオブジェクトを追加しています、sqlalchemyはオブジェクトを関連付けます

+0

もちろん、あなたは正しいです。私は明白なことを逃した。私を指してくれてありがとう! – igor

関連する問題