python之ORM的使用(2)
4.多外键关联
下表中,Customer表中有两个字段都关联了Address表
1 from sqlalchemy import Integer, ForeignKey, String, Column 2 from sqlalchemy.ext.declarative import declarative_base 3 from sqlalchemy.orm import relationship 4 5 Base = declarative_base() 6 7 class Customer(Base): 8 __tablename__ = 'customer' 9 id = Column(Integer, primary_key=True) 10 name = Column(String) 11 12 billing_address_id = Column(Integer, ForeignKey("address.id")) 13 shipping_address_id = Column(Integer, ForeignKey("address.id")) 14 15 billing_address = relationship("Address") 16 shipping_address = relationship("Address") 17 18 class Address(Base): 19 __tablename__ = 'address' 20 id = Column(Integer, primary_key=True) 21 street = Column(String) 22 city = Column(String) 23 state = Column(String)
创建表结构好像没什么问题,但是插入数据时,或出现错误:
1 sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join 2 condition between parent/child tables on relationship 3 Customer.billing_address - there are multiple foreign key 4 paths linking the tables. Specify the 'foreign_keys' argument, 5 providing a list of those columns which should be 6 counted as containing a foreign key reference to the parent table.
因为添加的两个外键之间分不清到底那个是哪个了,所以要让他们能够区分开:
1 class Customer(Base): 2 __tablename__ = 'customer' 3 id = Column(Integer, primary_key=True) 4 name = Column(String) 5 6 billing_address_id = Column(Integer, ForeignKey("address.id")) 7 shipping_address_id = Column(Integer, ForeignKey("address.id")) 8 9 billing_address = relationship("Address", foreign_keys=[billing_address_id]) 10 shipping_address = relationship("Address", foreign_keys=[shipping_address_id])
这样子sqlachemy就能分得清那个外键是哪个字段了
5.多对多关系
这种需要应用的场景是:设计一个能够描述“图书”与“作者”的关系表结构,需求是:
1.一本书可以有好几个作者一起出版
2.一个作者可以写好几本书
此时你会发现你需要用到多对关系了!
我们首先创建两张表,用来存储作者和书
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()
class Book(Base): __tablename__ = 'books' id = Column(Integer,primary_key=True) name = Column(String(64)) pub_date = Column(DATE) 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
我们再创建一张第三张表,用来指向需要用到多对多的两张表,把他们关联到第三张表
1 from sqlalchemy import Table, Column, Integer,String,DATE, ForeignKey 2 from sqlalchemy.orm import relationship 3 from sqlalchemy.ext.declarative import declarative_base 4 from sqlalchemy import create_engine 5 from sqlalchemy.orm import sessionmaker 6 7 8 Base = declarative_base() 9 10 book_m2m_author = Table('book_m2m_author', Base.metadata, 11 Column('book_id',Integer,ForeignKey('books.id')), 12 Column('author_id',Integer,ForeignKey('authors.id')),
总体代码:
#一本书可以有多个作者,一个作者又可以出版多本书 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() 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) 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 orm 多对多
接下来创建数据
1 Session_class = sessionmaker(bind=engine) #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例 2 s = Session_class() #生成session实例 3 4 b1 = Book(name="跟Alex学Python") 5 b2 = Book(name="跟Alex学把妹") 6 b3 = Book(name="跟Alex学装逼") 7 b4 = Book(name="跟Alex学开车") 8 9 a1 = Author(name="Alex") 10 a2 = Author(name="Jack") 11 a3 = Author(name="Rain") 12 13 b1.authors = [a1,a2] 14 b2.authors = [a1,a2,a3] 15 16 s.add_all([b1,b2,b3,b4,a1,a2,a3]) 17 18 s.commit()
此时,手动连上mysql,分别查看这3张表,你会发现,book_m2m_author中自动创建了多条纪录用来连接book和author表
1 mysql> select * from books; 2 +----+------------------+----------+ 3 | id | name | pub_date | 4 +----+------------------+----------+ 5 | 1 | 跟老三学Python | NULL | 6 | 2 | 跟老三学把妹 | NULL | 7 | 3 | 跟Alex学装逼 | NULL | 8 | 4 | 跟Alex学开车 | NULL | 9 +----+------------------+----------+ 10 4 rows in set (0.00 sec) 11 12 mysql> select * from authors; 13 +----+------+ 14 | id | name | 15 +----+------+ 16 | 10 | Alex | 17 | 11 | Jack | 18 | 12 | Rain | 19 +----+------+ 20 3 rows in set (0.00 sec) 21 22 mysql> select * from book_m2m_author; 23 +---------+-----------+ 24 | book_id | author_id | 25 +---------+-----------+ 26 | 2 | 10 | 27 | 2 | 11 | 28 | 2 | 12 | 29 | 1 | 10 | 30 | 1 | 11 | 31 +---------+-----------+ 32 5 rows in set (0.00 sec)
此时,我们去用orm查一下数据
1 print('--------通过书表查关联的作者---------') 2 3 book_obj = s.query(Book).filter_by(name="跟Alex学Python").first() 4 print(book_obj.name, book_obj.authors) 5 6 print('--------通过作者表查关联的书---------') 7 author_obj =s.query(Author).filter_by(name="Alex").first() 8 print(author_obj.name , author_obj.books) 9 s.commit()
输出如下:
--------通过书表查关联的作者--------- 跟Alex学Python [Alex, Jack] --------通过作者表查关联的书--------- Alex [跟Alex学把妹, 跟Alex学Python]
多对对删除
删除数据时不用管boo_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()
处理中文:
sqlalchemy设置编码字符集一定要在数据库访问的URL上增加charset=utf8,否则数据库的连接就不是utf8的编码格式
eng = create_engine('mysql://root:root@localhost:3306/test2?charset=utf8',echo=True)
---恢复内容结束---