flask之sqlalchemy ORM 一对多、多对多

一、一对多关系

爱好和人的关系,一个爱好可以对应多个人

1、models 建立表关系

# 1 导入一些模块
import datetime
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base, \
    relationship  # 新的变成它,老的会有  from sqlalchemy.ext.declarative import declarative_base  Warning

from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index

# 2 得到所有表模型需要继承 基类
Base = declarative_base()

class Hobby(Base):
    __tablename__ = 'hobby'
    id = Column(Integer, primary_key=True)
    caption = Column(String(50), default='篮球')

    def __str__(self):
        return self.caption

class Person(Base):
    __tablename__ = 'person'
    id = Column(Integer, primary_key=True)
    name = Column(String(32), index=True, nullable=True)
    # hobby指的是tablename而不是类名,uselist=False
    hobby_id = Column(Integer, ForeignKey("hobby.id"))  # 关联字段写在多的一方

    # 跟数据库无关,不会新增字段,只用于快速链表操作
    # 类名,backref用于反向查询
    hobby = relationship('Hobby', backref='pers')  # 等同于django中的relate_name,反向查询替换表名小写的

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.name

补充:

1.1 __str__ 和 __repr__ 方法

__str__ 作用:

  • __str__ 方法用于定义对象的“用户友好”字符串表示形式。

  • 当你使用 print(obj) 或 str(obj) 时,Python 会调用 __str__ 方法来获取对象的字符串表示。

  • 这个方法通常用于向用户展示对象的描述性信息。

user = Users(name="Alice")
print(user)  # 输出: Alice

__repr__作用:

  • __repr__ 方法用于定义对象的“开发者友好”字符串表示形式。

  • 当你直接在交互式环境中输入对象名称(如 obj)或使用 repr(obj) 时,Python 会调用 __repr__ 方法。

  • 这个方法通常用于调试和开发,返回的字符串应该尽可能明确地描述对象的状态。

  • 如果 __str__ 方法未定义,__repr__ 会作为 __str__ 的备用方法。

def __repr__(self):
    return f"<User(id={self.id}, name={self.name}, email={self.email})>"

1.2、 relationship 不会新增成字段只是用于连表查询

 hobby = relationship('Hobby', backref='pers') 

  • 'Hobby'

    • 指定关联的模型类为 Hobby

  • backref='pers'

    • 在 Hobby 模型中创建一个 pers 属性,用于反向查询与该 Hobby 关联的所有 Person 对象。

    • 例如,可以通过 hobby.pers 获取所有喜欢该 Hobby 的 Person 对象。

正向查询

通过 Person 对象访问关联的 Hobby 对象:

person = session.query(Person).all()
print(person[1].name)
print(person[1].hobby)

## 结果
dapeng
乒乓球

反向查询

通过 Hobby 对象访问关联的 Person 对象:

hobby = session.query(Hobby).all()
print(hobby[1])
print(hobby[1].pers)  # 获取所有喜欢该 Hobby 的 Person 对象

## 结果
乒乓球
[jingzhiz, dapeng, lisi]

2、 一对多关系新增和查询

笨办法:先添加爱好再添加人和对应爱好字段

hobby = Hobby(caption='乒乓球')
session.add(hobby)
person = Person(name='lisi', hobby_id=2)  # hobby_id=1 只能放数字
session.add(person)
session.commit()
session.close()

高级点的用法

hobby 放对象,用对象做关联,但是必须用 relationship

hobby = Hobby(caption='橄榄球')
person = Person(name='彭于晏', hobby=hobby)
session.add(person)
session.commit()
session.close()

二、多对多关系

1、models创建表关系

# 1 导入一些模块
import datetime
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base, \
    relationship  # 新的变成它,老的会有  from sqlalchemy.ext.declarative import declarative_base  Warning

from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index

# 2 得到所有表模型需要继承 基类
Base = declarative_base()

#  多对多
class Boy2Girl(Base):
    __tablename__ = 'boy2girl'
    id = Column(Integer, primary_key=True, autoincrement=True)
    girl_id = Column(Integer, ForeignKey('girl.id'))
    boy_id = Column(Integer, ForeignKey('boy.id'))
    ctime = Column(DateTime, default=datetime.datetime.now)  # 约会时间

class Girl(Base):
    __tablename__ = 'girl'
    id = Column(Integer, primary_key=True)
    name = Column(String(64), unique=True, nullable=False)

    # boy = relationship('Boy', secondary='boy2girl', backref='girls')
    def __repr__(self):
        return self.name

class Boy(Base):
    __tablename__ = 'boy'
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(64), unique=True, nullable=False)

    # 与生成表结构无关,仅用于查询方便,放在哪个单表中都可以,这个字段放boy或girl表都可以
    girls = relationship('Girl', secondary='boy2girl', backref='boys')

    def __repr__(self):
        return self.name

# # 4 创建 engine,需要手动创建数据库
engine = create_engine(
    "mysql+pymysql://root:bigdata@192.168.1.238:3306/sqlalchemy01?charset=utf8",
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)

# 5 通过 engine 把上面的表模型,创建到数据库中
Base.metadata.create_all(engine)

2、多对多新增和查询

2.1 新增

新增简单用法

# 先新增人
boy = Boy(name='张三')
girl = Girl(name='小红')
session.add_all([boy, girl])

# 再新增约会
b = Boy2Girl(girl_id=1, boy_id=1)
session.add(b)
session.commit()

高级用法

一起新增多个约会

boy = Boy(name='张五')
girl = Girl(name='大红红红')
# 查出小红
xh = session.query(Girl).filter_by(name='小红').first()
boy.girls = [girl, xh]
session.add(boy)
session.commit()

2.2 查询

正向查询

# 查询:张五跟谁约过
zw = session.query(Boy).filter_by(name='张五').first()
print(zw.girls)  # 基于对象的跨表的正向查询

反向查询

xh = session.query(Girl).filter_by(name='小红').first()
print(xh.boys)  # 基于对象的跨表的正向查询

注意:

这里的xh.boys 是Boy表中 girls = relationship('Girl', secondary='boy2girl', backref='boys')   backref 指定的字段

 

posted @ 2025-01-15 16:03  凡人半睁眼  阅读(40)  评论(0)    收藏  举报