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替换mysqlimport pymysqlpymysql.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的记录,因为后面用的是excludemodels.employee.objects.exclude(id__in=[10, 20, 66])。# 获取employee数据表中id大于1 且 小于10的记录,两个过滤条件的关系等价于SQL的andmodels.employee.objects.filter(id__gt=1, id__lt=10)# 获取employee数据表中id在范畴1~66内的记录,等价于SQL的id bettwen 1and 66models.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.phoneemp_info = models.employeeinfo.objects.get(id=2)# 反向操作,因为定义了related_name="info_related",所以用info_relatedemp_name = emp_info.info_related.name# 反向操作第二种办法# 如果在models.py的employee类中的info字段未定义related_name="info_related",能够用以下形式# 一对一反向操作不必employee_set,间接用empemp_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.pyclass employee(models.Model):  …  salary=models.DecimalField(max_digits=8,decimal_places=2)# view.pydef 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,Countsalary_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.pydef 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.pydef 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='李'))饭店啦