Python的魔法ORM --《PonyORM教程》 4 高级定

2019-10-12  本文已影响0人  justonlyyo

实体类的高级定义

假设我们有Student(学生),Classroom(班级)和MasterTeacher(班主任)三个类。他们之间的关系如下:

根据上面的需求,我们进行了如下的定义

联合主键

class Student(db.Entity):
    """学生"""
    _table_ = "student"
    name = Required(str, max_len=40)
    master_teacher = Required("MasterTeacher")  # 班主任
    classroom = Required("Classroom")  # 班级

class Classroom(db.Entity):
    """班级"""
    _table_ = "classroom"
    name = Required(str, max_len=40)
    master_teacher = Optional("MasterTeacher")  # 班主任
    students = Set(Student)  # 学生


class MasterTeacher(db.Entity):
    """班主任"""
    _table_ = "master_teacher"
    name = Required(str, max_len=40)
    classroom = Required(Classroom)  # 班级
    students = Set(Student)  # 学生

定义以后,有了一个新的问题:
有班主任同名的现象,这样单依赖名字就无法区别老师了,(比如三一班和三二班的班主任都叫张三)于是提出用老师的名字+班级 确认老师的唯一性,这个问题就解决了(三一班的张三老师和三二班的张三老师),这时候,我们就要用到联合主键了

不要在意上面的需求是否合理,我们提出上述假设的需求的目的只是为了演示一些pony的高级用法。

修改MasterTeacher类的定义

class MasterTeacher(db.Entity):
    """班主任"""
    _table_ = "master_teacher"
    name = Required(str, max_len=40)
    classroom = Required(Classroom)  # 班级
    students = Set(Student)  # 学生
    PrimaryKey(name, classroom)  # 定义一个联合主键

执行

db.generate_mapping(create_tables=True)  # 生成实体,表和映射关系
屏幕截图_1.png

我们会发现数据库中的主键不再是id,而是name和classroom生成的联合主键了。

联合辅助键

如果你需要一个联合辅助键,那么只需要把PrimaryKey换成composite_key即可。

class MasterTeacher(db.Entity):
    """班主任"""
    _table_ = "master_teacher"
    name = Required(str, max_len=40)
    classroom = Required(Classroom)  # 班级
    students = Set(Student)  # 学生
    composite_key(name, classroom)  # 联合辅助键
屏幕截图_4.png

观察数据库,你会发现master_teacher的主键依然是id,只是多个一个而是name和classroom生成的联合辅助键

联合索引

composite_index(name, classroom)  # 联合索引

联合唯一索引

composite_key(name, classroom)  # 联合唯一索引

属性定义

属性定义有很多参数,用于对字段进行约束或者验证。下面介绍一些常用的属性定义,注意,属性定义往往只在特定的字段类型上生效!

  • size=8 ⇔ TINYINT(4)
  • size=16 ⇔ SMALLINT(6)
  • size=24 ⇔ MEDIUMINT(9)
  • size=32 ⇔ INT(11)
  • size=64 ⇔ BIGINT(20)

连接查询

我们按照连接方式分为自然连接,左连接和右连接,我们分别就这3种连接进行示范和说明。

定义2个实体类,Boy和Girl,分别代表男孩和女孩。

实体类代码如下:

class Girl(db.Entity):
    name = Required(str, max_len=40)  # 名字
    lover = Optional("Boy", column="boy_id", nullable=True, default=None, reverse="lover")  # 爱人
    

class Boy(db.Entity):
    name = Required(str, max_len=40)  # 名字
    lover = Optional(Girl, nullable=True, default=None, reverse="lover")  # 爱人

自然连接

把不是单身🐶的小两口的名字打印出来。

    with db_session(sql_debug=True):
        query = select((x.name, x.lover.name) for x in Boy)
        for x in query:
            print(x)

sql_debug 你很容易发现多了一个参数,这个参数的目的就是为了在操作数据库时,把生成的语句打印出来,这样做的目的是: 一旦发现查询结果不对,我们可以参照打印出来的sql语句来判断自己的表达式是否拼写错误。
执行的结果是这样的

