SQLAlchemy使用关系和未提交的对象进行复杂化

时间:2011-08-10 20:06:08

标签: python sqlalchemy

我正在通过Flask使用SQLAlchemy,我想在我的webapp中添加简单的个人消息。该模型有一个User类,一个PersonalMessage类和一个PersonalMessageUser关联类,后者建立了与前两个关系类似的关系。这是一个精简版:

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解释器中玩它时,我注意到一些奇怪的事情:当我创建一个有一个发件人和一个收件人的新PersonalMessage时,_pm_users backref实际列出了每个用户两次。一旦将对象提交到数据库,它看起来没问题。请参阅以下会话作为示例:

>>> 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设置中是否遗漏了某些内容,或者我会忽略这一点?

1 个答案:

答案 0 :(得分:3)

致电时

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

你有两次将对象追加到_pm_users list

这应该适合你:

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

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

设置relationship property时,sqlalchemy为您关联对象