Flask-SQLAlchemy单向一对多关系的影响及写法差异与性能对比
我来帮你理清这两个关于Flask-SQLAlchemy一对多关系的问题:
一对多关系的核心是数据库层面的外键约束加上ORM层面的relationship映射,仅从一侧定义的情况要分两种来看:
仅在从实体(子表,比如你的
CustomJob)定义外键,不在主实体(父表,比如Country)添加relationship:- 你只能从子表实例通过外键字段(比如
custom_job.country_from)拿到父表的ID,再手动查询父表数据;但反过来,父表实例没有直接的属性(比如country.custom_jobs)来一键获取所有关联的子表记录,必须自己写CustomJob.query.filter_by(country_from=country.id).all()这种查询语句,ORM的便捷性就没了。 - 这种属于纯数据库层面的单向关联,ORM层面没有提供双向访问的能力,操作起来比较繁琐。
- 你只能从子表实例通过外键字段(比如
仅在主实体添加
relationship但子表不定义外键:- 这种写法根本不成立!因为
relationship只是ORM用来映射关联的工具,它的底层依赖数据库的外键约束来找到关联关系。没有外键的话,ORM不知道怎么把两个表关联起来,直接会抛出错误。所以实际开发中,外键是必须在子表定义的,relationship是可选的(但加了会更方便)。
- 这种写法根本不成立!因为
先明确两种写法的核心区别:
- 写法一:主实体
Country通过relationship定义了关联,还通过backref给子实体CustomJob自动生成了反向引用属性country,并且设置了lazy=False(立即加载)。 - 写法二:只有子实体
CustomJob的外键,主实体没有任何relationship配置。
两者的性能差异主要体现在关联查询的时机和方式上:
查询子实体时:
两种写法在查询CustomJob本身数据时性能完全一致,因为外键是数据库层面的约束,查询子表的逻辑没有区别。如果要从CustomJob关联到Country,写法一可以直接用custom_job.country(backref自动生成的属性),写法二需要通过country_from手动查Country——本质上都是执行一次关联查询,性能差异可以忽略。查询主实体时:
写法一中,因为设置了lazy=False,当你查询Country实例时,ORM会自动执行JOIN查询,一次性把Country和它关联的所有CustomJob数据都拉回来;而写法二中,查询Country只会加载父表本身的数据,如果你之后需要获取关联的CustomJob,得再单独发起一次查询。
这时候的性能差异取决于你的业务场景:- 如果每次查
Country都需要用到它的CustomJob,写法一的立即加载会减少一次数据库请求,更高效; - 如果大部分时候查
Country不需要关联子表,写法一的立即加载会额外拉取不需要的数据,反而浪费资源——这种情况你应该把lazy改成默认的'select'(按需加载,用到country.custom_jobs时才查询),或者'joined'(显式指定JOIN,按需触发)。
- 如果每次查
总结:两种写法的性能差异本质上是ORM关联加载策略带来的,和单向/双向定义本身无关。核心是看你的查询需求是否匹配
relationship的lazy配置,配置得当的话写法一更高效,配置不当反而不如写法二。
内容的提问来源于stack exchange,提问作者Achref Othmeni

