概述 对象关系映射(Object Relational Mapping,简称 ORM )用于实现面向对象编程语言里不同类型系统的数据之间的转换。
ORM 在业务逻辑层和数据库层之间充当了桥梁的作用。
ORM 是通过使用描述对象和数据库之间的映射的元数据,将程序中的对象自动持久化到数据库中。
使用ORM的优点
使用ORM的缺点
ORM 代码转换为 SQL 语句时,需要花费一定的时间,执行效率会有所降低。
长期写 ORM 代码,会降低编写 SQL 语句的能力。
ORM解析过程
ORM 会将 Python 代码转成为 SQL 语句。
SQL 语句通过 pymysql 传送到数据库服务端。
在数据库中执行 SQL 语句并将结果返回。
ORM开发步骤
在models.py定义模型类
生成迁移文件: python manage.py makemigrations
–> 在migrations文件夹生成0001_intial.py
执行迁移生成数据库(默认会用sqite3数据库,生成数据库名为:db.sqlite3)
python mange.py migrate
-> 在数据库中生成对应的数据 test01_department 等
通过模型类和对象,对数据进行增删改查
ORM对应关系表
Django配置 连接MySQL 1.创建数据库 1 create database db1 default character set utf8 collate utf8_general_ci;
2.settings.py 数据库配置:
1 2 3 4 5 6 7 8 9 10 11 DATABASES = { 'default' : { 'ENGINE' : 'django.db.backends.mysql' , 'HOST' : 'rm-2zebj2u1pd5c2y29jto.mysql.rds.aliyuncs.com' , 'NAME' : 'polaris-dev' , 'PORT' : 3306 , 'USER' : 'vcgapp' , 'PASSWORD' : '******' , "OPTIONS" : {"init_command" : "SET default_storage_engine=INNODB;" } } }
DEBUG日志:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 LOGGING = { 'version' : 1 , 'disable_existing_loggers' : False , 'handlers' : { 'console' :{ 'level' :'DEBUG' , 'class' :'logging.StreamHandler' , }, }, 'loggers' : { 'django.db.backends' : { 'handlers' : ['console' ], 'propagate' : True , 'level' :'DEBUG' , }, } }
3.安装pymysql
1 2 3 4 vim __init__.py #项目根目录,和settings.py同级目录 import pymysql pymysql.install_as_MySQLdb()
4.使用model 若模型位于项目中的 myapp.models
模块( 此包结构由 manage.py startapp
命令创建), INSTALLED_APPS
应设置如下:
1 2 3 4 5 INSTALLED_APPS = [ 'myapp' , ]
模型Model 参考官方文档:https://docs.djangoproject.com/zh-hans/3.1/topics/db/models/
样例 1 2 3 4 5 from django.db import modelsclass Person (models.Model): first_name = models.CharField(max_length=30 ) last_name = models.CharField(max_length=30 )
first_name
和 last_name
是模型的 字段 。每个字段都被指定为一个类属性,并且每个属性映射为一个数据库列。
上面的 Person
模型会创建一个如下的数据库表:
1 2 3 4 5 CREATE TABLE myapp_person ( "id" serial NOT NULL PRIMARY KEY, "first_name" varchar(30) NOT NULL, "last_name" varchar(30) NOT NULL );
一些技术上的说明:
该表的名称 myapp_person
是自动从某些模型元数据中派生出来,但可以被改写。参阅 表名称 获取更多信息。
一个 id
字段会被自动添加,但是这种行为可以被改写。请参阅 自动设置主键 。
本例子中 创建数据表
的语法是 PostgreSQL 格式的。值得注意的是,Django 依据你在 配置文件 中指定的数据库后端生成对应的 SQL 语句。
字段 模型中最重要且唯一必要的是数据库的字段定义。字段在类属性中定义。定义字段名时应小心避免使用与 模型 API 冲突的名称, 如 clean
, save
, or delete
等.
举例:
1 2 3 4 5 6 7 8 9 10 11 12 from django.db import models class Musician(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) instrument = models.CharField(max_length=100) class Album(models.Model): artist = models.ForeignKey(Musician, on_delete=models.CASCADE) name = models.CharField(max_length=100) release_date = models.DateField() num_stars = models.IntegerField()
字段类型 模型中每一个字段都应该是某个 Field
类的实例, Django 利用这些字段类来实现以下功能:
字段类型用以指定数据库数据类型(如:INTEGER
, VARCHAR
, TEXT
)。
在渲染表单字段时默认使用的 HTML 视图 (如: <input type="text">
, <select>
)。
基本的有效性验证功能,用于 Django 后台和自动生成的表单。
Django 内置了数十种字段类型;你可以在 模型字段参考 中看到完整列表。如果 Django 内置类型不能满足你的需求,你可以很轻松地编写自定义的字段类型;参见 编写自定义模型字段(model fields) 。
Django模型属性和MySQL数据库数据类型对应关系
分类
模型属性类型
sql数据类型
自增
AutoField
int
布尔
BooleanField
tinyint
NullBooleanField
tinyint
字符
CharField
varchar
EmailField
varchar
TextField
longtext
数字
IntegerField
int
DecimalField
decimal
FloatField
double
日期和时间
DateField
date
TimeField
time
DateTimeField
datetime
文件
FileField
varchar
ImageField
varchar
外键
ForeignKey
alter table B add constraint A_B_Ids foreign key(Aid) references A(Ids)
ManytoMany
建中间表再关联外键
字段设计
避免允许 null 值的字段,null 值难以查询优化且占用额外的索引空间
充分考虑每张表的数据规模,选取合适主键字段类型。比如数据创建比较频繁的表,主键建议使用 BigIntegerField
使用正确的字段类型,避免TextField
代替CharField
,IntegerField
代替BooleanField
等
如果字段的取值是一个有限集合,应使用 choices
选项声明枚举值
1 2 3 4 5 6 7 8 9 10 11 class Students (models.Model): class Gender (object ): MALE = 'MALE' FEMALE = 'FEMALE' GENDER_CHOICES = ( (Gender.MALE, "男" ), (Gender.FEMALE, "女" ), ) gender = models.IntegerField("性别" , choices=GENDER_CHOICES)
如果某个字段或某组字段被频繁用于过滤或排序查询,建议建立单字段索引或联合索引
1 2 3 4 5 6 7 8 9 10 title = models.CharField(max_length=255 , db_index=True ) class Meta : index_together = ['field_name_1' , 'field_name_2' ] class Meta : unique_together = ('field_name_1' , 'field_name_2' )
数据的增删查改 查询 基本要求
了解 Django ORM 是如何缓存数据的
了解 Django ORM 何时会做查询
不要以牺牲代码可读度为代价做过度优化
实际应用
避免全表扫描。优先使用exists
, count
等方法
1 2 3 4 5 6 7 8 projects = Project.objects.filter (enable=True ) project_count = len (projects) project_count = projects.count()
避免 N + 1 查询。可使用select_related
提前将关联表进行 join,一次性获取相关数据,many-to-many 的外键则使用prefetch_related
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 students = Student.objects.all () student_in_class = {student.name: student.cls.name for student in students} students = Student.objects.select_related('cls' ).all () student_in_class = {student.name: student.cls.name for student in students} articles = Article.objects.filter (id__in=(1 ,2 )) for item in articles: item.tags.all () articles = Article.objects.prefetch_related("tags" ).filter (id__in=(1 ,2 )) for item in articles: item.tags.all ()
如果仅查询外键 ID,则无需进行连表操作。使用 外键名_id
可直接获取
1 2 3 4 5 6 7 8 student = Student.objects.first() cls_id = student.cls.id cls_id = student.cls_id
避免查询全部字段。可使用values
, values_list
, only
, defer
等方法进行过滤出需要使用的字段。
1 2 3 4 5 6 7 8 students = Student.objects.all () student_names = [student.name for student in students] students = Student.objects.all ().values_list('name' , flat=True )
避免在循环中进行数据库操作。尽量使用 ORM 提供的批量方法,防止在数据量变大的时候产生大量数据库连接导致请求变慢
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 project_names = ['ProjectA' , 'ProjectB' , 'ProjectC' ] for project_name in project_names: Project.objects.create(name=project_name) projects = [] for project_name in project_names: project = Project(name=project_name) projects.append(project) Project.objects.bulk_create(projects) project_names = ['ProjectA' , 'ProjectB' , 'ProjectC' ] projects = [] for project_name in project_names: project = Project.objects.get(name=project_name) projects.append(project) projects = Project.objects.filter (name__in=project_names) project_names = ['ProjectA' , 'ProjectB' , 'ProjectC' ] projects = Project.objects.filter (name__in=project_names) for project in projects: project.enable = True project.save() projects.update(enable=True )
1 2 3 4 5 6 7 8 9 groups = Group.objects.filter (type ="typeA" ) members = Member.objects.filter (group__in=groups) group_ids = Group.objects.filter (type ="typeA" ).values_list('id' , flat=True ) members = Member.objects.filter (group__id__in=list (group_ids))
update_or_create
与 get_or_create
不是线程安全的。因此查询条件的字段必须要有唯一性约束
1 2 3 4 5 host, is_created = Host.objects.get_or_create( ip="127.0.0.1" , bk_cloud_id="0" )
如果查询集只用于单次循环,建议使用 iterator()
保持连接查询。当查询结果有很多对象时,QuerySet 的缓存行为会导致使用大量内存。如果你需要对查询结果进行好几次循环,这种缓存是有意义的,但是对于 QuerySet 只循环一次的情况,缓存就没什么意义了。在这种情况下,iterator()
可能是更好的选择
1 2 3 4 5 6 7 for task in Task.objects.all (): for task in Task.objects.all ().iterator():