通过 SQLAlchemy 实现多表映射

server/2025/2/11 6:50:17/

在使用 SQLAlchemy 进行多表映射时,我们可以使用 ORM(对象关系映射) 的方式将多个表与 Python 类进行映射。SQLAlchemy 提供了功能强大的机制,能够轻松地将数据库表和 Python 对象之间的关系建立起来。

在这里插入图片描述

1、问题背景

假设我们有一个数据库结构,由三个表组成:

items
- item_id
- item_handleattributes
- attribute_id
- attribute_nameitem_attributes
- item_attribute_id
- item_id
- attribute_id
- attribute_value

我们希望在 SQLAlchemy 中进行如下操作:

item = Item('item1')
item.foo = 'bar'session.add(item)
session.commit()item1 = session.query(Item).filter_by(handle='item1').one()
print item1.foo # => 'bar'

但是,对于 SQLAlchemy 新手来说,我们遇到了困难。虽然我们在文档中找到了相关解决方案,但它只允许将 item_idattribute_id 添加到 Item 中,而无法将属性添加到 Item 对象。

2、解决方案

我们可以在 SQLAlchemy 中通过实现“实体-属性-值模式”(Entity-Attribute-Value,EAV)来实现这种多表映射。EAV 是一种数据模型,它将实体的属性存储在一张单独的表中,而不是将它们作为实体本身的列。

一个解决方案是将属性存储在一个文本字段中。这种方法的好处在于它非常直观,并且很容易实现。但是,这种方法的缺点是无法对属性进行过滤。

另一种解决方案是使用 PostgreSQL 中的 hstore 模块,它可以存储字符串到字符串的映射。这种方法的好处是可以对属性进行过滤,但是它要求使用 PostgreSQL 数据库

下面的代码示例展示了如何使用 SQLAlchemy 实现多表映射:

class VerticalProperty(object):"""A key/value pair.This class models rows in the vertical table."""def __init__(self, key, value):self.key = keyself.value = valuedef __repr__(self):return '<%s %r=%r>' % (self.__class__.__name__, self.key, self.value)class VerticalPropertyDictMixin(object):"""Adds obj[key] access to a mapped class.This is a mixin class.  It can be inherited from directly, or includedwith multiple inheritence.Classes using this mixin must define two class properties::_property_type:The mapped type of the vertical key/value pair instances.  Will beinvoked with two positional arugments: key, value_property_mapping:A string, the name of the Python attribute holding a dict-basedrelationship of _property_type instances.Using the VerticalProperty class above as an example,::class MyObj(VerticalPropertyDictMixin):_property_type = VerticalProperty_property_mapping = 'props'mapper(MyObj, sometable, properties={'props': relationship(VerticalProperty,collection_class=attribute_mapped_collection('key'))})Dict-like access to MyObj is proxied through to the 'props' relationship::myobj['key'] = 'value'# ...is shorthand for:myobj.props['key'] = VerticalProperty('key', 'value')myobj['key'] = 'updated value']# ...is shorthand for:myobj.props['key'].value = 'updated value'print myobj['key']# ...is shorthand for:print myobj.props['key'].value"""_property_type = VerticalProperty_property_mapping = None__map = property(lambda self: getattr(self, self._property_mapping))def __getitem__(self, key):return self.__map[key].valuedef __setitem__(self, key, value):property = self.__map.get(key, None)if property is None:self.__map[key] = self._property_type(key, value)else:property.value = valuedef __delitem__(self, key):del self.__map[key]def __contains__(self, key):return key in self.__map# Implement other dict methods to taste.  Here are some examples:def keys(self):return self.__map.keys()def values(self):return [prop.value for prop in self.__map.values()]def items(self):return [(key, prop.value) for key, prop in self.__map.items()]def __iter__(self):return iter(self.keys())class Animal(VerticalPropertyDictMixin):"""An animal.Animal facts are available via the 'facts' property or by usingdict-like accessors on an Animal instance::cat['color'] = 'calico'# or, equivalently:cat.facts['color'] = AnimalFact('color', 'calico')"""_property_type = AnimalFact_property_mapping = 'facts'def __init__(self, name):self.name = namedef __repr__(self):return '<%s %r>' % (self.__class__.__name__, self.name)if __name__ == '__main__':from sqlalchemy import (MetaData, Table, Column, Integer, Unicode,ForeignKey, UnicodeText, and_, not_)from sqlalchemy.orm import mapper, relationship, create_sessionfrom sqlalchemy.orm.collections import attribute_mapped_collectionmetadata = MetaData()# Here we have named animals, and a collection of facts about them.animals = Table('animal', metadata,Column('id', Integer, primary_key=True),Column('name', Unicode(100)))facts = Table('facts', metadata,Column('animal_id', Integer, ForeignKey('animal.id'),primary_key=True),Column('key', Unicode(64), primary_key=True),Column('value', UnicodeText, default=None),)class AnimalFact(VerticalProperty):"""A fact about an animal."""mapper(Animal, animals, properties={'facts': relationship(AnimalFact, backref='animal',collection_class=attribute_mapped_collection('key')),})mapper(AnimalFact, facts)metadata.bind = 'sqlite:///'metadata.create_all()session = create_session()stoat = Animal(u'stoat')stoat[u'color'] = u'reddish'stoat[u'cuteness'] = u'somewhat'session.add(stoat)session.flush()session.expunge_all()critter = session.query(Animal).filter(Animal.name == u'stoat').one()print critter[u'color']print critter[u'cuteness']critter[u'cuteness'] = u'very'print 'changing cuteness:'metadata.bind.echo = Truesession.flush()metadata.bind.echo = Falsesession.close()

