SQLAlchemy子类/继承关系

2024-04-19 04:18:21 发布

您现在位置:Python中文网/ 问答频道 /正文

class Geolocation(db.Model):
    __tablename__ = "geolocation"
    id = db.Column(db.Integer, primary_key=True)
    latitude = db.Column(db.Float)
    longitude = db.Column(db.Float)
    elevation = db.Column(db.Float)         # Meters
    # Relationships
    pin = db.relationship('Pin', uselist=False, backref="geolocation")

    def __init__(self, latitude, longitude, elevation):
        self.latitude = latitude
        self.longitude = longitude
        self.elevation = elevation

    def __repr__(self):
        return '<Geolocation %s, %s>' % (self.latitude, self.longitude)


class Pin(db.Model):
    __tablename__ = "pin"
    id = db.Column(db.Integer, primary_key=True)
    geolocation_id = db.Column(db.Integer, db.ForeignKey('geolocation.id'))  # True one to one relationship (Implicit child)

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

    def __repr__(self):
        return '<Pin Object %s>' % id(self)      # Instance id merely useful to differentiate instances.


class User(Pin):
    #id = db.Column(db.Integer, primary_key=True)
    pin_id = db.Column(db.Integer, db.ForeignKey('pin.id'), primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password_hash = db.Column(db.String(120), nullable=False)
    salt = db.Column(db.String(120), nullable=False)
    # Relationships
    #posts = db.relationship('Post', backref=db.backref('user'), lazy='dynamic')               #One User to many Postings.

    def __init__(self, username, password_hash, salt, geolocation_id):
        super(Pin, self).__init__(self, geolocation_id)
        self.username = username
        self.password_hash = password_hash
        self.salt = salt

    def __repr__(self):
        return '<User %r>' % self.username

我对如何在SQLAlchemy中设置id和与子类的关系感到困惑(我碰巧使用了Flask SQLAlchemy)。我的总体设计是让超类Pin成为任何具有地理位置(即用户、地点等)的对象的高级表示。

Pin和Geolocation对象之间有一对一的关系,因此Geolocation不包含两个用户(或一个用户和一个地方)的位置。现在我想子类化Pin来创建用户类。一个用户对象应该有一个名称、密码、salt,我还希望能够通过userObj.geolocation查找用户的地理位置。但是,稍后我想创建一个类Place,它也可以作为Pin的子类,并且我应该能够通过placeObj.geolocation查找一个地方的地理位置。给定一个geolocation对象,我应该能够使用geolocationObj.pin查找geolocation对象对应的用户/位置/etc。我引入超类Pin的全部原因是为了确保Pin和地理位置对象之间有一个纯粹的一对一关系,而不是让地理位置与需要地理位置表具有user_idplace_id列的用户或个人相关联,其中一个列始终为空。

我希望每个用户都通过父Pin类自动拥有一个.geolocation属性,父Pin类引用了地理位置,但是看起来SQLAlchemy没有这样做。如何使子类化关系工作,以实现让User和Place以及潜在的其他类子类Pin的目标,让这些类中的每个类都通过Pin具有geolocation属性,并在Pin和geolocation之间具有一对一的关系?


Tags: 对象用户selfidtruedbdefpin
2条回答

我想出的解决办法。这是在声明式样式的SQLAlchemy中进行子类化并使用Join继承的完整示例。

class Geolocation(Base):
    __tablename__ = "geolocation"
    id = Column(Integer, primary_key=True)
    latitude = Column(Float)
    longitude = Column(Float)
    elevation = Column(Float)         # Meters
    # Relationships
    person = relationship('Pin', uselist=False, backref="geolocation")

    def __init__(self, latitude, longitude, elevation):
        self.latitude = latitude
        self.longitude = longitude
        self.elevation = elevation

    def __repr__(self):
        return '<Geolocation %s, %s>' % (self.latitude, self.longitude)


class Pin(Base):
    __tablename__ = 'pin'
    id = Column(Integer, primary_key=True)
    geolocation_id = Column(Integer, ForeignKey('geolocation.id'), unique=True, nullable=False)  # True one to one relationship (Implicit child)
    type = Column('type', String(50))              # discriminator
    __mapper_args__ = {'polymorphic_on': type}

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


class User(Pin):
    __tablename__ = 'user'
    id = Column(Integer, ForeignKey('pin.id'), primary_key=True)
    __mapper_args__ = {'polymorphic_identity': 'user',
                       'inherit_condition': (id == Pin.id)}
    user_id = Column(Integer, autoincrement=True, primary_key=True, unique=True)
    username = Column(String(80), unique=True)
    password_hash = Column(String(120))
    salt = Column(String(120))
    posts = relationship('Posting', primaryjoin="(User.user_id==Posting.user_id)", backref=backref('user'), lazy='dynamic')   #One User to many Postings.

    def __init__(self, username, password_hash, salt, geo_id):
        super(User, self).__init__(geo_id)
        self.username = username
        self.password_hash = password_hash
        self.salt = salt

    def __repr__(self):
        return '<User %s>' % (self.username)


class Posting(Pin):
    __tablename__ = 'posting'
    id = Column(Integer, ForeignKey('pin.id'), primary_key=True)
    __mapper_args__ = {'polymorphic_identity': 'posting',
                        'inherit_condition': (id == Pin.id)}
    posting_id = Column(Integer, autoincrement=True, primary_key=True, unique=True)
    creation_time = Column(DateTime)
    expiration_time = Column(DateTime)
    user_id = Column(Integer, ForeignKey('user.user_id'))              # One User to many Postings

    def __init__(self, creation_time, expiration_time, user_id, geo_id):
        super(Posting, self).__init__(geo_id)
        # For now, require creation time to be passed in. May make this default to current time.
        self.creation_time = creation_time
        self.expiration_time = expiration_time
        self.user_id = user_id

    def __repr__(self):
        #TODO come up with a better representation
        return '<Post %s>' % (self.creation_time)

这是SQLAlchemy中mapping inheritance hierarchiesdoing it declaratively的文档。

我相信您会想要joined table继承风格,这意味着父类链中的每个类都有自己的表,其中的列是唯一的。基本上,您需要在pin表中添加一个discriminator列来表示每个Pin的子类类型,并在类中添加一些双下划线属性来描述对SQLAlchemy的继承配置。

相关问题 更多 >