1

ORM(Object Relational Mapping)

orm使用步骤

  1. 在项目使用的数据库管理系统中建立数据库。
DATABASES = {
  'default': {'ENGINE': 'django.db.backends.mysql', 
              # 数据库引擎,指明数据库类型
              'HOST': '127.0.0.1',
              # 数据库安装在本机
              'PORT': '3306',
              # 端口号
              'NAME': 'test_orm',
              # 数据库名称
              'USER': 'root',
              # 数据库用户名
              'PASSWORD': 'root',
              # 数据库密码
             }
}
  1. 在项目的配置文件settings.py中设置数据库的连接字符。
INSTALLED_APPS = [
  '应用名称'
]
  1. 在应用程序的models.py文件中编写继承于models.Model的数据模型。
  2. 运行python manage.py makemigrationspython manage.py migrate两个命令生成数据库表。
  3. 使用Django ORM操作数据库表。
# 在project文件夹下的_init_中需要使用pymysql替换mysql
import pymysql

pymysql.install_as_MySQLdb()

常用orm字段类型

  • Char Field:字符类型,必须提供max_length参数,max_length表示字符长度。
verbose_name在Django Admin管理后台是字段的显示名称,可理解为字段别名,verbose_name在SQL层面没有具体的体现,也就是说加不加verbose_name对数据库中的字段没影响。
name=models.CharField(max_length=32,verbose_name='姓名')
  • Email Field:邮箱类型,实际上是字符类型,只是提供了邮箱格式检验。
email=models.EmailField(verbose_name='邮箱')
  • Text Field:文本类型,存储大段文本字符串。字符串如果超过 254 个字符建议使用Text Field。
descript=models.TextField(verbose_name="简介")
  • Integer Field:整数类型。
int= models.IntegerField()
  • Date Field:日期字段。
date=models.DateField(auto_now=True, auto_now_add=False)
auto_now参数自动保存当前时间,一般用来表示最后修改时间。在第一次创建记录的时候, Django将auto_now_add字段值自动设置为当前时间,用来表示记录对象的创建时间
  • Time Field:时间字段。
time= models.TimeField(auto_now=False, auto_now_add=False)
  • Date Time Field:日期时间字段,合并了日期字段与时间字段。
datetime=models.DateTimeField(auto_now=False, auto_now_add=False)
  • File Field:实际上是字符串类型,用来把上传的文件的路径保存在数据库中。文件上传到指定目录,主要参数upload_to指明上传文件的保存路径,这个路径与Django配置文件的MEDIA_ROOT变量值有关。
filetest =models.FielField (upload_to = 'test/')
  • Image Field:实际上是字符串类型,用来把上传的图片的路径保存在数据库中。图片文件上传到指定目录,主要参数upload_to指明上传图片文件的保存路径,与File Field中upload_to相同。
picture = models.Image Field(upload_to = 'pic/')

常用字段属性

  1. db_index:db_index=True表示设置此字段为数据库表的索引。
title = models.CharField(max_length=32, db_index=True)
  1. unique:unique=True表示该字段在数据库表中不能有重复值。
  2. default:设置字段默认值,如default='good'。
  3. auto_now_add:Datetime Field、Date Field、Time Field 这 3 种字段的独用属性, auto_now_add=True表示把新建该记录的时间保存为该字段的值。
  4. auto_now:Datetime Field、Date Field、Time Field这3种字段的独用属性,auto_now= True表示每次修改记录时,把当前时间存储到该字段。

基本操作CRUD

