一对多



一、外键约束
  • 图示 正向查询
  • 图示 反向查询
  • 知识点:
    • 外键:
      • Column(Integer, ForeignKey('student.id'))
      • 约束:字段被定义为外键,则表明该字段的值,只能是另一张表中相关联的字段值域(多个值)中的一个
      • 关系:外键(1)->值域为(N)“多对一”  # 有外键的表始终是一
      • 作用:连表获取字段
    • relationship:
      • 规定:将relationship放在有外键的表中(方便理解一对多还是多对一)
      • 意义:在内存中创建一个字段,对应另外一张表中的对象
      • 作用:此时不用通过连表就能获取另外一张表中字段
        • favor = relationship("Favor", backref='xxx')
        • A.favor 字段正向查询: 结果为B表中的一个对象,# 外键所在表为一,只能对应值域中一个
        • B.xxx    字段反向查询: 结果为A表中的对象列表,# 值域中的一个,对应外键所在表中的多个
  • 数据初始化:
#!/usr/bin/env python
# -*- encoding:utf-8 -*-


import sqlalchemy
from sqlalchemy import DATE
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 ForeignKey
from sqlalchemy.orm import relationship

engine = create_engine("mysql+pymysql://root:@127.0.0.1/db01?charset=utf8")
Base = declarative_base()
# 创建表结构
class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    u_id = Column(Integer, nullable=False)
    name = Column(String(32), nullable=False)
    age = Column(Integer)
    favor_id = Column(Integer, ForeignKey('favor.id')) # favor 为表名,表示外键约束,真实存放在数据库中

    favor = relationship('Favor', backref='xxx') # Favor 为类名,表示虚拟关系,存放于内存中

class Favor(Base):
    __tablename__ = 'favor'
    id = Column(Integer, primary_key=True)
    type = Column(String(32), nullable=False)

# Base.metadata.create_all(bind=engine)
# Base.metadata.drop_all(bind=engine)

Session_cls = sessionmaker(bind=engine)
session = Session_cls()
# 创建表数据
session.add_all([
    Favor(type='favor-01'),
    Favor(type='favor-02'),
    Favor(type='favor-03'),
    Favor(type='favor-04'),
])
session.add_all([
    User(u_id=2001, name='user1', age=15, favor_id=1),
    User(u_id=2002, name='user2', age=20, favor_id=3),
    User(u_id=2003, name='user3', age=30, favor_id=2),
    User(u_id=2004, name='user4', age=20, favor_id=3),
    User(u_id=2005, name='user5', age=22, favor_id=2),
    User(u_id=2006, name='user6', age=28, favor_id=3),
    User(u_id=2007, name='user7', age=19, favor_id=1),
    User(u_id=2008, name='user8', age=32, favor_id=4),
    User(u_id=2009, name='user9', age=129, favor_id=3),
])
session.commit()
  • 查询:
    • # 查询 user3 有什么爱好
      • # 连表查询
ret = session.query(User).join(Favor).filter(User.name == 'user3').first()
# SELECT user.id AS user_id, user.u_id AS user_u_id, user.name AS user_name, user.age AS user_age, user.favor_id AS user_favor_id
# FROM user INNER JOIN favor ON favor.id = user.favor_id
# WHERE user.name = %(name_1)s
ret2 = session.query(Favor).filter(Favor.id == ret.favor_id).first()
print(ret2.type)
      • # relationship 正向查询
ret = session.query(User).filter(User.name == 'user3').first().favor.type
# first() 获取到一个User对象,.favor获取到对应表中的Favor对象,.type 获取到该对象的type字段
print(ret)
    • # favor-03 爱好的共有哪些人
      • # 连表查询
f_id = session.query(Favor).filter(Favor.type == 'favor-03').first()
print(f_id.id)
ret_lst = session.query(User.name).filter(User.favor_id == f_id.id).all()
print(ret_lst)
      • # relationship 反向查询
ret_lst = session.query(Favor).filter(Favor.type == 'favor-03').first().xxx
# .first() 获取到一个Favor对象,.xxx获取到对应表中的User对象列表,.name 获取该对象的name字段
for ret in ret_lst:
    print(ret.name)
    • # 说明问题:
      • 关于连表操作
        1. 最后想输出哪个字段,该字段对应的表就应该写在 query()中(若加映射,则最终结果为数组)
        2. join 自动会查找外键关系,ON student.id = studyrecord.stu_id
      • 关于relationship:
        1. 所有查询都在一张表中完成



二、多外键问题:
  • 重点:
#!/usr/bin/env python
# -*- encoding:utf-8 -*-


import sqlalchemy
from sqlalchemy import DATE
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 ForeignKey
from sqlalchemy.orm import relationship

engine = create_engine("mysql+pymysql://root:@127.0.0.1/db01?charset=utf8")
Base = declarative_base()

# 创建表结构
# 重点解释
# 因为此时,同一张表的2个字段(收件地址,付款地址),都对应到了另外一张表中(地址表)
# 那么在给地址表插入数据时会报错:不清楚该条数据到底应该对应到“收件地址”还是“付款地址”
# foreign_keys=[billing_address_id]
# 告诉orm ,若要正向查询虚拟字段 billing_address, 则去billing_address_id字段对应的外键去查
# 告诉orm ,若要正向查询虚拟字段 shipping_address, 则去shipping_address_id字段对应的外键去查
class Customer(Base):
    __tablename__ = 'customer'
    id = Column(Integer, primary_key=True)
    name = Column(String(32))

    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], backref='xxx')
    shipping_address = relationship("Address", foreign_keys=[shipping_address_id], backref='yyy')

class Address(Base):
    __tablename__ = 'address'
    id = Column(Integer, primary_key=True)
    street = Column(String(32))
    city = Column(String(32))
    state = Column(String(32))

# Base.metadata.drop_all(bind=engine)
# Base.metadata.create_all(bind=engine)

# 插入数据
Session_cls = sessionmaker(bind=engine)
session = Session_cls()
session.add_all([
    Address(street='street-bbb', city='city-bbb', state='state-bbb'),
    Address(street='street-ccc', city='city-ccc', state='state-ccc'),
    Address(street='street-ddd', city='city-ddd', state='state-ddd')
])
session.add_all([
Customer(name='user1', billing_address_id=1, shipping_address_id=2),
Customer(name='user2', billing_address_id=2, shipping_address_id=3),
])
session.commit()

# 正向查询,user2 的付款地址是多少
ret = session.query(Customer).filter(Customer.name == 'user2').first().billing_address.street
print(ret)


# 反向查询,付款地址(billing_address)为street-ccc 有那些人
ret_lst1 = session.query(Address).filter(Address.street == 'street-ccc').first().xxx
for ret in ret_lst1:
    print(ret.name)
# 反向查询,邮寄地址(shipping_address)为street-ccc 有那些人
ret_lst2 = session.query(Address).filter(Address.street == 'street-ccc').first().yyy
for ret in ret_lst2:
    print(ret.name)



 

posted on 2017-01-18 10:05  台灯不太亮  阅读(377)  评论(0编辑  收藏  举报

导航