与不同模型的关系中的双向行为

时间:2015-01-25 13:11:45

标签: python flask sqlalchemy

我有一些具有不同属性(请求)的classess,它们都应该与另一个类有关(通知)

我的结构是这样的:

db = SQLAlchemy()

class RequestMixin(object):
    id = db.Column(db.Integer, autoincrement=True, primary_key=True)
    description = db.Column(db.Text(250))

    @declared_attr
        def __tablename__(self):
        return self.__name__.lower()

    @declared_attr
    def notification_id(self):
        return db.Column(db.Integer, ForeignKey('notification.id'))

    @declared_attr
    def notification(self):
        return db.relationship('Notification')

def DailyLeaveRequest(db.Model, RequestMixin):
    demand_date = db.Column(db.Date, nullable=False)

def HourlyLeaveRequest(db.Model, RequestMixin):
    demand_date = db.Column(db.Date, nullable=False)
    time_from = db.Column(db.String(10), nullable=False)
    time_to = db.Column(db.String(10), nullable=False)

def FundRequest(db.Model, RequestMixin):
    amount = db.Column(db.Integer)


class Notification(db.Model):
    __tablename__='notification'
    id = db.Column(db.Integer, autoincrement=True, primary_key=True)
    seen = db.Column(db.BOOLEAN, nullable=False, default=False)
    concept = db.Column(db.Integer, nullable=False)
    # and some relationships to users which doesn't matter here

通过这种方式,我可以通过每个Request对象访问Notification对象,但由于没有' backref',我无法以相反的方式访问。

所以我现在要做的就是检查Notification.concept并手动查询相关的请求。这样:

if notification_obj.concept == 1:
    related_request = DailyLeaveRequest.query.filter_by(notification_id=notification_obj.id).first()
elif notification_obj == 2:
    .
    .

这很恶心。 (我有超过3个请求类)

当我尝试添加这样的反射时:

.
.
@declared_attr
def notification(self):
    return db.relationship('Notification', backref='request')
.
.

,我收到此错误:

  

sqlalchemy.exc.ArgumentError:创建backref'请求'时出错关系' HourlyLeaveRequest.notification':该名称的属性存在于mapper' Mapper | Notification | notification'

有没有办法让一个与其他不同类相关的类具有反向访问权限?

1 个答案:

答案 0 :(得分:1)

你应该看看SQLAlchemy's polymorphic relationships

使用它的最简单方法是joined table inheritance。它允许您定义包含所有常见属性的基表,包括与Notification的关系。

class Request(db.Model):
    id = db.Column(db.Integer, autoincrement=True, primary_key=True)
    description = db.Column(db.Text(250))

    notification_id = db.Column(db.Integer, ForeignKey('notification.id'))
    notification = db.relationship('Notification')

    type = db.Column(db.String(50))

    __mapper_args__ = {
        'polymorphic_identity': 'request',
        'polymorphic_on': type,
    }


class DailyLeaveRequest(Request):
    id = db.Column(db.Integer, db.ForeignKey('request.id'), primary_key=True)

    demand_date = db.Column(db.Date, nullable=False)

    __mapper_args__ = {
        'polymorphic_identity': 'dailyleave',
    }


class HourlyLeaveRequest(Request):
    id = db.Column(db.Integer, db.ForeignKey('request.id'), primary_key=True)

    demand_date = db.Column(db.Date, nullable=False)
    time_from = db.Column(db.String(10), nullable=False)
    time_to = db.Column(db.String(10), nullable=False)

    __mapper_args__ = {
        'polymorphic_identity': 'hourlyleave',
    }


def FundRequest(Request):
    id = db.Column(db.Integer, db.ForeignKey('request.id'), primary_key=True)

    amount = db.Column(db.Integer)

    __mapper_args__ = {
        'polymorphic_identity': 'hourlyleave',
    }

然后,您可以查询所有Requests

requests = Request.query.all()
for request in requests:
    demand_date = getattr(
        request, 'demand_date', 'demand_date not available on this type')
    print(request.type, demand_date)

Request的所有属性以及特定的表都可用,但不能用于其他表。

您还可以查询特定类型:

requests = DailyLeaveRequest.query.all():
for request in requests:
    print(request.type, request.demand_date)