方式一
new_emp=models.employee.objects.create(name="tom",email="tom@163.com",dep_id=66)
方式二:使用save方法
new_emp= models.employee (name="tom",email="tom@163.com",dep_id=66)
new_emp.save()
删除记录,用filter()过滤出符合条件的记录后调用delete()删除。
models.employee.objects.filter(name= "张三").delete()
# 将指定条件的记录更新,并更新指定字段的值
models.employee.objects.filter(name='tom').update(email="tom2@163.com")
# 修改单条数据
# 取出单条信息
obj = models.employee.objects.get(id=66)
# 修改
obj.email = "tom2@sina.com"
# 保存修改
obj.save()
# 获取全部
Emp_list= models.employee.objects.all()
# 获取单条数据,数据不存在则报错
Emp=models.employee.objects.get(id=123)
# 获取指定条件的记录集
Emp_group=models. employee.objects.filter(name= "张三")

Django ORM数据操作常用函数

Django的Query Set对象集本质上是对应于数据库表的记录集合,QuerySet有一个特性就是“惰性”,即返回值为Query Set的函数不会立即去数据库操作数据。当我们用到Query Set的值时,它才会去数据库中获取数据,如遍历QuerySet、打印Query Set、判断Query Set是否有值时,它才会到数据库表中获取数据。
  • all()函数,返回符合条件的全部记录。
objects = models.employee.objects.all()
  • filter()函数,返回指定条件的记录。filter后面的括号内为过滤条件,类似于SQL中语句where后面的条件语句。
objects = models.employee.objects.filter(name='tom')
filter后面的括号内存放的是过滤条件,针对数据表的字段过滤一般用“字段名+双下划线+条件名词”,括号内的过滤条件可以有多个,这些条件之间是“与”关系也就是and关系,条件名词在Django ORM中主要包括contains、icontains、in、gt、lt、range、startswith、endswith、istartswith、iendswith等,部分用法如下。
# 获取name字段包含“Tom”的记录
models.employee.objects.filter(name__contains="Tom")
# 获取name字段包含“tom”的记录,icontains忽略大小写
models.employee.objects.filter(name__icontains="tom")
# 获取employee数据表中id等于10、20、66的数据
models.employee.objects.filter(id__in=[10, 20, 66])
# 获取employee数据表中id不等于10、20、66的记录,因为前面用的是exclude
models.employee.objects.exclude(id__in=[10, 20, 66])。
# 获取employee数据表中id大于1 且 小于10的记录,两个过滤条件的关系等价于SQL的and
models.employee.objects.filter(id__gt=1, id__lt=10)
# 获取employee数据表中id在范围1~66内的记录,等价于SQL的id bettwen 1and 66
models.employee.objects.filter(id__range=[1, 66])
# 获取employee数据表中birthday字段中月份为9月的记录,birthday为日期格式
models.employee.objects.filter(birthday__month=9)
  • exclude()函数,返回不符合括号内条件的记录,与filter()函数具有相反的意义。
objects = models.employee.objects.exclude(name='tom')
  • order_by()函数,按照order_by后面括号中的字段排序。
objects = models.employee.objects.exclude(name='tom').order_by('name','id')
字段名中加“-”,表示按该字段倒序排列。如下代码表示,按name字段倒序排列列表。
objects = models.employee.objects.order_by('-name')
  • distinct()函数,去掉记录集合中完全一样的记录(重复记录),然后返回这个记录集。
objects = models.employee.objects.filter (name='tom').distinct()
返回QuerySet
以下3个函数返回其他数据类型,可以理解为特殊的Query Set类型。
  • values()函数,返回一个字典类型序列。
objects = models.employee.objects.values('id','name','email')
print( objects)
输出如下
<Query Set[{'id': 1, 'name': '刘大华', 'email': 'ldh@163.com'}, {'id': 2, 'name': '古连田', 'email': 'glt@123.com'}, {'id': 4, 'name': '张三', 'email': 'zs@sina.com'}]>
  • values_list()函数,返回一个元组类型序列。
objects = models.employee.objects.values_list('id','name','email')
print( objects)
输出如下
<Query Set[(1, '刘大华', 'ldh@163.com'), (2, '古连田', 'glt@123.com'), (4, '张三','zs@sina.com')]>
  • get()、first()、last()返回单个对象,可以理解为返回数据表中的一条记录。