SELECT DISTINCT `x`.`name`, `girl`.`name`
FROM `boy` `x`, `girl` `girl`
WHERE `x`.`id` = `girl`.`boy_id`

('Tom', 'Abby')
COMMIT
RELEASE CONNECTION

Process finished with exit code 0

左连接

把所有的男孩子包括他们的情侣的名字打印出来。

    with db_session(sql_debug=True):
        query = left_join((x.name, x.lover.name) for x in Boy)
        for x in query:
            print(x)

执行的结果是这样的

GET NEW CONNECTION
SELECT DISTINCT `x`.`name`, `girl`.`name`
FROM `boy` `x`
  LEFT JOIN `girl` `girl`
    ON `x`.`id` = `girl`.`boy_id`

('Tom', 'Abby')
('John', None)
COMMIT
RELEASE CONNECTION

Process finished with exit code 0

右连接

pony中没有提供右连接方法,如果需要类似的功能,你需要把查询的对象反过来即可。

把所有的女孩子包括他们的情侣的名字打印出来。

    with db_session(sql_debug=True):
        query = left_join((x.name, x.lover.name) for x in Girl)
        for x in query:
            print(x)

查询结果

GET NEW CONNECTION
SELECT DISTINCT `x`.`name`, `boy`.`name`
FROM `girl` `x`
  LEFT JOIN `boy` `boy`
    ON `x`.`boy_id` = `boy`.`id`

('Abby', 'Tom')
('Anna', None)
COMMIT
RELEASE CONNECTION

Process finished with exit code 0

查询实例

下面我们使用例子来说明一些常用的查询函数的使用方法

实体类Worker的定义如下:

from decimal import Decimal


class Worker(db.Entity):
    """工人"""
    name = Required(str, max_len=40)  # 名字
    sex = Required(str, max_len=16)  # 性别
    age = Required(int, size=8)  # 年龄
    salary = Required(Decimal)  # 月薪

表中有如下数据


屏幕截图_8.png

条件查询

    with db_session:
        query = select(x for x in Worker if between(x.age, 22, 35) and x.sex == "男")
        for x in query:
            print(x.to_dict())
    with db_session:
        query = avg(x.salary for x in Worker if x.age > 30)
        print(query)
        max(x.salary for x in Worker if x.age > 30)
        min(x.salary for x in Worker if x.age > 30)
count(x for x in Worker if x.age > 30)

或者

select(x.salary for x in Worker if x.age > 30).count()
select(x for x in Worker).order_by(lambda x: x.age)
select(x for x in Worker).order_by(lambda x: desc(x.age))

或者

select(x for x in Worker).order_by(Worker.age)
select(x for x in Worker).order_by(desc(Worker.age))

或者

select(x for x in Worker).order_by("x.age")
select(x for x in Worker).order_by(desc("x.age"))
select(x for x in Worker).order_by(desc(Worker.age)).order_by(desc(Worker.salary))
select(x for x in Worker).page(pagenum=1, pagesize=2)
select(x for x in Worker).order_by(desc(Worker.age)).limit(3)

或者

select(x for x in Worker).order_by(desc(Worker.age))[:3]

不要以为第二种方法是先查询出来全部数据再对结果进行切片的,pony明白操作者的意图,会对sql语句进行优化。实际上两种方式生成的sql语句是一模一样的😜
SELECT x.id, x.name, x.sex, x.age, x.salary
FROM worker x
ORDER BY x.age DESC LIMIT 3

sum(x.salary for x in Worker)
avg(x.salary for x in Worker)

或者

select(x.salary for x in Worker).avg()
select(x for x in Worker).where(lambda x: x.name.startswith("T"))
select(x for x in Worker).where(lambda x: x.name.endswith("a"))
select(x for x in Worker).where(lambda x: "m" in x.name)
select(x for x in Worker).where(lambda x: raw_sql("x.name = 'Tom'"))
  1. Python的魔法ORM --《PonyORM教程》 1.连接,声明和查询
  2. Python的魔法ORM --《PonyORM教程》 2 实体关系
  3. Python的魔法ORM --《PonyORM教程》 3 实体继承
  4. Python的魔法ORM --《PonyORM教程》 4 高级定义和连接查询
上一篇 下一篇

猜你喜欢

热点阅读