SQLAlchemy:如何实现表的只读引用作为另一表字段合法值来源
在SQLAlchemy中实现单向引用(子表不可修改父表记录)
你的需求是让Country表作为参考数据表,其他表只能引用它的记录,而不能通过关联字段修改Country本身的数据。我们可以通过数据库外键约束+ORM关联控制来实现这个需求,下面是具体的实现步骤和代码示例:
1. 基础模型定义与外键约束
首先补全你的Country模型,再定义一个示例子表(比如City),通过外键关联到Country的主键。外键约束会在数据库层面保证子表的引用字段只能是Country中已存在的记录:
from sqlalchemy import Column, Integer, String, ForeignKey from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship Base = declarative_base() class Country(Base): __tablename__ = 'countries' id = Column(Integer, primary_key=True) name = Column(String, unique=True, nullable=False) code = Column(String, unique=True, nullable=False) # 关联子表(可选),如果不需要通过Country操作子表,可设置viewonly=True cities = relationship("City", back_populates="country", viewonly=True) class City(Base): __tablename__ = 'cities' id = Column(Integer, primary_key=True) name = Column(String, nullable=False) # 外键关联Country的主键,数据库层面确保只能引用存在的Country country_id = Column(Integer, ForeignKey('countries.id'), nullable=False) # 关联到Country模型,uselist=False表示一个City属于一个Country # 这里默认可以通过city.country获取关联的Country,但如果要防止修改Country的属性,需要在业务层控制 country = relationship("Country", back_populates="cities")
2. 防止通过子表修改父表记录
上面的代码已经实现了引用约束,但默认情况下,你仍然可以通过city.country.name = "新名称"这样的代码修改Country的字段。要避免这种情况,可以从以下两个层面入手:
层面一:ORM对象属性控制
你可以把Country的字段设置为只读,或者封装Country模型,只提供读取接口。比如使用@property装饰器来限制修改:
class Country(Base): __tablename__ = 'countries' id = Column(Integer, primary_key=True) _name = Column("name", String, unique=True, nullable=False) _code = Column("code", String, unique=True, nullable=False) cities = relationship("City", back_populates="country", viewonly=True) @property def name(self): return self._name @property def code(self): return self._code
这样,当你尝试修改country.name或者city.country.name时,会因为没有setter方法而报错,从对象层面阻止了修改。
层面二:数据库权限控制
如果需要更严格的限制,可以在数据库层面给Country表设置只读权限(除了初始化数据的账号外,其他操作账号只能执行SELECT,不能执行UPDATE/DELETE)。比如在PostgreSQL中:
REVOKE UPDATE, DELETE ON countries FROM your_app_user; GRANT SELECT, INSERT ON countries TO your_app_user;
这样即使ORM层面允许修改,数据库也会拒绝修改操作,从底层保证数据的不可修改性。
3. 关键注意点
- 外键约束是基础:它确保子表的引用字段只能指向
Country中已存在的记录,避免无效引用。 - 业务逻辑配合:如果不需要严格的数据库级只读,在应用层封装
Country模型的访问权限是更灵活的方式。 viewonly=True的作用:在Country的cities关联中设置viewonly=True,可以防止通过Country实例修改子表(比如country.cities.append(new_city)),但这和你的需求(子表不能修改父表)无关,仅用于控制父表对子表的操作。
内容的提问来源于stack exchange,提问作者beta2
相关产品推荐
相关产品推荐

