如何扩展SQLAlchemy的association_proxy的`getter`功能?

2 投票
3 回答
868 浏览
提问于 2025-04-17 11:57

编辑:我想建立一个1 对 0:1的关系,也就是说一个User(用户)可以有零个或一个Comment(评论)。我希望直接访问评论,而不是先访问Comment对象。使用SQLAlchemy的association_proxy可以很好地实现这个需求,但有一个问题:在没有关联Comment的情况下访问User.comment会出错。不过我希望在这种情况下返回None,而不是AttributeError错误。

看看下面的例子:

import sqlalchemy as sa
import sqlalchemy.orm as orm
from sqlalchemy import Column, Integer, Text, ForeignKey, Table
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy

Base = declarative_base()

class User(Base):
  __tablename__ = 'users'
  id = Column(Integer, primary_key=True)
  name = Column(Text)

  def __init__(self, name):
      self.name = name

  # proxy the 'comment' attribute from the 'comment_object' relationship
  comment = association_proxy('comment_object', 'comment')


class Comment(Base):
  __tablename__ = 'comments'
  id = Column(Integer, primary_key=True)
  comment = Column('comment', Text, nullable=False, default="")
  user_id = Column(ForeignKey('users.id'), nullable=False, unique=True)
  # user_id has to be unique to ensure that a User can not have more than one comments

  def __init__(self, comment):
      self.comment = comment

  user_object = orm.relationship(
      "User",
      uselist=False, # added after edditing the question
      backref=orm.backref('comment_object', uselist=False)
      )

if __name__ == "__main__":
  engine = sa.create_engine('sqlite:///:memory:', echo=True)
  Session = orm.sessionmaker(bind=engine)
  Base.metadata.create_all(engine)
  session = Session()

现在,下面的代码会抛出一个AttributeError错误:

u = User(name="Max Mueller")
print u.comment

有什么好的方法可以捕获这个错误,并提供一个默认值(比如空字符串)呢?

3 个回答

0

我没有看到任何能解决这个问题的答案,而且也能和“sort_by”一起使用的。

也许使用'column_property'会更好,具体可以参考这个链接:通过关联代理排序:无效的sql

2

试试这个

import sqlalchemy as sa
import sqlalchemy.orm as orm
from sqlalchemy import Column, Integer, Text, ForeignKey, Table
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy

Base = declarative_base()

class User(Base):
  __tablename__ = 'users'
  id = Column(Integer, primary_key=True)
  name = Column(Text)

  def __init__(self, name):
      self.name = name

  # proxy the 'comment' attribute from the 'comment_object' relationship
  comment = association_proxy('comment_object', 'comment')


class Comment(Base):
  __tablename__ = 'comments'
  id = Column(Integer, primary_key=True)
  comment = Column('comment', Text, nullable=False, default="")
  user_id = Column(ForeignKey('users.id'), nullable=False)

  def __init__(self, comment):
      self.comment = comment

  user_object = orm.relationship(
      "User",
      backref=orm.backref('comment_object'),
      uselist=False
      )

if __name__ == "__main__":
  engine = sa.create_engine('sqlite:///:memory:', echo=True)
  Session = orm.sessionmaker(bind=engine)
  Base.metadata.create_all(engine)
  session = Session()
  u = User(name="Max Mueller")
# comment = Comment("")
# comment.user_object = u

# session.add(u)
# session.commit()
  print "SS :", u
  print u.comment

你在 backref 中使用了 uselist,但它必须在 relationship 中。

2

其实你并不需要用到association_proxy。你完全可以用一个普通的property就搞定了。出现AttributeError错误的原因可能是因为comment_object本身是None,也就是说没有相关的行,而None是没有comment这个属性的。

class User(Base):
  __tablename__ = 'users'
  id = Column(Integer, primary_key=True)
  name = Column(Text)

  def __init__(self, name):
      self.name = name

  # proxy the 'comment' attribute from the 'comment_object' relationship
  @property
  def comment(self):
      if self.comment_object is None:
          return ""
      else:
          return self.comment_object.comment

  @comment.setter
  def comment(self, value):
      if self.comment_object is None:
          self.comment_object = Comment()
      self.comment_object.comment = value

撰写回答