Python中SQLAlchemy不使用ForeignKey如何进行关联?

官方文档

class Address(Base):
     __tablename__ = 'addresses'
     id = Column(Integer, primary_key=True)
     email_address = Column(String, nullable=False)
     user_id = Column(Integer, ForeignKey('users.id'))
 user = relationship("User", back_populates="addresses")

 def __repr__(self):
     return "<Address(email_address='%s')>" % self.email_address

这里 ForeignKey('users.id') 是必须的么? 可以不使用外键强制约束吗?


Python中SQLAlchemy不使用ForeignKey如何进行关联?

7 回复

可以啊,自己写程序实现关联呗。


在SQLAlchemy里,不用ForeignKey也能做关联,核心是靠relationship()和手动指定关联条件。下面是个完整例子:

from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, sessionmaker

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    # 不用ForeignKey,用primaryjoin指定关联条件
    addresses = relationship(
        "Address",
        primaryjoin="User.id == Address.user_id",  # 手动指定关联条件
        back_populates="user"
    )

class Address(Base):
    __tablename__ = 'addresses'
    id = Column(Integer, primary_key=True)
    email = Column(String)
    user_id = Column(Integer)  # 这里没有ForeignKey约束
    
    user = relationship(
        "User",
        primaryjoin="Address.user_id == User.id",
        back_populates="addresses"
    )

# 创建数据库
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)

Session = sessionmaker(bind=engine)
session = Session()

# 测试数据
user = User(name="张三")
addr1 = Address(email="zhangsan@example.com", user_id=1)
addr2 = Address(email="zhangsan2@example.com", user_id=1)

user.addresses = [addr1, addr2]
session.add(user)
session.commit()

# 查询测试
queried_user = session.query(User).first()
print(f"用户: {queried_user.name}")
for addr in queried_user.addresses:
    print(f"  邮箱: {addr.email}")

关键点:

  1. relationship()里的primaryjoin参数用来指定关联条件
  2. 数据库层面没有外键约束,但ORM层面关联正常
  3. 查询和操作跟有外键时一样

这种方案适合:数据库没外键约束、跨数据库关联、或者用已有数据库结构的情况。不过要注意数据一致性得自己维护。

总结:用primaryjoin手动指定关联条件就行。

当然可以啦, 比如说你有一个 users 表:

class User(Base):
tablename = “users”
id_ = Column(Integer, primary_key=True)
name = Column(String(16))

你还有一个 address 表:

class Address(Base):
tablename = “address”
id_ = Column(Integer, primary_key=True)
place = Column(String(256))
user_id = Column(Integer)

user = relationship(“User”, uselist=False, primaryjoin=foreign(user_id) == remote(User.id_))

你只需要在 relationship 里用 primaryjoin 来手动指定 join 关系就可以了, 前提是已经导入了 User. 这部分也在文档里提到了

此外还可以写成

relationship(“User”, uselist=False, foreign_keys=user_id, primaryjoin=“Address.user_id==User.id_”)

这种写法同样要求已经导入 User, 但是会被 ide 识别为 unused import. 所以建议使用第一种

#2
user = relationship(“User”, uselist=False, primaryjoin=foreign(user_id) == remote(User.id_))

这不还是有 foreign 么

…首先你是不是被降权了,没收到通知

其次虽然这里有 foreign,但实际上在数据库里面是不会有外键约束的。这里只是把这个 user_id 看作了外键,以便于进行 join 查询,和他本身是不是外键是没关系的。

#4
好像是吧……

表示在 sqlalchemy 里并没有 foreign 和 remote 两个方法

no named module

#4
好吧,在 sqlalchemy.orm 中

回到顶部