# 返回id为1的记录,括号内是过滤条件
object1 = models.employee.objects.get(id=1)
# 返回数据集的第一条记录
object2 = models.employee.objects.first()
# 返回数据集的最后一条记录
object3 = models.employee.objects.last()
# 返回数据集的个数
object4= models.employee.objects.count()

跨表操作

Django中的数据模型关联关系主要是由外键、多对多键、一对一键形成的关联关系,Django数据模型的实例对象中保存着关联关系相关的信息,这样在跨表操作过程中,我们可以根据一个表的记录的信息查询另一个表中相关联的记录,充分利用关系型数据库的特点。
Foreign Key字段
在数据模型中一般把Foreign Key字段设置在“一对多”中“多”的一方,Foreign Key可以和其他表做关联关系,也可以和自身做关联关系。
  • Foreign Key字段一般在models.py文件的数据模型类中定义,其形式如下。
# 员工的部门,外键,形成一对多的关系
dep=models.ForeignKey(to="department",to_field="id",related_name="dep_related",on_delete=models.CASCADE)
  • Foreign Key字段主要有4个属性,如下。

    • to用来设置要关联的表,形如to="tname",其中tname就是要关联的数据模型。
    • to_field用来设置要关联的字段,形如to_field="id",Django默认使用被关联对象的主键,一般不用设置。
    • related_name是在反向操作时使用的名字,用于代替原反向查询时的“表名_set”,形如related_name="dep_related",如果这样定义,dep_obj.employee_set.all()就要被dep_obj.dep_related.all()代替。

      • 如果使用了related_name,在反向操作中就不能用“表名_set”。
    • 属性on_delete=models.CASCADE用来删除关联数据,与之关联的数据也要删除。这是该属性的常规设置,另外还可将其设置成models.DO_NOTHING、models.PROTECT、models.SET_NULL、models.SET_DEFAULT,这些设置不常用
外键跨表关联操作

正向:单 -> 多

反向:多 -> 单

首先介绍一下数据操作的常规说法,正向操作是指由存在外键的表通过外键查找关联的数据库表,反向操作指的是由关联表查找存在外键的数据库表。

以前面定义的employee数据表与department数据表为例,正向操作是通过employee的一条记录的外键查找与之关联的department的记录,代码如下。

emp=employee.objects.get(id=2)
dep=emp.dep.dep_name

用emp.dep.dep_name取得员工所在部门的名称,其中emp是保存employee的一条记录对象的变量,dep为外键名字。

反向操作是通过department的一条记录查找employee中关联的记录,用“表名_set”,其中表名用的是含有外键字段的表的名称,代码如下。

dep_obj=department.objects.get(id=8)
emp_list=dep_obj.employee_set.all()
通过dep_obj.employee_set.all()取得一个部门的所有员工名,dep_obj是存储department的一条记录对象的变量,“employee_set”就是“表名_set”的形式。
外键跨表操作的样例
  • 创建url与视图关联
path('test_foreign/',test_foreign)
  • 在/test_orm/employee/views.py中编写test_foreign代码。
def test_foreign(request):
  # 取出employee的一条记录
  emp=employee.objects.get(id=16)
  # 正向操作,通过外键值dep关联到department数据表的一条记录,然后取得该记录的dep_name字段
  dep_name=emp.dep.dep_name
  dep_obj=department.objects.get(id=6)
  # 反向操作,通过employee_set关联到employee数据表,然后用all()函数取得其全部记录
  emp_list=dep_obj.employee_set.all()
  for item in emp_list:
    print(item)
外键跨表查询字段
查询字段的值也分正向操作与反向操作两种形式。
  • 正向操作查询字段值,取得字段值的形式为“外键+双下划线+关联表的字段名”,如下所示。
