SQLAchemy 学习(2)

接着上一篇SQLAlchemy学习

relationship

relationship函数是sqlalchemy对关系之间提供的一种便利的调用方式, backref参数则对关系提供反向引用的声明
通过relationship我们可以轻松在实现嵌套将一个实例的属性转换成另一个类的实例。

1
2
3
def get_addresses_from_user(user_name):
user = session.query(User).filter_by(name=user_name).first()
return user.addresses #返回的是Adresses的一个对象

back_populates参数和backref参数功能类似,只是前者提供单向的关系引用,且必须成对存在,但是完成的功能和backref是一样的

定义1:1 1:n m:n 关系

1:n

1
2
3
4
5
6
7
8
9
10
11
12
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
children = relationship("Child", back_populates="parent")
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))
parent = relationship("Parent", back_populates="children")
# 子表类中附加一个 relationship() 方法
# 并且在(父)子表类的 relationship() 方法中使用 relationship.back_populates 参数

1:1

1
2
3
4
5
6
7
8
9
10
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
child = relationship("Child", uselist=False, back_populates="parent")
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))
parent = relationship("Parent", back_populates="child")

和1:n的不同在于,在父表上用uselist参数进行了限定

m:n

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
association_table = Table('association', Base.metadata,
Column('left_id', Integer, ForeignKey('left.id')),
Column('right_id', Integer, ForeignKey('right.id'))
)
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship(
"Child",
secondary=association_table,
back_populates="parents")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
parents = relationship(
"Parent",
secondary=association_table,
back_populates="children")

SQLAlchemy 数据表结构改变(数据迁移)方法

在使用Django的ORM的时候我们可以轻易地通过python manage.py makemigration python manage.py migrate 实现数据表结构的改变
但是在SQLAlchemy这个工具中似乎不支持数据迁移,所以在这里使用到一个另外一个库:SQLAlchemy-migrate
使用这个库我们可以分别写两个文件表示创建数据库的时候和修改数据库的时候:

创建数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from migrate.versioning import api
import os
from model import Base
from parse import engine
from config import *
Base.metadata.create_all(bind=engine)
# 创建表结构
if not (os.path.exists(sqlachemy_migrate_repo)):
api.create(sqlachemy_migrate_repo,'database repository')
api.version_control(postgresql_url,sqlachemy_migrate_repo)
else:
api.version_control(postgresql_url,sqlachemy_migrate_repo)

修改数据表的结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from migrate.versioning import api
import os
import imp
from model import Base
from parse import engine
from config import *
Base.metadata.create_all(bind=engine)
# 迁移表结构
migration = sqlachemy_migrate_repo + '/versions/%03d_migration.py' % (api.db_version(postgresql_url, sqlachemy_migrate_repo) + 1)
tmp_module = imp.new_module('old_model')
old_model = api.create_model(postgresql_url, sqlachemy_migrate_repo)
exec(old_model, tmp_module.__dict__)
script = api.make_update_script_for_model(postgresql_url, sqlachemy_migrate_repo, tmp_module.meta, Base.metadata)
open(migration, 'wt').write(script)
api.upgrade(postgresql_url, sqlachemy_migrate_repo)
print('New migration saved as ' + migration)
print('Current database version: ' + str(api.db_version(postgresql_url, sqlachemy_migrate_repo)))

但是当修改的东西过多的时候,这个时候会报错

Django 中get_or_create 方法的实现

和上面一样在SQLAlchemy中也不存在非常方便的get_or_create的方法,用的比较多的时候,应该有必要重新写一下:

1
2
3
4
5
6
7
8
9
10
def get_or_create(session, model, defaults=None, **kwargs):
instance = session.query(model).filter_by(**kwargs).first()
if instance:
return instance, False
else:
params = dict((k, v) for k, v in kwargs.iteritems() if not isinstance(v, ClauseElement))
params.update(defaults or {})
instance = model(**params)
session.add(instance)
return instance, True

参考:https://codeday.me/bug/20170612/24948.html