这个解决方案允许我们将属性存储在单独的表中,并且可以在 Item 对象中使用它们。

希望这个详细的解释对您有所帮助。如果您还有其他问题,请随时提出。

SQLAlchemy 的 ORM 映射功能简化了数据库操作,允许通过 Python 对象轻松地进行增删改查,同时保持数据的完整性。


http://www.ppmy.cn/server/166684.html

相关文章

【大模型】DeepSeek与chatGPT的区别以及自身的优势

目录 一、前言二、核心技术对比2.1 模型架构设计2.1.1 ChatGPT的Transformer架构2.1.2 DeepSeek的混合架构 2.2 训练数据体系2.2.1 ChatGPT的数据特征2.2.2 DeepSeek的数据策略 三、应用场景对比3.1 通用场景表现3.1.1 ChatGPT的强项领域3.2.2 DeepSeek的专项突破 3.3 响应效率…

2025.2.9 每日学习记录2:技术报告写了一半+一点点读后感

0.近期主任务线 1.完成小论文准备 目标是3月份完成实验点1的全部实验和论文。 2.准备教资笔试 打算留个十多天左右&#xff0c;一次性备考笔试的三个科目 1.实习申请技术准备&#xff1a;微调、Agent、RAG 1.今日完成任务 1.电子斗蛐蛐&#xff08;文本书写领域&am…

MATLAB中extract 函数用法

目录 语法 说明 示例 从地址中提取邮政编码 提取在数值位置处的字符 extract函数的功能是从字符串中提取子字符串。 语法 newStr extract(str,pat) newStr extract(str,pos) 说明 newStr extract(str,pat) 返回 str 中与 pat 指定的模式匹配的任何子字符串。 如果 s…

Pdf手册阅读(1)--数字签名篇

PDF支持的数字签名&#xff0c; 不仅仅是公私钥签名&#xff0c;还可以是指纹、手写、虹膜等生物识别签名。 参考资料&#xff1a; Pdf reference 1.7 GitCode - 全球开发者的开源社区,开源代码托管平台 Digital signature in a pdf https://www.adobe.com/devnet-docs/acro…

机器学习 - 线性回归(最大后验估计)

最大似然估计的一个缺点是当训练数据比较少时会发生过拟合&#xff0c;估计的参数可能不准确.为了避免过拟合&#xff0c;我们可以给参数加上一些先验知识. 一、先从最大似然估计的一个缺点入手 最大似然估计&#xff08;MLE&#xff09;在处理小样本数据时&#xff0c;容易发…

【计算机中级职称 信息安全工程师 备考】DES算法的经典案例

案例 己知DES算法S盒如下&#xff0c; 如果该S盒的输入为001011&#xff0c; 则其二进制输出为()

编写Bash实现Linux网络流量监控统计,无需额外工具

项目需求&#xff1a; 编写Linux脚本&#xff0c;运行后可以统计所有网口的流量汇总数据&#xff0c;对出口流量区分内网流量和公网流量&#xff0c;并自动保存到日志文件。 运行效果&#xff1a; 完整代码&#xff1a; #!/bin/bash# 日志文件路径 LOG_FILE"/var/log/n…

[概率论] 随机变量

Kolmogorov 定义的随机变量是基于测度论和实变函数的。这是因为随机变量的概念需要精确地定义其可能的取值、发生的概率以及这些事件之间的依赖关系。 测度论&#xff1a;在数学中&#xff0c;测度论是用来研究集合大小的理论&#xff0c;特别是无穷可数集和无界集的大小。对于…