emp=models.employee.objects.values_list('name',"dep__dep_name","dep__dep_script")
print(emp)
emp2=models.employee.objects.values('name',"dep__dep_name","dep__dep_script")
print(emp2)
values_list()和values()函数传入的参数:name取的是employee数据表中的字段;dep__dep_name是“外键+双下划线+关联表的字段名”的形式,它通过employee外键dep关联到department数据表,然后获取dep_name的值;dep__dep_script也通过外键取得关联表的dep_script字段的值。
  • 反向操作查询字段值,取得字段值的形式为“表名+双下划线+字段名”,表名是有外键字段的表的名称,如下所示。
dep_emp=models.department.objects.values_list("employee__name")
print(dep_emp)
如果在外键字段定义了related_name属性,就必须用related_name指定的名字取字段,形式如“related_name值+双下划线+字段名”

举例:

employee/module.py

dep=models.Foreign Key(to="department",to_field="id",related_name='dep_related',on_delete=models.CASCADE)

depmartment/views

dep_emp=models.department.objects.values_list("dep_related__name","dep_related__email")
与多对多键有关的跨表操作
  • Many To Many Field字段
Many To Many Field字段一般在models.py文件的数据模型类中定义,其形式如下。
# 员工加入的团体,多对多关系,即一个员工可以加入多个团体,一个团体可以有多个员工
group=models.ManyToManyField(to="group",related_name="group_related")
Many To Many Field字段主要有to和related_name两个属性,这两个属性与Foreign Key字段的同名属性意义相同
  • 多对多键跨表关联操作

    这里也涉及正向操作与反向操作,正向操作指的是从有多对多键的表(employee)查找关联表(group),反向操作指的是从关联表查找有多对多键的表。跨表操作主要用函数进行。
    • create()函数,创建一个新的记录并保存在数据库表中,最后将它添加到关联对象集(记录集)之中
    # 正向操作
    models.employee.objects.first().group.create(group_name='搏击',group_script='搏击也是健身项目')
    # 反向操作
    models.group.objects.first().employee_set.create(name='tom',email='wy@163.com',dep_id='11')
    # 反向操作
    models.group.objects.get(id=4).employee_set.create(name='john',email='lm2@163.com',dep_id='11')
  • add()函数取出数据库表中的记录,然后将其添加到关联数据表的记录集。

    第一行代码先把group的记录取出来放到变量group_list中,第二行代码把取出的记录通过group.add(group_list)关联到models.employee.objects.first()取出的记录上,注意变量前要加“”号。同理可以通过id值进行关联,以下代码是把group中id值为1、2、6的记录关联到employee表的第一条记录上,注意列表变量前要加“*”号。
# 取出一条关联表的信息
group_list=models.group.objects.filter(id=6)
# 塞进多对多表
models.employee.objects.first().group.add(*group_list)
  • set()函数,更改数据库表中记录的关联记录,不管该记录以前关联任何记录,用新的关联替换

    下面代码用group数据表中id值为4、5、6的记录关联employee数据表中id值为11的记录,注意列表变量前不加“*”号。
models.employee.objects.get(id=11).group.set([4,5,6])
  • remove()函数,从记录对象中删除一条关联记录,参数为关联数据库表的id。

    下面代码是从employee数据表中取出第一条记录,然后删除这条记录关联的group数据表中id值为4的记录。
obj_list = models.employee.objects.all().first()
obj_list.group.remove(4)
  • clear()函数,从记录对象中删去一切关联记录。
models.employee.objects.last().group.clear()
多对多关联跨表查询字段值
多对多关联跨表查询字段值也分正向操作与反向操作两种形式
  1. 正向操作查询字段值,取得字段值的形式为“多对多键+双下划线+关联表的字段名”
id和name为employee数据表中字段,group__group_name可以取group数据表中的group_name字段的值,返回值emp_m2m是元组格式
emp_m2m=models.employee.objects.values_list("id","name","group__group_name")
print(emp_m2m)

