参考源:http://blog.csdn.net/fgf00/article/details/52949973
一、ORM介绍
如果写程序用pymysql和程序交互,那是不是要写原生sql语句。如果进行复杂的查询,那sql语句就要进行一点一点拼接,而且不太有重用性,扩展不方便。而且写的sql语句可能不高效,导致程序运行也变慢。 为了避免把sql语句写死在代码里,有没有一种方法直接把原生sql封装好了并且以你熟悉的方式操作,像面向对象那样? orm(object relational mapping),就是对象映射关系程序,简单来说我们类似python这种面向对象的程序来说一切皆对象,但是我们使用的数据库却都是关系型的,为了保证一致的使用习惯,通过orm将编程语言的对象模型和数据库的关系模型建立映射关系,这样我们在使用编程语言对数据库进行操作的时候可以直接使用编程语言的对象模型进行操作就可以了,而不用直接使用sql语言。
ORM 相当于把数据库也给你实例化了,在代码操作mysql中级又加了orm这一层。 orm的优点:
- 隐藏了数据访问细节,“封闭”的通用数据库交互,ORM的核心。他使得我们的通用数据库交互变得简单易行,并且完全不用考虑该死的SQL语句。快速开发,由此而来。
- ORM使我们构造固化数据结构变得简单易行。
缺点:
- 无可避免的,自动化意味着映射和关联管理,代价是牺牲性能(早期,这是所有不喜欢ORM人的共同点)。现在的各种ORM框架都在尝试使用各种方法来减轻这块(LazyLoad,Cache),效果还是很显著的。
二,SQLAchemy
SQLAchemy是python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作,
简言之便是:将对象转换成SQL,然后使用数据API执行SQL并获取执行结果。
ORM框架的作用就是把数据库表的一行记录与一个对象互相做自动转换。 正确使用ORM的前提是了解关系数据库的原理。 ORM就是把数据库表的行与相应的对象建立关联,互相转换。 由于关系数据库的多个表还可以用外键实现一对多、多对多等关联,相应地, ORM框架也可以提供两个对象之间的一对多、多对多等功能。
sqlalchemy的安装:同其它。
SQLAlchemy本身无法操作数据库,其必须以来pymsql等第三方插件,Dialect用于和数据API进行交流,
根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:
MySQL-Python mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname> pymysql mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]
更多:
MySQL-Python mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname> pymysql mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>] MySQL-Connector mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname> cx_Oracle oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...] 更多详见:http://docs.sqlalchemy.org/en/latest/dialects/index.html
1. 创建表 ( declarative:宣言的,陈述的。)
import sqlalchemy from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String # 定义引擎(创建实例),并连接oldboydb库 engine = create_engine("mysql+pymysql://root:1234@localhost/oldboydb", encoding='utf-8', echo=True) #echo=True把所有的信息都打印出来。 # echo=True 显示信息 Base = declarative_base() # 生成orm基类。declarative_base() 创建了一个 Base类,这个类的子类可以自动与一个表关联。 class User(Base): #类User继承Base基类 __tablename__ = 'user' # 表名 id = Column(Integer, primary_key=True) name = Column(String(32)) password = Column(String(64)) Base.metadata.create_all(engine) #创建表结构 (这里是父类调子类)。把所有继承我的儿子们都创建了。会找到 Base 的所有子类,并在数据库中建立这些表。
运行,显示相关信息(echo=True),包括生成的sql语句:
create table user ( id integer not null auto_increment, name varchar(32), password varchar(64), primary key (id) )
除上面的创建之外,还有一种创建表的方式
- 定义:描述数据的数据,对数据及信息资源的描述性信息。元数据(Metadata),又称中介数据、中继数据,为描述数据的数据(data about data),主要是描述数据属性(property)的信息,用来支持如指示存储位置、历史数据、资源查找、文件记录等功能。
- 元数据的作用和意义: 元数据是关于数据的描述性数据信息,说明数据内容、质量、状况和其他有关特征的背景信息。其目的是促进数据集的高效利用,并为计算机辅助软件工程服务。
- 元数据(MetaData),即定义数据的数据。打个比方,就好像我们要想搜索一首歌(歌本身是数据),而我们可以通过歌名,作者,专辑等信息来搜索,那么这些歌名,作者,专辑等等就是这首歌的元数据。因此数据库的元数据就是一些注明数据库信息的数据
此段程序亲测有问题~~~~
from sqlalchemy import Table, MetaData, Column, Integer, String, ForeignKey from sqlalchemy.orm import mapper metadata = MetaData() user = Table('user', metadata, Column('id', Integer, primary_key=True), Column('name', String(50)), Column('fullname', String(50)), Column('password', String(12)) ) class User(object): def __init__(self, name, fullname, password): self.name = name self.fullname = fullname self.password = password mapper(User, user) # 类User 和 user关联起来 # the table metadata is created separately with the Table construct, # then associated with the User class via the mapper() function # 如果数据库里有,就不会创建了。
最基本的表我们创建好了,那我们开始用orm创建一条数据试试
2、插入一条数据
#创建表结构 import sqlalchemy from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String from sqlalchemy.orm import sessionmaker # 创建实例(定义引擎),并连接oldboydb库 engine = create_engine("mysql+pymysql://root:1234@localhost/oldboydb") # echo=True 显示信息 Base = declarative_base() # 生成orm基类 class User(Base): __tablename__ = 'user' # 表名 id = Column(Integer, primary_key=True) name = Column(String(32)) password = Column(String(64)) Base.metadata.create_all(engine) #创建表结构 (这里是父类调子类) # 创建与数据库的会话session类 ,注意,这里返回给session的是个class,不是实例 Session_class=sessionmaker(bind=engine) # 实例和engine绑定 Session=Session_class() # 生成session实例,相当于游标 user_obj=User(name='AAAA',password='1234') # 生成你要创建的数据对象
print(user_obj.name,user_obj.id) # 此时还没创建对象呢,不信你打印一下id发现还是None
Session.add(user_obj) # 把要创建的数据对象添加到这个session里, 一会统一创建
print(user_obj.name,user_obj.id) #此时也依然还没创建 Session.commit() #到此才统一提交,创建数据
没有注释的代码
import sqlalchemy from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String from sqlalchemy.orm import sessionmaker engine = create_engine("mysql+pymysql://root:1234@localhost/oldboydb") Base = declarative_base() class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True) name = Column(String(32)) password = Column(String(64)) Base.metadata.create_all(engine) Session_class=sessionmaker(bind=engine) Session=Session_class() user_obj=User(name='AAAA',password='1234') Session.add(user_obj) Session.commit()
老师所述的过程:创建1个类---------生成1个实例---------用面向对象的形式创建1条记录--------把要创建的数据对象添加到实例里面-------commit一下,创建成功。
自己的理解:
1. 创建socket,engine=create_engine("mysql+pymysql://root:1234@localhost/oldboydb")
2. 生成1个基类
3. 继承基类,创建表结构
4. 创建1个session类
5. 生成1个session实例,相当于游标
6. 创建1条数据
7. commit
3.1、查询
此时的表结构如下:
#Session.commit() 查询是不需要commit的,可以把这句注释掉。
my_user = Session.query(User).filter_by(name="fgf").first() # 查询 print(my_user)
import sqlalchemy from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String from sqlalchemy.orm import sessionmaker engine = create_engine("mysql+pymysql://root:1234@localhost/oldboydb") Base = declarative_base() class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True) name = Column(String(32)) password = Column(String(64)) Base.metadata.create_all(engine) Session_class=sessionmaker(bind=engine) Session=Session_class() data=Session.query(User).filter_by(name='AAAA').all() print(data) #user_obj=User(name='BBBB',password='1234') #Session.add(user_obj) #Session.commit() 查询不需要commit
此时你看到的输出是这样的应该
<__main__.User object at 0x7f0a5a3dea20>
sqlalchemy把返回的数据映射成一个对象啦,这样你调用每个字段就可以跟调用对象属性一样啦,like this..
data=Session.query(User).filter_by(name='AAAA').all()
print(data[0].name,data[0].password)
# 输出
AAAA 1234
不过刚才显示的内存对象对址没办法分清返回的是什么数据的,除非打印具体字段看一下,如果想让它变的可读,只需在定义表的类下面加上这样的代码
def __repr__(self): return "<User(name='%s', password='%s')>" % ( self.name, self.password)
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker
engine = create_engine("mysql+pymysql://root:1234@localhost/oldboydb")
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String(32))
password = Column(String(64))
def __repr__(self):
return "<%s name:%s>"%(self.id,self.name)
Base.metadata.create_all(engine)
Session_class=sessionmaker(bind=engine)
Session=Session_class()
data=Session.query(User).filter_by().all() #查所有数据
print(data)
#user_obj=User(name='BBBB',password='1234')
#Session.add(user_obj)
#Session.commit() 查询不需要commit
显示结果:
获取所有数据
print(Session.query(User.name,User.id).all() ) #获取所有数据
Session.query(User).filter(User.id>2).all()
Session.query(User).filter(User.id>2).filter(User.id<4).all()
Session.query(User).filter(User.id==2).all()
Session.query(User).filter_by(id=2).all()
data=Session.query(User).filter_by().first()
3.2、多条件查询
filter_by与filter
my_user1 = Session.query(User).filter(User.id>2).all() my_user2 = Session.query(User).filter_by(id=27).all() # filter_by相等用‘=’ my_user3 = Session.query(User).filter(User.id==27).all() # filter相等用‘==’ print(my_user1,' ',my_user2,' ',my_user3)
多条件查询
objs = Session.query(User).filter(User.id>0).filter(User.id<7).all()
上面2个filter的关系相当于 user.id >1 AND user.id <7 的效果
4、修改
data = Session.query(User).filter_by(name="AAAA").first() data.password = "4567" Session.commit()
5、回滚
data = Session.query(User).filter_by(id=1).first() data.name = "Jack" fake_user = User(name='Rain', password='12345') #创建了一个新的用户 Session.add(fake_user) print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all() ) #这时看session里有你刚添加和修改的数据 Session.rollback() #此时你rollback一下 print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all() ) #再查就发现刚才添加的数据没有了。 # Session # Session.commit()
实例
import sqlalchemy from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String from sqlalchemy.orm import sessionmaker engine = create_engine("mysql+pymysql://root:1234@localhost/oldboydb") Base = declarative_base() class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True) name = Column(String(32)) password = Column(String(64)) def __repr__(self): return "<%s name:%s>"%(self.id,self.name) Base.metadata.create_all(engine) Session_class=sessionmaker(bind=engine) Session=Session_class() new_user=User(name='CCCC',password='1357') Session.add(new_user) print(Session.query(User).filter_by(name='CCCC').all()) Session.rollback() print('after rollback') print(Session.query(User).filter_by(name='CCCC').all()) Session.commit() #查询不需要commit
打印结果:
C:abccdxdddOldboypython-3.5.2-embed-amd64python.exe C:/abccdxddd/Oldboy/Py_Exercise/Day12/orm_basic2.1.py [<4 name:CCCC>] after rollback [] Process finished with exit code 0
创建完以后直接打印,发现数据已经存在了。rollback一下,再打印,发现数据为空了。
说明数据曾经存在过。
6、统计和分组
统计 count,MySQL默认是不区分大小写的,所以统计的时候,会统计所有的不区分大小写的匹配信息
Session.query(User).filter(User.name.like("f%")).count() # mysql不区分大小写
Session.query(User).filter_by(name='DDDD').count()
分组 group_by
from sqlalchemy import func print(Session.query(User.name,func.count(User.name)).group_by(User.name).all() )
程序
import sqlalchemy from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String from sqlalchemy.orm import sessionmaker from sqlalchemy import func engine = create_engine("mysql+pymysql://root:1234@localhost/oldboydb") Base = declarative_base() class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True) name = Column(String(32)) password = Column(String(64)) def __repr__(self): return "<%s name:%s>"%(self.id,self.name) Base.metadata.create_all(engine) Session_class=sessionmaker(bind=engine) Session=Session_class() print(Session.query(User.name,func.count(User.name)).group_by(User.name).all() ) Session.commit() #查询不需要commit
结果:
C:abccdxdddOldboypython-3.5.2-embed-amd64python.exe C:/abccdxddd/Oldboy/Py_Exercise/Day12/orm_basic2.1.py [('AAAA', 1), ('BBBB', 1), ('DDDD', 3)] Process finished with exit code 0
7. 删除 del
7.1、外键关联
准备工作:先删除原来的旧表,再创建一个新表,然后插入数据
建立2个新表
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Enum,DATE,ForeignKey
from sqlalchemy.orm import sessionmaker,relationship
# 创建实例,并连接test库
engine = create_engine("mysql+pymysql://root:1234@localhost/oldboydb",
encoding='utf-8')
Base = declarative_base() # 生成orm基类
class Student(Base):
__tablename__="student"
id=Column(Integer,primary_key=True)
name=Column(String(32),nullable=False)
register_date=Column(DATE,nullable=False)
def __repr__(self):
return "<%s name:%s>"%(self.id,self.name)
class StudyRecord(Base):
__tablename__="study_record"
id=Column(Integer,primary_key=True)
day=Column(Integer,nullable=False)
status=Column(String(32),nullable=False)
stu_id=Column(Integer,ForeignKey("student.id")) #创建外键
def __repr__(self):
return "<%s day:%s>"%(self.id,self.day)
Base.metadata.create_all(engine) #创建表结构
接下来连续插入好几条数据以后,对它进行查询。
因为不知名的报错,所以分开对表进行数据输入:
给student表创建数据
import sqlalchemy from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, Enum,DATE,ForeignKey from sqlalchemy.orm import sessionmaker,relationship engine = create_engine("mysql+pymysql://root:1234@localhost/oldboydb", encoding='utf-8') Base = declarative_base() # 生成orm基类 class Student(Base): __tablename__="student" id=Column(Integer,primary_key=True) name=Column(String(32),nullable=False) register_date=Column(DATE,nullable=False) def __repr__(self): return "<%s name:%s>"%(self.id,self.name) Base.metadata.create_all(engine) #创建表结构 Session_class = sessionmaker(bind=engine) # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例 Session = Session_class() # 生成session实例 #cursor s1 = Student(name="Alex",register_date="2014-05-21") s2 = Student(name="Jack",register_date="2014-03-21") s3 = Student(name="Rain",register_date="2014-02-21") s4 = Student(name="Eric",register_date="2013-01-21") Session.add_all([s1,s2,s3,s4]) Session.commit()
给StudyRecord表创建数据
import sqlalchemy from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, Enum,DATE,ForeignKey from sqlalchemy.orm import sessionmaker,relationship engine = create_engine("mysql+pymysql://root:1234@localhost/oldboydb", encoding='utf-8') Base = declarative_base() # 生成orm基类 class Student(Base): __tablename__="student" id=Column(Integer,primary_key=True) name=Column(String(32),nullable=False) register_date=Column(DATE,nullable=False) def __repr__(self): return "<%s name:%s>"%(self.id,self.name) class StudyRecord(Base): __tablename__="study_record" id=Column(Integer,primary_key=True) day=Column(Integer,nullable=False) status=Column(String(32),nullable=False) stu_id=Column(Integer,ForeignKey("student.id")) def __repr__(self): return "<%s day:%s>"%(self.id,self.day) Base.metadata.create_all(engine) #创建表结构 Session_class = sessionmaker(bind=engine) # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例 Session = Session_class() # 生成session实例 #cursor study_obj1=StudyRecord(day=1,status="YES",stu_id=1) study_obj2=StudyRecord(day=2,status="NO",stu_id=1) study_obj3=StudyRecord(day=3,status="YES",stu_id=1) study_obj4=StudyRecord(day=1,status="YES",stu_id=2) Session.add_all([study_obj1,study_obj2,study_obj3,study_obj4]) Session.commit()
至此,准备工作完成,2表内容如下:
先来个简单的查询
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Enum,DATE,ForeignKey
from sqlalchemy.orm import sessionmaker,relationship
engine = create_engine("mysql+pymysql://root:1234@localhost/oldboydb",
encoding='utf-8')
Base = declarative_base() # 生成orm基类
class Student(Base):
__tablename__="student"
id=Column(Integer,primary_key=True)
name=Column(String(32),nullable=False)
register_date=Column(DATE,nullable=False)
def __repr__(self):
return "<%s name:%s>"%(self.id,self.name)
class StudyRecord(Base):
__tablename__="study_record"
id=Column(Integer,primary_key=True)
day=Column(Integer,nullable=False)
status=Column(String(32),nullable=False)
stu_id=Column(Integer,ForeignKey("student.id"))
def __repr__(self):
return "<%s day:%s>"%(self.id,self.day)
Base.metadata.create_all(engine) #创建表结构
Session_class = sessionmaker(bind=engine) # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
Session = Session_class() # 生成session实例 #cursor
stu_obj=Session.query(Student).filter(Student.name=='Alex').first()
print(stu_obj)
Session.commit()
返回结果:
<1 name:Alex>
接下来进行复杂查询,Alex一共上了几节课,涉及到2张表联合查询了。
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Enum,DATE,ForeignKey
from sqlalchemy.orm import sessionmaker,relationship
engine = create_engine("mysql+pymysql://root:1234@localhost/oldboydb",
encoding='utf-8')
Base = declarative_base() # 生成orm基类
class Student(Base):
__tablename__="student"
id=Column(Integer,primary_key=True)
name=Column(String(32),nullable=False)
register_date=Column(DATE,nullable=False)
def __repr__(self):
return "<%s name:%s>"%(self.id,self.name)
class StudyRecord(Base):
__tablename__="study_record"
id=Column(Integer,primary_key=True)
day=Column(Integer,nullable=False)
status=Column(String(32),nullable=False)
stu_id=Column(Integer,ForeignKey("student.id"))
student=relationship("Student",backref="my_study_record")
#关联Student表,在studyrecord里面通过student字段可以去查Student里面的所有字段。
#允许你在Student表里通过backref字段反向查出所有它在studyRecord表里的关联项数据.
#这个nb,添加关系,反查(在内存里)
def __repr__(self):
return "<day:%s status:%s>"%(self.day,self.status)
Base.metadata.create_all(engine) #创建表结构
Session_class = sessionmaker(bind=engine) # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
Session = Session_class() # 生成session实例 #cursor
stu_obj=Session.query(Student).filter(Student.name=='Alex').first()
print(stu_obj.my_study_record)
Session.commit()
输出结果:
C:abccdxdddOldboypython-3.5.2-embed-amd64python.exe C:/abccdxddd/Oldboy/Py_Exercise/Day12/orm_fk3.py [<day:1 status:YES>, <day:2 status:NO>, <day:3 status:YES>] Process finished with exit code 0
通过修改字段,让输出结果更人性化
class StudyRecord(Base): __tablename__="study_record" id=Column(Integer,primary_key=True) day=Column(Integer,nullable=False) status=Column(String(32),nullable=False) stu_id=Column(Integer,ForeignKey("student.id")) student=relationship("Student",backref="my_study_record") #关联Student表,在studyrecord里面通过student字段可以去查Student里面的所有字段。 #允许你在Student表里通过backref字段反向查出所有它在studyRecord表里的关联项数据. #这个nb,添加关系,反查(在内存里)
#外键是在数据库中真正存在的,relationship是在内存中存在的。 def __repr__(self): return "<%s day:%s status:%s>"%(self.student.name,self.day,self.status)
输出结果:
C:abccdxdddOldboypython-3.5.2-embed-amd64python.exe C:/abccdxddd/Oldboy/Py_Exercise/Day12/orm_fk3.py [<Alex day:1 status:YES>, <Alex day:2 status:NO>, <Alex day:3 status:YES>] Process finished with exit code 0
连表
ret = session.query(Users, Favor).filter(Users.id == Favor.nid).all() # 以下两种 必须表之间有外键关联才能查
ret = session.query(Person).join(Favor).all() ret = session.query(Person).join(Favor, isouter=True).all()
第一种示例:
先新建2张表
7.2、多外键关联
多外键关联,并且关联同一个表。
下表中,Customer表有2个字段都关联了Address表,
orm_many_fk.py程序如下
from sqlalchemy import Integer, ForeignKey, String, Column from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship from sqlalchemy import create_engine Base = declarative_base() #创建对象的基类 class Customer(Base): __tablename__ = 'customer' id = Column(Integer, primary_key=True) name = Column(String(64)) # 账单地址和邮寄地址 都关联同一个地址表 billing_address_id = Column(Integer, ForeignKey("address.id")) shipping_address_id = Column(Integer, ForeignKey("address.id")) billing_address = relationship("Address", foreign_keys=[billing_address_id])
#relationship的作用是关联Address表,这样在customer表里面通过billing_address字段可以去查address里面的所有字段。加上foreign_keys是为了让sqlachemy分清哪个外键是对应哪个字段。
shipping_address = relationship("Address", foreign_keys=[shipping_address_id]) class Address(Base): __tablename__ = 'address' id = Column(Integer, primary_key=True) street = Column(String(64)) city = Column(String(64)) state = Column(String(64)) def __repr__(self): return self.street engine = create_engine("mysql+pymysql://root:123456@localhost/test", encoding='utf-8') Base.metadata.create_all(engine) # 创建表结构
#要不然反向查询的时候就会分不清哪个是哪个,进而报错。
show出来的信息如下:
mysql> show create table customer;
|
| customer | CREATE TABLE `customer` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(64) DEFAULT NULL,
`billing_address_id` int(11) DEFAULT NULL,
`shipping_address_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `billing_address_id` (`billing_address_id`),
KEY `shipping_address_id` (`shipping_address_id`),
CONSTRAINT `customer_ibfk_1` FOREIGN KEY (`billing_address_id`) REFERENCES `ad
dress` (`id`),
CONSTRAINT `customer_ibfk_2` FOREIGN KEY (`shipping_address_id`) REFERENCES `a
ddress` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+----------+--------------------------------------------------------------------
可以看出,有了2个外键。
mysql> desc customer; +---------------------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------------------+-------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(64) | YES | | NULL | | | billing_address_id | int(11) | YES | MUL | NULL | | | shipping_address_id | int(11) | YES | MUL | NULL | | +---------------------+-------------+------+-----+---------+----------------+ 4 rows in set (0.01 sec)
正常写的时候,表结构单独写一个模块 orm_API.py。添加数据
from Day12 import orm_many_fk from sqlalchemy.orm import sessionmaker Session_class = sessionmaker(bind=orm_many_fk.engine) # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例 session = Session_class() # 生成session实例 #cursor addr1 = orm_many_fk.Address(street="Tiantongyuan", city="ChangPing", state="BJ") addr2 = orm_many_fk.Address(street="Wudaokou", city="Haidian", state="BJ") addr3 = orm_many_fk.Address(street="Yanjiao", city="LangFang", state="HB") session.add_all([addr1,addr2,addr3]) c1 = orm_many_fk.Customer(name="Alex", billing_address= addr1,shipping_address=addr2) c2 = orm_many_fk.Customer(name="Jack", billing_address= addr3,shipping_address=addr3) session.add_all([c1,c2]) session.commit() #obj = session.query(orm_many_fk.Customer).filter(orm_many_fk.Customer.name=="Alex").first() #print(obj.name,obj.billing_address,obj.shipping_address) # 查询
运行结果:
查询结果:
C:abccdxdddOldboypython-3.5.2-embed-amd64python.exe C:/abccdxddd/Oldboy/Py_Exercise/Day12/orm_API.py Alex Tiantongyuan Wudaokou Process finished with exit code 0
出处:http://blog.csdn.net/fgf00/article/details/52949973
7.3 多对多关系
现在来设计一个能描述“图书”与“作者”的关系的表结构,需求是
- 一本书可以有好几个作者一起出版
- 一个作者可以写好几本书 此时你会发现,用之前学的外键好像没办法实现上面的需求了 那怎么办呢? 此时,我们可以再搞出一张中间表,就可以了
这样就相当于通过book_m2m_author表完成了book表和author表之前的多对多关联 双向一对多,就是多对多。
用orm如何表示呢?
orm_m2m.py的程序
# 创建表结构 from sqlalchemy import Table, Column, Integer,String,DATE, ForeignKey from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker Base = declarative_base() # 第三张表 自己创建。不需要手动管理,orm自动维护 book_m2m_author = Table('book_m2m_author', Base.metadata, Column('book_id',Integer,ForeignKey('books.id')), Column('author_id',Integer,ForeignKey('authors.id')), ) class Book(Base): __tablename__ = 'books' id = Column(Integer,primary_key=True) name = Column(String(64)) pub_date = Column(DATE) # book表不知道第三张表,所以关联一下第三张表 authors = relationship('Author',secondary=book_m2m_author,backref='books') def __repr__(self): return self.name class Author(Base): __tablename__ = 'authors' id = Column(Integer, primary_key=True) name = Column(String(32)) def __repr__(self): return self.name engine = create_engine("mysql+pymysql://root:1234@localhost/oldboydb", encoding='utf-8') Base.metadata.create_all(engine) # 创建表结构
运行看效果:
#做了复合键了。
orm_m2m_API.py的程序如下:
# 添加数据 import orm_m2m from sqlalchemy.orm import sessionmaker Session_class = sessionmaker(bind=orm_m2m.engine) # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例 session = Session_class() # 生成session实例 #cursor # 创建书 b1 = orm_m2m.Book(name="learn python with Alex",pub_date="2014-05-2") b2= orm_m2m.Book(name="learn Zhangbility with Alex",pub_date="2015-05-2") b3 = orm_m2m.Book(name="Learn hook up girls with Alex",pub_date="2016-05-2") # 创建作者 a1 = orm_m2m.Author(name="Alex") a2 = orm_m2m.Author(name="Jack") a3 = orm_m2m.Author(name="Rain") # 关联关系 b1.authors = [a1,a3] b3.authors = [a1,a2,a3] session.add_all([b1,b2,b3,a1,a2,a3]) session.commit()
此时的数据
查询:
# 重要是查询 author_obj = session.query(orm_m2m.Author).filter(orm_m2m.Author.name=="alex").first() print(author_obj.books[0:])
author_obj = session.query(orm_m2m.Author).filter(orm_m2m.Author.name=="alex").first()
print(author_obj.books[1].pub_date)
book_obj = session.query(orm_m2m.Book).filter(orm_m2m.Book.id==2).first() #反向查 print(book_obj.authors)
多对多删除
删除数据时不用管book_m2m_authors,sqlalchemy会自动帮你把对应的数据删除
通过书删除作者
author_obj =s.query(Author).filter_by(name="Jack").first() book_obj = s.query(Book).filter_by(name="跟Alex学把妹").first() book_obj.authors.remove(author_obj) #从一本书里删除一个作者 s.commit()
直接删除作者
删除作者时,会把这个作者跟所有书的关联关系数据也自动删除
author_obj =s.query(Author).filter_by(name="Alex").first() # print(author_obj.name , author_obj.books) s.delete(author_obj) s.commit()
8、中文问题
先查看数据库的字符集
MariaDB [test]> show create database test; +----------+-----------------------------------------------------------------+ | Database | Create Database | +----------+-----------------------------------------------------------------+ | test | CREATE DATABASE `test` /*!40100 DEFAULT CHARACTER SET latin1 */ | +----------+-----------------------------------------------------------------+
sqlalchemy 连接时用如下方式指定
engine = create_engine("mysql+pymysql://root:1234@localhost/oldboydb?charset=utf8")