ORM
ORM
Object-Relational Mapping
对应关系
ORM DB 类 数据表 对象 数据行 属性 字段
ORM 提高开发效率,降低了执行效率
Flask - Sqlalchemy
$ python manage.py migrate
字段类型和参数
1、字段类型
# 自增长 默认int Auto = models.AutoField(primary_key=True) BigAuto = models.BigAutoField() # 二进制 Binary = models.BinaryField() # 布尔 Boolean = models.BooleanField() NullBoolean = models.NullBooleanField() # 整型 PositiveSmallInteger = models.PositiveSmallIntegerField() # 5字节 SmallInteger = models.SmallIntegerField() # 6字节 PositiveInteger = models.PositiveIntegerField() # 10字节 Integer = models.IntegerField() # 11字节 BigInteger = models.BigIntegerField() # 20字节 # 字符串 Char = models.CharField() # varchar Text = models.TextField() # longtext # 时间日期 Date = models.DateField() DateTime = models.DateTimeField() Duration = models.DurationField() # int python timedelta实现 # 浮点型 Float = models.FloatField() Decimal = models.DecimalField() # 其他 Email = models.EmailField() # 邮箱 Image = models.ImageField() File = models.FileField() FilePath = models.FilePathField() URL = models.URLField() UUID = models.UUIDField() GenericIPAddress = models.GenericIPAddressField()
2、关系型字段
一对一 OneToOneField
多对一 ForeignKey
多对对 ManyToManyField 默认或自定义中间表
3、字段类型参数
默认表名
应用_模型类名小写
eg: app_student
(1)所有字段都有的参数
db_column="name" 指定数据库字段名
verbose_name="别名"
help_text="表单说明"
primary_key=True 设置主键
unique=True 唯一键
null=True 数据库允许为空,默认都是不能为 null 的
blank=True 前端提交表单允许为空
db_index=True 建立索引
editable=False 不允许编辑字段
(2)个别字段的参数
CharField
max_length=100 最大长度
DateField、DateTime
unique_for_date=True 日期必须唯一
unique_for_month=True 月份唯一
auto_now=True 自动更新记录时间
auto_now_add=True 自动增加记录时间
DecimalField
max_digits=4 总位数
decimal_places=2 小数点数
eg: 11.22 16.34
(3)关系型字段参数
OneToOneField
related_name="one" 父表查字表数据
ForeignKey
on_delete="" 外键所关联的对象被删除时进行的操作
CASCADE: 级联删除。将定义有外键的模型对象同时删除, 默认操作
PROTECT: 阻止删除。会报完整性错误。
DO_NOTHING:什么也不做
SET_NULL:外键设置为 null,前提是 null=True, blank=True。
SET_DEFAULT:设置为外键的默认值,前提是设置了 default 参数。
SET():会调用外面的值,可以是一个函数。一般情况下使用 CASCADE 就可以了。
eg:
class B(models.Model): foreign = models.ForeignKey(A, on_delete=models.CASCADE) foreign = models.ForeignKey(A, on_delete=models.PROTECT) foreign = models.ForeignKey(A, on_delete=models.DO_NOTHING) foreign = models.ForeignKey(A, on_delete=models.SET_NULL, null=True, blank=True) foreign = models.ForeignKey(A, on_delete=models.SET_DEFAULT, default=0) foreign = models.ForeignKey(A, on_delete=models.SET)
4、自关联
示例:地址 省-市-县 级联
class AddressInfo(models.Model): address = models.CharField(max_length=200, null=True, blank=True, verbose_name="地址") pid = models.ForeignKey("self", null=True, blank=True, verbose_name="自关联", on_delete=models.SET_NULL) def __str__(self): return self.address
元数据 Meta
修改应用名称
apps.py
class OrmdemoConfig(AppConfig): name = 'ormdemo' verbose_name = "应用名称"
修改 model 元数据
class AddressInfo(models.Model): address = models.CharField(max_length=200, null=True, blank=True, verbose_name="地址") note = models.CharField(max_length=200, null=True, blank=True, verbose_name="说明") pid = models.ForeignKey("self", null=True, blank=True, verbose_name="自关联", on_delete=models.SET_NULL) def __str__(self): return self.address class Meta: db_table = "address" # 自定义表名 ordering = ["pid_id"] # 指定排序字段 verbose_name = "地址" # 单数 verbose_name_plural = verbose_name # 复数 abstract = True # 设置为基类 permissions = (("定义好的权限", "权限说明"),) managed = False # 按照Django默认方式管理数据表 unique_together = ("address", "note") # 联合唯一键,单元组或多元组 app_label = "ormdemo" # 定义模型类属于哪个应用 db_tablespace = "" # 定义数据库表空间
模型类开发实例
""" 讲师 - 助教 一对一 讲师 - 课程表 一对多 课程表 - 学生 多对多 """ class Teacher(models.Model): """讲师信息表""" nickname = models.CharField(max_length=30, primary_key=True, db_index=True, verbose_name="昵称") introduction = models.TextField(default="这位同学很懒,木有签名的说~", verbose_name="简介") fans = models.PositiveIntegerField(default=0, verbose_name="粉丝数") created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") updated_at = models.DateTimeField(auto_now=True, verbose_name="创建时间") class Meta: verbose_name = "讲师信息表" verbose_name_plural = verbose_name def __str__(self): return self.nickname class Course(models.Model): """课程信息表""" title = models.CharField(max_length=100, primary_key=True, db_index=True, verbose_name="课程名") type = models.CharField(choices=((0, "其他"), (1, "实战课"), (2, "免费课")), max_length=1, default=0, verbose_name="课程类型") price = models.PositiveSmallIntegerField(verbose_name="价格") volume = models.BigIntegerField(verbose_name="销量") online = models.DateField(verbose_name="上线时间") # 删除级联 teacher = models.ForeignKey(Teacher, null=True, blank=True, on_delete=models.CASCADE, verbose_name="讲师") created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") updated_at = models.DateTimeField(auto_now=True, verbose_name="创建时间") class Meta: verbose_name = "课程信息表" verbose_name_plural = verbose_name def __str__(self): return f"{self.get_type_display()}-{self.title}" class Student(models.Model): """学生信息表""" nickname = models.CharField(max_length=30, primary_key=True, db_index=True, verbose_name="学生姓名") age = models.PositiveSmallIntegerField(verbose_name="年龄") gender = models.CharField(choices=((0, "保密"), (1, "男"), (2, "女")), max_length=1, default=0, verbose_name="性别") study_time = models.PositiveIntegerField(default=0, verbose_name="学习时长(h)") course = models.ManyToManyField(Course, verbose_name="课程") created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") updated_at = models.DateTimeField(auto_now=True, verbose_name="创建时间") class Meta: verbose_name = "学生信息表" verbose_name_plural = verbose_name def __str__(self): return self.nickname class TeacherAssistant(models.Model): """助教信息表""" nickname = models.CharField(max_length=30, primary_key=True, db_index=True, verbose_name="昵称") hobby = models.CharField(max_length=10, null=True, blank=True, verbose_name="爱好") # 删除置空 teacher = models.OneToOneField(Teacher, null=True, blank=True, on_delete=models.SET_NULL, verbose_name="讲师") created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") updated_at = models.DateTimeField(auto_now=True, verbose_name="创建时间") class Meta: verbose_name = "助教信息表" verbose_name_plural = verbose_name def __str__(self): return self.nickname
生成数据表
python manage.py makemigrations # 生成迁移文件 python manage.py migrate # 执行迁移文件
删除某个模型类的完整操作:
(1)在已创建的 app 下, 首先删除 models.py 中需要删除的模型类
(2)删除该模型类在迁移脚本 migrations 中的对应文件
(3)删除该项目在 django_migrations 中的对应记录
(4)删除数据库中对应的数据表
导入数据
(1)django shell
$ python manage.py shell
from app.models import Mode model = Model() model.name = "name" model.save()
(2)脚本导入
# -*- coding: utf-8 -*- import os import random from datetime import date import django import sys # 导入项目配置 project_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(project_path) os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings' django.setup() from ormdemo.models import Teacher, Course, Student, TeacherAssistant def import_data(): # 讲师数据 create Teacher.objects.create(nickname="Jack", introduction="Python工程师", fans=666) # 课程数据 bulk_create data = [ Course(title=f"Python系列教程{i}", teacher=Teacher.objects.get(nickname="Jack"), type=random.choice([0, 1, 2]), price=random.randint(200, 300), volume=random.randint(100, 10000), online=date(2018, 10, 1) ) for i in range(1, 5) ] Course.objects.bulk_create(data) # 学生数据 update_or_create Student.objects.update_or_create( nickname="A同学", defaults={ "age": random.randint(18, 58), "gender": random.choice([0, 1, 2]), "study_time": random.randint(9, 999) } ) # 正向添加 # 销量大于等于1000的课程 Student.objects.get(nickname="A同学").course.add(*Course.objects.filter(volume__gte=1000)) # 反向添加 # 学习时间大于等于500小时的同学 Course.objects.get(title="Python系列教程1").student_set.add(*Student.objects.filter( study_time__gte=500 )) # 助教数据 get_or_create() TeacherAssistant.objects.get_or_create( nickname="助教1", defaults={ "hobby": "慕课网学习", "teacher": Teacher.objects.get(nickname="Jack") } ) if __name__ == '__main__': import_data()
(3)fixtures
Django serialization -> model 保存
python manage.py dumpdata > data.json # 导出数据
python manage.py loaddata data.json # 导入数据
(4)mysqldump
MySQL 数据库导入导出数据
(5)PyCharm
PyCharm 自带的导入导出工具
ModelAPI
1、查询,检索,过滤
Teacher.objects.all() # 返回多个结果 Teacher.objects.get(id=1) 返回一条结果 Teacher.objects.filter(fans__gte=500) # 返回多个结果
2、数据匹配大小写敏感
# 双下划线开头`__` Teacher.objects.filter(fans__in=[500, 600]) # i开头表示大小写敏感 Teacher.objects.filter(nickname__icontains="A")
3、结果切片、排序、链式查询
Teacher.objects.all()[:1] Teacher.objects.all().order_by("-age") # 负号表示降序 Teacher.objects.filter(fans__gte=500).order_by("nickname") # 查询集可以继续使用排序
4、查看执行的原生 SQL
Teacher.objects.all().order_by("-nickname").query """ SELECT `ormdemo_teacher`.`nickname`, FROM `ormdemo_teacher` ORDER BY `ormdemo_teacher`.`nickname` DESC """
返回新的 QuerySet 的 API
读操作大部分都返回
1、第一类
all 全部
filter 过滤
order_by 排序
exclude 排除
reverse 逆序 需要在元数据中设置 ordering
distinct 去重
Student.objects.all().exclude(nickname="A同学") # SELECT `ormdemo_student`.`nickname` # FROM `ormdemo_student` # WHERE NOT (`ormdemo_student`.`nickname` = A同学) Student.objects.all().exclude(nickname="A同学").reverse() """ ELECT `ormdemo_student`.`nickname` FROM `ormdemo_student` WHERE NOT (`ormdemo_student`.`nickname` = A同学) """
2、第二类
extra 别名
defer 排除一些字段
only 选择一些字段
Student.objects.all().extra(select={"name": "nickname"}) """ SELECT (nickname) AS `name`, `ormdemo_student`.`nickname`, `ormdemo_student`.`age` FROM `ormdemo_student` """ Student.objects.all().only("nickname", "age") """ SELECT `ormdemo_student`.`nickname`, `ormdemo_student`.`age` FROM `ormdemo_student` """
3、第三类
values 获取字典形式
values_list 获取元组形式
Student.objects.values("nickname", "age") # <QuerySet [{'nickname': 'A同学', 'age': 54}, {'nickname': 'B同学', 'age': 36}]> Student.objects.values_list("nickname", "age") # <QuerySet [('A同学', 54), ('B同学', 36)]> Student.objects.values_list("nickname", flat=True) # 单个字段返回列表 # <QuerySet ['A同学', 'B同学']>
4、第四类
dates、datatimes 根据时间日期获取查询集
Student.objects.dates("created_at", "month") """ SELECT DISTINCT CAST(DATE_FORMAT(`ormdemo_student`.`created_at`, '%Y-%m-01') AS DATE) AS `datefield` FROM `ormdemo_student` WHERE `ormdemo_student`.`created_at` IS NOT NULL ORDER BY `datefield` ASC """
5、第五类
union 并集
intersection 交集 需要数据库支持
difference 差集 需要数据库支持
ret1 = Student.objects.filter(nickname="同学A") ret2 = Student.objects.filter(nickname="同学B") ret1.union(ret2) """ (SELECT `ormdemo_student`.`nickname FROM `ormdemo_student` WHERE `ormdemo_student`.`nickname` = '同学A') UNION (SELECT `ormdemo_student`.`nickname` FROM `ormdemo_student` WHERE `ormdemo_student`.`nickname` = '同学B') LIMIT 2
6、第六类
select_related 一对一,多对一查询优化
prefetch_related 一对多,多对多查询优化
Course.objects.all().select_related("teacher") """ SELECT `ormdemo_course`.`title`, `ormdemo_teacher`.`nickname` FROM `ormdemo_course` LEFT OUTER JOIN `ormdemo_teacher` ON (`ormdemo_course`.`teacher_id` = `ormdemo_teacher`.`nickname`) LIMIT 21; """ Student.objects.all().prefetch_related("course") """ SELECT `ormdemo_student`.`nickname` FROM `ormdemo_student` LIMIT 21; SELECT (`ormdemo_student_course`.`student_id`) AS `_prefetch_related_val_student_id`, `ormdemo_course`.`title` FROM `ormdemo_course` INNER JOIN `ormdemo_student_course` ON (`ormdemo_course`.`title` = `ormdemo_student_course`.`course_id`) WHERE `ormdemo_student_course`.`student_id` IN ('A同学', 'B同学', 'C同学', 'D同学'); """
反向查询
Teacher.objects.get(nickname="Jack").course_set.all() """ SELECT `ormdemo_teacher`.`nickname` FROM `ormdemo_teacher` WHERE `ormdemo_teacher`.`nickname` = 'Jack' SELECT `ormdemo_course`.`title` FROM `ormdemo_course` WHERE `ormdemo_course`.`teacher_id` = 'Jack' LIMIT 21; """
7、第七类
annotate 聚合计数,求和,平均数,执行原生 SQL(分组后的数据进行统计)
from django.db.models import Count, Avg, Max, Min, Sum Course.objects.values("teacher").annotate(vol=Sum("volume")) """ SELECT `ormdemo_course`.`teacher_id`, SUM(`ormdemo_course`.`volume`) AS `vol` FROM `ormdemo_course` GROUP BY `ormdemo_course`.`teacher_id` ORDER BY NULL LIMIT 21; """ Course.objects.values("teacher").annotate(pri=Avg("price")) """ SELECT `ormdemo_course`.`teacher_id`, AVG(`ormdemo_course`.`price`) AS `pri` FROM `ormdemo_course` GROUP BY `ormdemo_course`.`teacher_id` ORDER BY NULL LIMIT 21; # order by null用途是强制对查询结果禁用排序 """
不返回 QuerySet 的 API
写操作都不返回结果集
1、获取对象
get, get_or_create, first, last, in_bulk
latest 最近记录, earliest 最早记录 需要设置元数据 get_latest_by
Student.objects.get(nickname="A同学") # 返回多个会报错 """ SELECT `ormdemo_student`.`nickname` FROM `ormdemo_student` WHERE `ormdemo_student`.`nickname` = 'A同学' """ Student.objects.first() """ SELECT `ormdemo_student`.`nickname` FROM `ormdemo_student` ORDER BY `ormdemo_student`.`nickname` ASC LIMIT 1 """ Student.objects.last() """ SELECT `ormdemo_student`.`nickname` FROM `ormdemo_student` ORDER BY `ormdemo_student`.`nickname` DESC LIMIT 1; """ Student.objects.in_bulk(["A同学"]) """ SELECT `ormdemo_student`.`nickname` FROM `ormdemo_student` WHERE `ormdemo_student`.`nickname` IN ('A同学'); """ Student.objects.latest() """ SELECT `ormdemo_student`.`nickname FROM `ormdemo_student` ORDER BY `ormdemo_student`.`created_at` DESC LIMIT 1; """ Student.objects.earliest() """ SELECT `ormdemo_student`.`nickname FROM `ormdemo_student` ORDER BY `ormdemo_student`.`created_at` ASC LIMIT 1;
2、创建对象
create, bulk_create, update_or_create
3、更新对象
update, update_or_create
Student.objects.filter(nickname="A同学").update(age=13) """ UPDATE `ormdemo_student` SET `age` = 13 WHERE `ormdemo_student`.`nickname` = 'A同学'; """
4、删除对象
delete 使用 filter 过滤
Student.objects.filter(nickname="A同学").delete() """ SELECT `ormdemo_student`.`nickname FROM `ormdemo_student` WHERE `ormdemo_student`.`nickname` = 'A同学'; DELETE FROM `ormdemo_student_course` WHERE `ormdemo_student_course`.`student_id` IN ('A同学'); DELETE FROM `ormdemo_student` WHERE `ormdemo_student`.`nickname` IN ('A同学'); (13, {'ormdemo.Student_course': 12, 'ormdemo.Student': 1}) """
5、其他操作
exists, count, aggregate(整个数据表统计)
Student.objects.filter(nickname="B同学").exists() """ SELECT (1) AS `a` FROM `ormdemo_student` WHERE `ormdemo_student`.`nickname` = 'B同学' LIMIT 1 """ Student.objects.filter(nickname="B同学").count() """ SELECT COUNT(*) AS `__count` FROM `ormdemo_student` WHERE `ormdemo_student`.`nickname` = 'B同学'; """ Course.objects.aggregate(Max("price"), Min("price"), Avg("price"), Sum("volum")) """ SELECT MAX(`ormdemo_course`.`price`) AS `price__max`, MIN(`ormdemo_course`.`price`) AS `price__min`, AVG(`ormdemo_course`.`price`) AS `price__avg`, SUM(`ormdemo_course`.`volume`) AS `volume__sum` FROM `ormdemo_course`; {'price__max': 295, 'price__min': 210, 'price__avg': 250.75, 'volume__sum': 60559} """
自定义聚合查询(略)
group_contact
F 对象和 Q 对象
F 对象 操作字段数据
Q 对象 结合 AND OR NOT | ~ & 实现复杂查询
from django.db.models import F, Q Student.objects.update(age=F("age") + 3) """ UPDATE `ormdemo_student` SET `age` = (`ormdemo_student`.`age` + 3); """ Student.objects.filter(Q(age__gte=1) & Q(age__lte=10)) """ SELECT `ormdemo_student`.`nickname` FROM `ormdemo_student` WHERE (`ormdemo_student`.`age` >= 1 AND `ormdemo_student`.`age` <= 10) LIMIT 21; """