# 返回
<Query Set[(1, '李立', '登山团队'), (1, '李立', '游泳队'), (1, '李立', '自行车队'), (2, 'sales', '登山团队'), (2, 'sales', '游泳队'), (2, 'sales', '自行车队'), (3, '张好人', '游泳队'), (3, '张好人', '自行车队'), (10, '刘七云', '登山团队'), (10, '刘七云', '游泳队'), (10, '刘七云','自行车队'), (13, '张三', '登山团队'), (13, '张三', '游泳队'), (13, '张三', '自行车队'),(13, '张三', '跑酷'), (14, 'tom', '登山团队'), (14, 'tom', '游泳队')]>
  1. 反向操作查询字段值,取得字段值的形式为“表名+双下划线+字段名”,表名用的是存在多对多键字段的表的名称
emp_m2m=models.group.objects.values("group_name","employee__name","employee__email")
# 返回的一个字典列表
print(emp_m2m)

# 返回
<Query Set[{'group_name': '游泳队', 'employee__name': '李立', 'employee__email':'ll@163.com'}, {'group_name': '游泳队', 'employee__name': 'sales','employee__email':'sales@163.com'},…

提示:如果在多对多键字段中定义了related_name属性,就必须用related_name指定的值取字段,形式如“related_name值+双下划线+字段名”。

与一对一键有关的跨表操作

  1. One To One Field字段
一对一的关联关系把本来可以存储在一个表的字段拆开分别放置在两个表中,将查询次数多的字段放在一个表中,将查询次数较少的字段放在另一个表中,然后为两个表建立一对一的关联关系。

module.py

class employeeinfo(models.Model):
  phone = models.CharField(max_length=11)
  address = models.Char Field(max_length=50)
  
class employee(models.Model):
  # 一对一字段
 info=models.OneToOneField(to='employeeinfo',related_name="info_related",on_delete=models.CASCADE)
  1. 一对一键跨表关联操作

一对一键跨表关联操作也涉及正向操作与反向操作,正向操作从有一对一键的表查找关联表,反向操作从关联表查找有一对一键的表。

正向操作和反向操作代码如下,其形式与外键基本一样,只是反向操作不用“表名_set”而用直接关联表名,形如“表名”。

提示:如果使用了related_name,在反向操作中就不能用“表名”

# 正向操作
emp=models.employee.objects.get(id=1)
dep=emp.info.phone
emp_info = models.employeeinfo.objects.get(id=2)
# 反向操作,因为定义了related_name="info_related",所以用info_related
emp_name = emp_info.info_related.name
# 反向操作第二种方法
# 如果在models.py的employee类中的info字段未定义related_name="info_related",可以用以下方式
# 一对一反向操作不用employee_set,直接用emp
emp_info = models.employeeinfo.objects.get(id=2)
emp_name = emp_info.employee.name
  1. 一对一关联跨表查询字段值
一对一关联跨表查询字段值有正向操作与反向操作两种形式,列举代码如下,与外键关联形式相同。
# 正向操作查询字段值
emp_one=models.employee.objects.values("id","name","info__phone","info__address")
# 反向操作查询字段值。
emp_one2=models.employeeinfo.objects.values("phone","address","employee__name","employee__email")

Django 聚合与分组查询

在Django ORM中,凡是能够查询数据库表记录的语句都可以称为查询语句,如models.employeeinfo. objects.get(id=2)、models.employee.objects.filter(name='tom')等能够返回数据库表记录集的语句都是Django ORM查询语句。

Django ORM查询语句支持链式操作,在查询语句的后面加上“.aggregate()”就是应用聚合查询,在查询语句的后面加上“.annotate()”就是应用分组查询

聚合查询

聚合查询主要对“.aggregate()”前面的查询语句取得的数据库表记录进行聚合计算。聚合计算主要有求合计值、求平均值、求最大值、求记录数等,因此aggregate()的参数主要是聚合函数Avg()、Sum()、Max()、Min()、Count()等
  • Sum
id__lt将筛选出id小于5的数据,Sum将他们加起来
# 假设在employee数据库表中有一个salary字段

# module.py
class employee(models.Model):
  …
  salary=models.DecimalField(max_digits=8,decimal_places=2)


# view.py
def aggreagate(request):
  salarySum = employee.object.filter(id__lt=5).aggregate(Sum('salary'))

聚合查询返回一个包含一些键值对的字典,返回值形式如下,这里可以看到返回值键名为“字段名+双下划线+聚合函数”

{'salary__sum': Decimal('89787.76')}

指定返回名称

salarySum = employee.object.filter(id__lt=5).aggregate(salary__sum = Sum('salary'))

  • 添加多个聚合查询
from django.db.models import Sum,Avg,Max,Min,Count

salary_data =     models.employee.objects.filter(id__lt=18).aggregate(count=Count("id"),salary_hj=Sum("salary"),salary_pj=Avg("salary"),salary_zd=Max("salary"),alary_zx=Min("salary"))

print(salary_data)
# 返回
{'count': 6, 'salary_hj': Decimal('89787.76'), 'salary_pj': 14964.626667,'salary_zd': Decimal('56666.88'), 'alary_zx': Decimal('888.00')}

分组查询

分组查询对“.annotate()”前面的查询语句返回的数据库表记录进行分组聚合计算,根据前面的查询语句是否含有values()函数进行分组聚合计算
  1. 查询语句不含values()函数
# views.py
def get_salary_count(request):
  # models.employee.objects得到employee中所有的记录(员工记录),有n个员工,就分n个组,每一组再由annotate()中的聚合函数进行分组统计
  # 返回employee数据表中全部记录,并且为每一条记录加一个新字段groupnum
    empList = employee.objects.annotate(groupnum=Count('group'))
    
    for item in empList:
        print(item.name, ': 参加', item.groupnum, '个团体')
    return HttpResponse('success')
  • 统计每一个部门薪水最高值
# views.py
def dep_list_max:

# Max("employee__salary")中的employee__salary通过双下划线取得关联表的字段值。
dep_list=models.department.objects.annotate(maxsalary=Max("employee__salary")    
for dep in dep_list:                                            print(dep.dep_name,dep.maxsalary)
  1. 查询语句包含values()函数
# 下面代码中“values('dep')”起的作用就是以dep值分组字段,相当于SQL语句中的group by dep。代码实现的功能就是计算每个部门员工的平均工资
dep_salary=models.employee.objects.values('dep').annotate(avg=Avg("salary")).values('dep__dep_name',"avg")

# 返回
<Query Set[{'dep__dep_name': '审计部', 'avg': 56666.88}, {'dep__dep_name': '经营部','avg': 12833.0}, {'dep__dep_name': '财务部', 'avg': 1000.0}, {'dep__dep_name':'资产管理中心', 'avg': 3227.44}]>

Djangoz中的F函数和Q函数

F函数 => Find

要实现字段值与字段值的比较或运算操作等就要用到F函数,在F函数中传入字段名就能取得字段的值。这个函数较易理解,这里只简单介绍。

以下代码实现id值小于30的员工的薪水增加600的功能。

from django.db.models import 

Fmodels.employee.objects.filter(id__lt=30).update(salary=F("salary")+600)

Q函数 => Query

在Django ORM查询语句中,filter()等函数中传入的条件参数是“与”关系,它相当于SQL语句的“AND”。通过把条件参数传入Q函数,再把各个Q函数与“&”“|”“~”操作符进行组合生成复杂的查询条件。其中 ,“&”表示与(AND)关系,“|”表示或(OR)关系,“~”表示反(NOT)关系
  • 在employee数据表中查询id值小于30或者salary值小于1000的记录。
from django.db.models import 

Qobj=models.employee.objects.filter(Q(id__lt=30)|Q(salary__lt=1000))
  • 查询employee数据表中salary值大于1000并且name字段值开头不是“李”的记录。
from django.db.models import 
Qobj=models.employee.objects.filter(Q(salary__gt=1000)&~Q(name__startswith='李'))饭店啦

Bill
163 声望11 粉丝

职业:网管