如何建立允许不同类型用户在 SqlAlchemy 中注册的数据库关系

Posted

技术标签:

【中文标题】如何建立允许不同类型用户在 SqlAlchemy 中注册的数据库关系【英文标题】:How can I establish a database relationship that allows different types of users to register in SqlAlchemy 【发布时间】:2020-05-09 05:46:02 【问题描述】:

我有不同的用户,他们将访问平台中的不同功能,并拥有与他们相关的不同信息。我根据下图实现了这些表,其中我有一个包含身份验证数据的用户表,我有两个表,用于两种用户类型及其相应的信息。

Database diagram

我与“zpeedr user”有一对一的关系,对于“merchant user”也是如此。我将 flask_sqlalchemy 与 postgresql 数据库一起使用。如果不按预期将其与现有 user.id 关联,则无法实例化 zpeedr 或商家类型用户。我的问题是,使用此设置,我可以为 zpeedr 和商家分配相同的 user_id。如何确保一个 user.id 只能由 zpeedr 用户或商家用户关联?

我创建表的简化代码是:

类用户(UserMixin,db.Model): '''用户属性''' id = db.Column(db.Integer, primary_key=True) 角色 = db.Column(db.String(10), nullable=False) 名称 = db.Column(db.String(50), nullable=False) 电子邮件 = db.Column(db.String(50),唯一 = True,可为空 = False) password_hash = db.Column(db.String(250), nullable=False) 状态 = db.Column(db.String(15), server_default="Pending") registration_date = db.Column(db.DateTime, server_default=db.func.current_timestamp()) zpeedr = db.relationship('Zpeedr', backref='user', uselist=False) 商家 = db.relationship('Merchant', backref='user', uselist=False)

def __init__(self, role, name, email, password_hash, status, registration_date, zpeedr, merchant):
    self.role = role
    self.name = name
    self.email = email
    self.password_hash = password_hash
    self.status = status
    self.registration_date = registration_date
    self.zpeedr = zpeedr
    self.merchant = merchant

类 Zpeedr(db.Model): ''' Zpeedr 属性 ''' id = db.Column(db.Integer, primary_key=True) # 用户表的外键 user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False, unique=True) 地址 = db.Column(db.String(200), nullable=False) 位置 = db.Column(db.String(50), nullable=False) 邮政编码 = db.Column(db.String(10), nullable=False) 联系人 = db.Column(db.String(15), nullable=False) 增值税 = db.Column(db.String(20), nullable=False, unique=True) nin = db.Column(db.String(20), nullable=False, unique=True)

def __init__(self, user_id, address, location, zipcode, contact, vat, nin):
    self.user_id = user_id
    self.address = address
    self.location = location
    self.zipcode = zipcode
    self.contact = contact
    self.vat = vat
    self.nin = nin

类商人(db.Model): '''商家属性''' id = db.Column(db.Integer, primary_key=True) # 外键到用户表 user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False, unique=True) 股票代码 = db.Column(db.String(4), nullable=False) 类别 = db.Column(db.String(10), nullable=False) 地址 = db.Column(db.String(200), nullable=False) 位置 = db.Column(db.String(50), nullable=False) 邮政编码 = db.Column(db.String(10), nullable=False) 联系人 = db.Column(db.String(15), nullable=False) 增值税 = db.Column(db.String(20), nullable=False, unique=True) nin = db.Column(db.String(20), nullable=False, unique=True) # 与Support_merchant Table建立关系(一对多) support_merchant = db.relationship('Support_merchant', backref='merchant')

def __init__(self, user_id, ticker, category, address, location, zipcode, contact, vat, nin): self.user_id = user_id self.ticker = ticker self.category = category self.address = address self.location = location self.zipcode = zipcode self.contact = contact self.vat = vat self.nin = nin

【问题讨论】:

【参考方案1】:

如果表用户的目的只是存储常用数据,您可以使用 postgres inheritance。

在一个非常简化的模型中,您可以拥有:包含所有公共数据的父表

create table user_(
  type varchar(1)
  );

和你的孩子:

 create table zeepdr(
   id integer primary key,
   nin varchar
   ) inherits(user_);

 create table merchant(
   id integer primary key,
   ticker char(4)
  ) inherits(user_);

SQLAlchemy 中对应的代码应该是这样的:

Table("zeepdr", metadata, ..., postgresql_inherits="user_")

基本用法

插入

根据我对山羊的看法,您不需要既不是商人也不是 zeepdr 的用户,因此您将插入数据,就好像父表不存在一样:

insert into zeepdr(id, type, nin) values(1,'Z','a');
insert into merchant(id, type, ticker) values(1,'M','aaaa');

这也意味着用户要么是商人,要么是 zeepdr。

选择

select * from zeepdr;

按照您的预期行事。此外,如果您只想检索所有用户的公共数据,您可以使用关键字ONLY

select * from ONLY user_;

或在 SQLAlchemy 中

result = table.select().with_hint(table, 'ONLY', 'postgresql')

更多信息:约束

只有检查或非空约束被继承,除非你使用NO INHERIT。其他类型没有继承。

最后一点

更多关于继承和 SQLAlchemy 的信息here

【讨论】:

非常有帮助!你为我指明了正确的方向。我的 SQLAlchemy 有点不同,但我想你可以同时做到这一点。我跟着docs.sqlalchemy.org/en/13/orm/inheritance.html 创建了一个连接表继承。还删除了构造函数,因为 Flask-SQLAlchemy 基本模型定义了它们并且它们默认工作。

以上是关于如何建立允许不同类型用户在 SqlAlchemy 中注册的数据库关系的主要内容,如果未能解决你的问题,请参考以下文章

SQLAlchemy 外键允许为空

如何在 laravel 中建立用户与用户的关系? [关闭]

SQLAlchemy怎么在Oracle中自动建立映射

如何仅允许特定用户查看 Drupal 中内容类型的特定字段?

通过 SQLAlchemy 获取随机行

如何使用 SQLAlchemy 建立多对多关系:一个很好的例子