私は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_,)
)
をを設定した場合:
なぜPersonalMessageUser.user_typeが 'primary_key'として定義されていますか? http://stackoverflow.com/questions/2415842/sqlalchemy-with-multiple-primary-keys-does-not-automatically-set-any – estin
@estin単一の複合主キー( 'pm_id、user_id、user_type')があります。関連テーブルの一般的な解決策です。主キーを構成するすべての列が手動で(直接または関係を介して)設定されるため、参照する質問は関係ありません。 –