django学习笔记(3)—— Django ORM

3. Django ORM

3.1 Django ORM概述

概念:
ORM(Object Relational Mapping)的意思是对象关系映射,Django ORM描述Django数据模型类和数据库之间的映射关系,通俗地讲就是让一个类和一个数据库表进行对应,这使ORM在数据库层和业务逻辑层之间起到了桥梁的作用。
Django通过类代码描述数据表字段、表间关系等内容,并通过相应命令把类所描述的内容持久化到数据库。

特点:

  • Django ORM通过统一格式的业务逻辑代码操作数据库,把SQL语句统一转换成较为固定的Django语法结构
  • Django ORM本质上是对SQL语句的功能进行封装,最终还是转化成SQL语句来操作数据库。
  • Django ORM与数据库映射的关系表现为Django中的一个数据模型(Model)映射一个数据库表。其基本情况是:类(django.db.models.Model)映射到数据库表,类的属性映射为数据库表字段,类的实例对象则映射为数据行。

Django ORM能实现的功能:

  • 是生成数据库表,如数据库表的创建、修改、删除
  • 是操作数据库表的数据行,如数据行的增、删、改、查。
  • Django ORM不能创建数据库,需要在数据库管理系统中手工创建。

Django ORM使用步骤主要有如下5步。

  • 在项目使用的数据库管理系统中建立数据库
  • 在项目的配置文件settings.py中设置数据库的连接字符。
  • 在应用程序的models.py文件中编写继承于models.Model的数据模型。
  • 运行python manage.pymakemigrationspython manage.py migrate两个命令生成数据库表
  • 使用Django ORM操作数据库表。

3.2 Django ORM 的用法

3.2.1 Django ORM字段

以之前我们创建的mytest数据库中的userInfo 的数据表为例:

  • Django生成数据表时,会把models.py中的类名转成小写,然后在前面加上应用程序的名称和下划线作为数据表名,如“myapp_userinfo”。
  • employee_group数据表中有id、user、email等3个字段,多出的id字段是自动添加的,它作为数据表的主键使用
    在这里插入图片描述
    Django ORM字段在models.py中创建,按照固定格式在数据模型类中建立,主要包括指定字段名的字段类型、字段属性等。
  1. 常用字段类型代码user=models.CharField(max_length=32,verbose_name='姓名')中的Char Field是字段类型,指明该字段的类型为字符型,括号内是设置字段属性的参数。下面列举一部分常用字段类型。
  • Char Field:字符类型,必须提供max_length参数,max_length表示字符长度。
from django.db import models
# 员工数据模型(员工数据表)
class employee(models.Model):
# name:员工姓名字段,字符类型,最大长度为32
name=models.CharField(max_length=32,verbose_name='姓名')
'''
verbose_name在Django Admin管理后台是字段的显示名称,可理解为字段别名,verbose_name在SQL层面没有具体的体现,也就是说加不加verbose_name对数据库中的字段没影响
'''
  • Email Field:邮箱类型,实际上是字符类型,只是提供了邮箱格式检验。
email=models.EmailField(verbose_name='邮箱')# 员工的邮箱
  • Text Field:文本类型,存储大段文本字符串。字符串如果超过 254 个字符建议使用TextField。
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.Time Field(auto_now=False,auto_now_add=False)
  • Date Time Field:日期时间字段,合并了日期字段与时间字段。
datetime=models.Date 
TimeField(auto_now=False, auto_now_add=False)
  • File Field:实际上是字符串类型,用来把上传的文件的路径保存在数据库中。文件上传到指定目录,主要参数upload_to指明上传文件的保存路径,这个路径与Django配置文件的MEDIA_ROOT变量值有关。
filetest =models.FileField (upload_to = 'test/')
'''
如果MEDIA_ROOT = os.path.join(BASE_DIR,'upload/')这句代码设定MEDIA_ROOT值为/test_orm/upload/,假设在数据表中filestest值是test.txt,那么文件路径为/test_orm/upload/test/test.txt。
'''
  • Image Field:实际上是字符串类型,用来把上传的图片的路径保存在数据库中。图片文件上传到指定目录,主要参数upload_to指明上传图片文件的保存路径,与File Field中upload_to相同。
picture = models.Image Field(upload_to ='pic/')

2.常用字段属性Django ORM字段在定义时,还需要传递参数指明字段属性,这里介绍常用的字段属性。

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

3.2.2 Django ORM 基本数据操作

  1. 增加记录
# 第一种方式
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()
  1. 删除记录
    用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数据操作常用函数下面列举的5个函数的返回值都是Query Set对象集。

Django的Query Set对象集本质上是对应于数据库表的记录集合,Query Set有一个特性就是“惰性”,即返回值为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 1 and 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后面括号中的字段排序。字段名中加“-”,表示按该字段倒序排列。
按name字段正序排列列表。
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()

以下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()

3.3 数据表操作案例

3.3.1 准备工作

  1. 先建立一个test_orm项目。输入命令django-admin startproject test_orm
  2. 生成一个名称为employee的应用程序, 在命令行终端输入命令python manage.pystartapp employee
  3. 创建一个名称为test_orm的数据库。登录MySQL数据库管理系统,输入create database test_orm;
  4. 使用 pymysql 模块来代替 DjangoMySQL客户端模块。
# /test_orm/test_orm/__init__.py
import pymysql
pymysql.install_as_MySQLdb()
  1. 在/test_orm/test_orm/settings.py中修改配置。
# 声明employee的应用程序模块
INSTALLED_APPS = ['employee',]
# 数据库配置为MySQL类型
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'Host':'127.0.0.1', #数据库安装在本机
        'PORT':'3306', # 端口号
        'NAME':'test_orm', #数据库名称
        'USER':'root', # 数据库用户名
        'PASSWORD':'数据库密码'
    }
}
  1. 创建数据模型
# /test_orm/employee/models.py
from django.db import models
# 员工数据模型(员工数据表)
class employee(models.Model):
    name=models.CharField(max_length=32,verbose_name='姓名')
    Emai=models.EmailField(verbose_name='邮箱')
    salary = models.DecimalField(max_digits=8, decimal_places=2)
    #  员工部门,Foreignkey类型,与department表中记录形成多对一的关系
    #  on_delete= models.CASCADE表示如果外键所关联的department表中的一条记录被删除,本表中与这条记录有关联的记录将全被删掉
    dep=models.ForeignKey(to="department",on_delete=models.CASCADE)
    # 员工加入的团体,多对多关系,即一个员工可以加入多个团体,一个团体有多个员工
    group=models.ManyToManyField(to="group")
    # 员工补充信息,一对一关系
    info=models.OneToOneField(to='employeeinfo',on_delete=models.CASCADE,null=True)

# 部门数据模型(部门数据表)
class department(models.Model):
    dep_name=models.CharField(max_length=32,verbose_name='部门名称')
    dep_script=models.CharField(max_length=60,verbose_name='备注')

# 团体数据模型(团体数据表)
class group(models.Model):
    group_name=models.CharField(max_length=32,verbose_name='团体名称')
    group_script=models.CharField(max_length=60,verbose_name='备注')

# 员工补充信息模型(员工补充信息数据表)
class employeeinfo(models.Model):
    phone=models.CharField(max_length=11)
    address=models.CharField(max_length=50)

以上代码生成4个数据模型:员工数据模型、部门数据模型、团体数据模型、员工补充信息模型。

一个部门可以有多名员工,因此在员工表中建立外键(Foreign Key)dep字段。这里要明确的是,
有外键的表是“多”,外键关联的表是“一”,也就是有外键的数据库表有多条记录对应外键关联的数
据库表的一条记录;数据模型中的外键在数据库表中以“外键名_id”形式命名字段,如数据模型
employee中的dep,在数据库表中字段名为dep_id

一个员工可以加入多个团体,一个团体可以有多个员工,因此在员工表建立了多对多键(Many ToMany Field)group字段。
一个员工对应一条补充信息,因此在员工表中建立一对一键(One To One Field)info字段,
info在数据库表中的字段名为info_id。

3.3.2 建立路由与视图函数的对应关系

jango是在urls.py中设立路由(URL)与视图函数的对应关系。用户在浏览器地址栏中输入网址,Django通过URL配置关系找到对应函数,这个函数接收请求,运行其中的逻辑代码,并生成响应发回浏览器,从而完成一次用户业务访问过程

为了层次清晰,我们建立两级URL配置文件。先建一级配置文件

# /test_orm/test_orm
from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    # 用include()函数把二级配置包含进来
    path('test_orm_old/',include('employee.urls')),
]

(1)include()函数中的参数是一个字符串,这个字符串指定二级URL配置文件的位置,与文件的路径有点相似,只是用“.”作为分隔符,并且最后的文件名不包含扩展名。
(2)如果URL配置文件分级,那么在匹配URL时,要把各级配置文件中URL表达式合并成一个完整的URL表达式进行匹配。

# 在/test_rom/employee文件夹下新建一个urls.py文件
from django.urls import path,include
# 导入视图函数,*代表所有
from employee.views import *
urlpatterns=[
# 操作员工数据表(employee)相关URL配置项
    path('list_employee_old/',list_employee_old),
    path('add_employee_old/',add_employee_old),
    path('edit_employee_old/<int:emp_id>/',edit_employee_old),
    path('del_employee_old/<int:emp_id>/',del_employee_old),
# 操作部门数据表(department)相关URL配置项
    path('add_dep_old/',add_dep_old),
    path('list_dep_old/',list_dep_old),
    path('del_dep_old/<int:dep_id>/',del_dep_old),
    path('edit_dep_old/<int:dep_id>/',edit_dep_old),
# 操作团体数据表(group)相关URL配置项
    path('add_group_old/',add_group_old),
    path('list_group_old/',list_group_old),
    path('del_group_old/<int:group_id>/',del_group_old),
    path('edit_group_old/<int:group_id>/',edit_group_old),
# 操作员工补充信息数据表(employeeinfo)相关URL配置项
    path('add_employeeinfo_old/',add_employeeinfo_old),
    path('list_employeeinfo_old/',list_employeeinfo_old),
    path('del_employeeinfo_old/<int:info_id>/',del_employeeinfo_old),
    path('edit_employeeinfo_old/<int:info_id>/',edit_employeeinfo_old),
]

(1)以上代码分别建立了员工、部门、团体、员工补充信息的增、删、改、查配置项。
path()的两个参数中的一个用来匹配路径,被称作URL表达式,它匹配网址的方式类似于正则表达式;
另一个参数是视图函数名,视图函数在views.py中定义。

(2)我们在配置项中的URL表达式和视图函数名后都加了“_old”,
因为这是代码的初始版本,旨在讲解Django ORM的内容,没有对页面进行美化等,
后续会有一个新样例来进行界面的美化。

3.3.3 编写视图函数

导包

代码首先导入Django响应的3个模块render、redirect、Http Response,然后导入前面建立的4个数据模型:employee、department、group、employeeinfo。这里必须要注意Django ORM操作一般都是以“models.”开头,如models.department.objects.all()。而在这个视图函数中直接以数据模型开头,如department.objects.all()。这是因为开头已经用from .models import employee,department,group,employeeinfo导入数据模型;用from.import models时,就要以models.开头操作数据。

from django.shortcuts import render,redirect,HttpResponse
from .models import employee,department,group,employeeinfo
数据列表

部门
list_dep_old()函数通过Django ORM的数据操作命令取出所有记录,并将其保存在变量dep_list中,然后通过render()函数发送给list_dep.html。

其中render()函数有3个参数:
第一个参数request是固定的;
第二个参数是HTML文件;
第三个参数是字典类型,这个参数传值给HTML文件,在网页中以模板变量形式放置在相应的位置。

# views.py
def list_dep_old(request):
    dep_list=department.objects.all()
    return  render(request,'test_orm_old/list_dep_old.html',{'dep_list':dep_list})

下面是list_dep_old.html文件的代码,这个文件存放在/test_orm/templates/test_orm_old文件夹中。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>部门列表</title>
</head>
<body>
    <div align="center">
        <h1>部门列表</h1>
    </div>
    <div>
        <a href="/test_orm_old/add_dep_old/">增加一条记录</a>
    </div>
    <table boder="1">
        <thead>
        <tr>
            <td>部门名称</td>
            <td>备注说明</td>
            <td colspan="2">操作</td>
        </tr>
        </thead>
        <tbody>
          {% for dep in dep_list %}
          <tr>
              <td>{{ dep.dep_name }}</td>
              <td>{{ dep.dep_script }}</td>
              <td><a href="/test_orm_old/del_dep_old/{{ dep.id }}/">删除</a></td>
              <td><a href="/test_orm_old/edit_dep_old/{{ dep.id }}/">修改</a></td>
          </tr>
        {% empty %}
        <tr>
            <td colspan="4">无相关记录!</td>
        </tr>
        {% endfor %}
        </tbody>
    </table>
</body>
</html>
  1. 视图函数传入的变量dep_list是一个DjangoQuery Set对象,它是一个数据集,实际包含数据表中一行一行的记录,所以在HTML文件中用{%for dep in dep_list %}取出每行记录存在dep中,然后通过{{ dep.字段名}}(如{{dep.dep_name }})取出每个字段的值。提示:{% for ×× in ××_list %}…{% endfor %}是模板标签,是一个循环语句代码块,与Python语法相似。
  2. HTML文件中用{{}}、{%%}包括字符串的形式,是模板语言语法,在后面章节中将详细介绍
  3. <td><a href="/test_orm_old/del_dep_old/{{ dep.id}}/">删除</a></td>中的{{ dep.id }}获取department数据表记录的id值。
  4. /test_orm_old/del_dep_old/{{ dep.id }}/这个URL分两部分,/test_orm_old/与一级URL配置文件/test_orm/tes_orm/urls.pypath('test_orm_old/',include ('employee.urls'))配置项有关联,它匹配path()函数的第一个参数;
    del_dep_old/{{ dep.id }}/与二级URL配置文件/test_orm/employee/urls.pypath('del_dep_old/<int:dep_id>/',del_dep_old)配置项有关联,它匹配path()函数的第一个参数。可以理解为单击这个< a>标签,Django会调用views.py中del_dep_old()视图函数,这个函数由path()的第二个参数指定。同理,<td><a href="/test_orm_old/edit_dep_old/{{ dep.id}}/">修改中的链接也是通过匹配调用相应视图函数,实现相应的功能
  • 在一级配置文件的urls.py中加入path(‘test_orm_old/’,include(‘employee.urls’))配置项
  • 在二级配置文件的urls.py中加入path(‘list_dep_old/’,list_dep_old)配置项。
  • 在views.py中建立视图函数def list_dep_old(request),编写逻辑代码。
  • 在templates/test_orm_old文件夹中建立页面文件list_dep_old.html。

员工

def list_employee_old(request):
    emp_list=employee.objects.all()
    return  render(request,'test_orm_old/list_employee_old.html',{'employee_list':emp_list})
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>员工列表</title>
</head>
<body>
    <div align="center">
        <h1>员工列表</h1>
    </div>
    <div>
        <a href="/test_orm_old/add_employee_old/">增加一条记录</a>
    </div>
    <table boder="1">
        <thead>
        <tr>
            <td>姓名</td>
            <td>邮箱</td>
            <td>薪水</td>
            <td>地址</td>
            <td>部门</td>
            <td>团体</td>
            <td colspan="2">操作</td>
        </tr>
        </thead>
        <tbody>
          {% for emp  in employee_list %}
          <tr>
              <td>{{ emp.name }}</td>
              <td>{{ emp.email }}</td>
              <td>{{ emp.salary }}</td>
              <td>{{ emp.info.address }}</td>
              <td>{{ emp.dep.dep_name }}</td>
              <td>
               {% for gp in emp.group.all %}
                  {% if forloop.last %}
                  {{ gp.group_name }}
                  {% else %}
                  {{ gp.group_name }},
                  {% endif %}
                  {% endfor %}
              </td>
              <td>
                  <a href="/test_orm_old/del_employee_old/{{ emp.id }}/">删除</a>
              </td>
              <td>
                  <a href="/test_orm_old/edit_employee_old/{{emp.id }}/">修改</a>
              </td>
          </tr>
        {% empty %}
        <tr>
            <td colspan="7">无相关记录!</td>
        </tr>
        {% endfor %}
        </tbody>
    </table>
</body>
</html>

1.emp_list是视图函数传过来的变量,它是一个Django Query Set对象集,用{% for emp inemp_list %}取出每一个对象放到emp中。这样emp对象成为employee数据模型的实例化对象,外键dep、多对多键group、一对一键info这些关联关系也包含在emp对象中,因为Django ORM会自动把关联关系也放在Query Set对象中
2. 在模板语法{{ emp.dep }}中可以通过dep这个外键取得与它关联的department数据表中的一条记录,{{ emp.dep.dep_name }}自然可以取得department数据表中相关联的dep_name字段的值。
3. 同理{{ emp.group }}通过group这个多对多键可以取得group数据表中相关联的记录,由于是多对多关系,这些关联记录不只一条,所以需要用{% for gp in emp.group.all %}把记录一条条取出来放在gp中,这样{{ gp.group_name }}就可以显示团体的名称了。
4. 在模板语法{ emp.info }}中可以取得employeeinfo数据表中与info一对一键有关联关系的记录,{{ emp.info.address }}自然可以取得employeeinfo数据表中关联的address字段的值。
5. < a href="/test_orm_old/del_employee_old/{{emp.id }}/">删除</ a>中的del_employee_old/{{emp.id }}/匹配urls.py文件的配置项path('del_employee_old/< int:emp_id>/',delete_employee_old)语句中path()函数的第一个参数;< a href="/test_orm_old/edit_employee_old/{{emp.id }}/">修改</a>中的edit_employee_old/{{ emp.id }}/匹配urls.py文件的配置项path('edit_employee_old/< int:emp_id>/',edit_employee_old)中path()函数的第一个参数。
团体

def list_group_old(request):
    group_list=group.objects.all()
    return  render(request,'test_orm_old/list_group_old.html',{'group_list':group_list})
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>团体列表</title>
</head>
<body>
    <div align="center">
        <h1>团体列表</h1>
    </div>
    <div>
        <a href="/test_orm_old/add_group_old/">增加一条记录</a>
    </div>
    <table boder="1">
        <thead>
        <tr>
            <td>团体名称</td>
            <td>备注说明</td>
            <td colspan="2">操作</td>
        </tr>
        </thead>
        <tbody>
          {% for group in group_list %}
          <tr>
              <td>{{ group.group_name }}</td>
              <td>{{ group.group_script }}</td>
              <td><a href="/test_orm_old/del_group_old/{{ group.id }}/">删除</a></td>
              <td><a href="/test_orm_old/edit_group_old/{{ group.id }}/">修改</a></td>
          </tr>
        {% empty %}
        <tr>
            <td colspan="4">无相关记录!</td>
        </tr>
        {% endfor %}
        </tbody>
    </table>
</body>
</html>

员工补充信息

def list_employeeinfo_old(request):
    employeeinfo_list=employeeinfo.objects.all()
    return  render(request,'test_orm_old/list_employeeinfo_old.html',{'employeeinfo_list':employeeinfo_list})
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>员工联系信息列表</title>
</head>
<body>
    <div align="center">
        <h1>员工联系信息列表</h1>
    </div>
    <div>
        <a href="/test_orm_old/add_employeeinfo_old/">增加一条记录</a>
    </div>
    <table boder="1">
        <thead>
        <tr>
            <td>电话号码</td>
            <td>家庭住址</td>
            <td colspan="2">操作</td>
        </tr>
        </thead>
        <tbody>
          {% for employeeinfo  in employeeinfo_list %}
          <tr>
              <td>{{ employeeinfo.phone }}</td>
              <td>{{ employeeinfo.address }}</td>
              <td><a href="/test_orm_old/del_employeeinfo_old/{{ employeeinfo.id }}/">删除</a></td>
              <td><a href="/test_orm_old/edit_employeeinfo_old/{{ employeeinfo.id }}/">修改</a></td>
          </tr>
        {% empty %}
        <tr>
            <td colspan="4">无相关记录!</td>
        </tr>
        {% endfor %}
        </tbody>
    </table>
</body>
</html>
增添数据

部门

# add
def add_dep_old(request):
    if request.method=='POST':
        dep_name=request.POST.get('dep_name')
        dep_script=request.POST.get('dep_script')
        if dep_name.strip()== '':
            return render(request,'test_orm_old/add_dep_old.html',{'error_info':'部门名称不能为空'})
    try: # 用create()函数新建一条记录,这条记录会自动保存,不用调用save()函数
        p = department.objects.create(dep_name=dep_name, dep_script=dep_script)
        return redirect('/test_orm_old/list_dep_old/')
    except Exception as e:
        return render(request, 'test_orm_old/add_dep_old.html', {'error_info': '输入部门名称重复或信息有错误!'})
    finally:
        return render(request, 'test_orm_old/add_dep_old.html')
  1. 提交数据一般用POST,通过request.POST.get取得HTML文件中form的标签中的值,request.POST.get()函数中的参数就是HTML文件中标签的name属性。
  2. 代码把request.POST.get取得的值,传递给Django ORM的create()函数,生成一条数据记录。代码中p=department.objects.create(dep_name=dep_name,dep_script=dep_script)也可用以下两种方式代替。
    第一种方式
    obj =department(dep_name=dep_name,dep_script=dep_script)obj.save()
    第二种方式,用字典类型传值,注意键名与字段名要一致
    dic ={"dep_name":dep_name,"dep_script":dep_script}department .create(**dic)
  3. 在生成新的记录后,通过return redirect('/test_orm_old/list_dep_old/')
    重新定向到部门列表页面;redirect()函数的参数是一个字符串,注意这个字符串匹配的是
    URL配置项,不是HTML文件名。
    也就是return redirect('/test_orm_old/list_dep_old/')语句执行
    views.py中list_dep_old()视图函数,这个函数是由URL配置项中path()函数的
    第二个参数指定的。
  4. try...except…finally是Python处理异常的语法结构,一般把可能出现异常的代码放在try代码块中,except代码块中放置处理异常情况的代码,finally中放置的代码无论是否出现异常都要执行。在视图函数代码中,try 代码块新增加一条记录,并跳转到部门列表页面;如果 try 代码块中任何一个语句出现错误或异常则执行 exceptException as e 代码块,即重新定向到add_dep_old.html;finally代码块最后执行,这里用的是pass,即什么也不做。
  5. 如果判断出不是POST请求,表示当前用户是第一次请求增加部门页面,则执行代码最后一条
    语句,直接用 return render(request,'test_orm_old/add_dep_old.html')在浏览
    器上打开add_dep_old.html文件,并显示增加部门页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>增加部门</title>
</head>
<body>
    <div align="center">
        <h1>增加部门</h1>
        <hr>
        <form action="" method="post">
            {% csrf_token %}
             <input type="hidden" name="id" id="id" value="{{ department.id }}" >
        <div>
            <label>部门:</label>
            <input type="text" name="dep_name">
        </div>
        <br>
        <div>
            <label>备注:</label>
            <input type="text" name="dep_script",id="dep_script">
        </div>
        <br>
        <div>
            <input type="submit" value="保存">
        </div>
        </form>
        {{ error_info }}
    </div>
</body>
</html>
  1. 在HTML文件中建立一个< form action=""method="post">标签,method属性设置请求方式为post。提示:在HTML文件中的post可以是小写形式,而在Django中必须是全大写形式。action属性设置处理请求的地址(URL),这个地址要和匹配文件urls.py文件中配置项一致。
  2. < form>标签中每个< input>标签中的输入的值会随着POST请求传给视图函数,视图函数通过request.POST.get(‘×××’)形式取得输入的值,×××是< input>标签中属性name的值。
  3. < form>标签中的{% csrf_token %}是Django为了防止CSRF所做的保护,是一种安全机制。

员工

def add_employee_old(request):
    if request.method == "POST":
        name = request.POST.get("name")
        email = request.POST.get("email")
        dep = request.POST.get("dep")
        info = request.POST.get("info")
        salary = request.POST.get("salary")
        # 取得多个值
        groups=request.POST.getlist("group")
        new_emp=employee.objects.create(name=name,email=email,salary=salary,dep_id=dep,info_id=info)
        # 给多对多键字段赋值
        new_emp.group.set(groups)
        return redirect('/test_orm_old/list_employee_old/')
    dep_list = department.objects.all()
    group_list = group.objects.all()
    info_list = employeeinfo.objects.all()
    return render(request, 'test_orm_old/add_employee_old.html',
                     {'dep_list': dep_list, 'group_list': group_list, 'info_list': info_list})
  1. employee数据模型(数据库表)有个多对多键group,也就是说group对应的group数据库表中的记录可能有多条,因此不能用request.POST.get取值,要用getlist()才能取出多个值。
  2. 数据模型在生成数据表时,外键在数据表中产生的字段名为外键名_id。在生成数据表时,employee数据模型中的外键dep在表中的字段名为dep_id;同理,employee数据表中的一对一键在表中的字段名为info_id,因此在new_emp=employee.objects.create(name=name,email=email,salary=salary,dep_id=dep,info_id=info)语句中可以直接把变量值赋给dep_id和info_id。
  3. 多对多键group涉及多个值,因些在生成一条记录new_emp后,需通过new_emp.group.set(groups)进行赋值。
  4. 如果是第一次打开网页,这时需要将dep_list、group_list、info_list变量传递给HTML文件,3个变量分别保存着department、group、employeeinfo这3个数据库表的所有记录,通过render()函数传到网页,通过模板语言把相关的值放到< select>标签中以供用户选择使用。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>增加员工</title>
</head>
<body>
    <div align="center">
        <h1>增加员工</h1>
        <hr>
        <formaction="/test_orm_old/add_employee_old/"method="post">
            {% csrf_token %}
        <div>
            <label>姓名:</label>
            <input type="text" name="name">
        </div>
        <br>
        <div>
            <label>邮箱:</label>
            <input type="text" name="email"id="email">
        </div>
        <br>
        <div>
            <label>薪水:</label>
            <input type="text" name="salary"id="salary">
        </div>
        <br>
        <div>
            <label>联系信息:</label>
            <select name="info" id="info">
                {% for info in info_list %}
                    <option value="{{ info.id }}">
                        {{ info.phone }}||{{ info.address }}
                    </option>
                {% endfor %}
        </select>
        </div>
        <br>
        <div>
            <label>部门:</label>
            <select name="dep" id="dep">
                {% for dep in dep_list %}
                    <option value="{{ dep.id }}">
                        {{dep.dep_name }}
                    </option>
                {% endfor %}
            </select>
        </div>
        <br>
        <div>
            <label>团体:</label>
            <select name="group" id="group" multiple="true" >
                {% for group in group_list %}
                    <option value="{{ group.id }}">
                        {{group.group_name }}
                    </option>
                {% endfor %}
            </select>
        </div>
        <br>
        <div>
            <input type="submit" value="增加">
        </div>
        </form>
    </div>
</body>
</html>
  1. add_employee_old.html文件接收视图函数add_employee_old()中render()函数传入的dep_list变量,通过{% for dep in dep_list %}循环给< select>标签的< option>赋值。
  2. add_employee_old.html文件接收info_list变量,并通过{% for info in info_list %}循环给< select>标签的< option>赋值。
  3. 同理add_employee_old.html文件接收group_list变量,并通过{% for group ingroup_list%}循环给< select>标签的< option>赋值。这里< select>标签设置属性multiple="true",实现多选,满足多对多关系。

团体

def add_group_old(request):
    if request.method=='POST':
        group_name=request.POST.get('group_name')
        group_script=request.POST.get('group_script')
        if group_name.strip()=='':
         return render(request,'test_orm_old/add_group_old.html',{'error_info':'团体名不能为空!'})
    try:
        group.objects.create(group_name=group_name,group_script=group_script)
        return redirect('/test_orm_old/list_group_old/')
    except Exception as e:
        return render(request,'test_orm_old/add_group_old.html',{'error_info':'输入的团体名称重复或者信息有误'})
    finally:
        return render(request,'test_orm_old/add_group_old.html')
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>增加团体</title>
</head>
<body>
    <div align="center">
        <h1>增加团体</h1>
        <hr>
        <form action="" method="post">
            {% csrf_token %}
            <input type="hidden" name="id" id="id" value="{{ group.id }}" >
        <div>
            <label>团体:</label>
            <input type="text" name="group_name">
        </div>
        <br>
        <div>
            <label>备注:</label>
            <input type="text" name="group_script",id="group_script">
        </div>
        <br>
        <div>
            <input type="submit" value="增加">
        </div>
        </form>
        <div style="color:red">
           {{ error_info }}
        </div>
    </div>
</body>
</html>

员工补充信息

def add_employeeinfo_old(request):
    if request.method=='POST':
        phone=request.POST.get('phone')
        address=request.POST.get('address')
        if phone.strip()=='':
            return render(request,'test_orm_old/add_employeeinfo_old.html',{'error_info':'电话号码不能为空!'})
    try:
        employeeinfo.objects.create(phone=phone,address=address)
        return redirect('/test_orm_old/list_employeeinfo_old/')
    except Exception as e:
        return render(request,'test_orm_old/add_employeeinfo_old.html',{'error_info':'信息有误'})
    finally:
        return render(request,'test_orm_old/add_employeeinfo_old.html')
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>增加员工联系信息</title>
</head>
<body>
    <div align="center">
        <h1>增加员工联系信息</h1>
        <hr>
        <form action="" method="post">
            {% csrf_token %}
             <input type="hidden" name="id" id="id" value="{{ info.id }}" >
        <div>
            <label>电话号码:</label>
            <input type="text" name="phone">
        </div>
        <br>
        <div>
            <label>家庭住址:</label>
            <input type="text" name="address",id="address">
        </div>
        <br>
        <div>
            <input type="submit" value="增加">
        </div>
        </form>
        {{ error_info }}
    </div>
</body>
</html>
删除数据

下面是删除部门记录的视图函数。根据传入参数,获取id字段等于参数值的记录对象,然后删除这个记录对象。视图函数del_dep_old()的第二个参数是在urls.py文件配置项的path()函数中定义的。而path('del_dep_old/< int:dep_id>/',del_dep_old)中的< int:dep_id>,int指明数据类型,dep_id指明传入视图函数del_dep_old()的第二个参数名称。参数值来自部门列表文件list_dep_old.html中的{{ dep.id }}

# del
def del_dep_old(request,dep_id):
    dep_object=department.objects.get(id=dep_id)
    dep_object.delete()
    return redirect('/test_orm_old/list_dep_old/')

def del_employee_old(request,emp_id):
    emp=employee.objects.get(id=emp_id)
    return redirect('/test_orm_old/list_employee_old/')
def del_group_old(request,group_id):
    group_object=group.objects.get(id=group_id)
    group_object.delete()
    return redirect('/test_orm_old/list_group_old/')

def del_employeeinfo_old(request,employeeinfo_id):
    employeeinfo_object=employeeinfo.objects.get(id=employeeinfo_id)
    employeeinfo_object.delete()
    return redirect('/test_orm_old/list_employeeinfo_old/')
编辑数据

部门
视图函数edit_dep_old()实现修改功能。代码首先判断请求方式是不是POST请求,如果是POST请求,通过dep_object=department.objects.get(id=id)取出数据表记录,然后给每一个字段赋值,这些值是POST请求传递过来的,最后通过dep_object.save()保存数据到数据表中。如果不是 POST 请求就推断出是第一次请求页面,首先根据参数 dep_id(来自部门列表文件list_dep_old.html中的{{ dep.id }})取出记录放到变量dep_object中,通过render()函数传递到edit_dep_old.html文件中

def edit_dep_old(request,dep_id):
    if request.POST.get('id'):
        dep_name=request.POST.get('dep_name')
        dep_script=request.POST.get('dep_script')
        dep_object=department.objects.get(id=dep_id)
        dep_object.dep_name = dep_name
        dep_object.dep_script = dep_script
        dep_object.save()
        return redirect('/test_orm_old/list_dep_old/')
    else:
        dep_object = group.objects.get(id=dep_id)
        return render(request, 'test_orm_old/edit_dep_old.html', {'department': dep_object})

视图函数edit_dep_old()通过return render(request,'test_orm_old/edit_dep_old.html',{'department':dep_object})传递参数给edit_dep_old.html文件,这个文件的部分代码如下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>修改部门</title>
</head>
<body>
    <div align="center">
        <h1>修改部门</h1>
        <hr>
        <form action="" method="post">
            {% csrf_token %}
            <input type="hidden" name="id" id="id"value="{{ department.id }}" >
            <div>
                <label>部门:</label>
                <input type="text" name="dep_name"value="{{department.dep_name }}">
            </div>
            <br>
            <div>
                <label>备注:</label>
                <input type="text" name="dep_script"id="dep_script" value="{{department.dep_script }}">
            </div>
            <br>
            <div>
                <input type="submit" value="保存">
            </div>
        </form>{{ error_info }}
    </div>
</body>
</html>
  1. 以上代码与add_dep_old.html中的代码相似,只是在< input>标签中给value属性进行了赋值,形如value="{{ xxx }}"。
  2. 在标签中增加一个隐含的< inptut>标签(< input type=“hidden” name=“id"id=“id"value=”{{ department.id }}”>),用于保存数据库表记录主键(id)的值,并提供给视图函数修改记录。

员工

def edit_employee_old(request,emp_id):
    if request.method = = "POST":
        id = request.POST.get('id')
        name = request.POST.get("name")
        email = request.POST.get("email")
        dep = request.POST.get("dep")
        info = request.POST.get("info")
        groups = request.POST.getlist("group")
        emp = employee.objects.get(id=id)
        emp.name = name
        emp.email = email
        emp.dep_id = dep
        emp.info_id = info
        emp.group.set(groups)
        emp.save()
        return redirect('/test_orm_old/list_employee_old/')
    emp = employee.objects.get(id=emp_id)
    dep_list = department.objects.all()
    group_list = group.objects.all()
    info_list = employeeinfo.objects.all()
    return render(request, 'test_orm_old/edit_employee_old.html',
                  {'emp': emp, 'dep_list': dep_list, 'group_list': group_list, 'info_list': info_list})
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>修改员工信息</title>
</head>
<body>
    <div align="center">
        <h1>修改员工信息</h1>
        <hr>
        <form action="" method="post">
            {% csrf_token %}
            <input type="hidden" name='id' id='id'value={{ emp.id }}>
            <div>
                <label>姓名:</label>
                <input type="text" name="name" value={{emp.name }}>
            </div>
            <br>
            <div>
                <label>邮箱:</label>
                <input type="text" name="email" id="email"value={{ emp.email }}>
            </div>
            <br>
            <div>
                <label>联系信息:</label>
                <select name="info" id="info">
                    {% for info in info_list %}
                        {% if emp.info_id == info.id %}
                            <option value="{{ info.id }}" selected >
                                {{info.phone }}||{{ info.address }}
                            </option>{% else %}
                            <option value="{{ info.id }}">
                                {{ info.phone }}||{{ info.address }}
                            </option>
                        {% endif %}{% endfor %}
                </select>
            </div>
            <br>
            <div>
                <label>部门:</label>
                <select name="dep" id="dep">
                    {% for dep in dep_list %}
                        {% if emp.dep_id == dep.id %}
                            <option value="{{ dep.id }}" selected >
                                {{dep.dep_name }}
                            </option>
                        {% else %}
                            <option value="{{ dep.id }}" >
                                {{dep.dep_name }}
                            </option>
                        {% endif %}
                    {% endfor %}
                </select>
            </div>
            <br>
        <div>
            <label>团体:</label>
            <select name="group" id="group" multiple="true" >
                {% for group in group_list %}
                    {% if group in emp.group.all %}
                        <option value="{{ group.id }}" selected>
                            {{group.group_name }}
                        </option>
                    {% else %}
                        <option value="{{ group.id }}">
                            {{group.group_name }}
                        </option>
                    {% endif %}
                {% endfor %}
            </select>
        </div>
            <br>
            <div>
                <input type="submit" value="保存">
            </div>
        </form>
    </div>
</body>
</html>
  1. {% for info in info_list %}代码块中,通过循环取出employeeinfo数据表中的记录对象,存放在info变量中。然后用info.id与员工记录中的info_id比较,如果相同就设置< select>标签中的< option>标签为selected。
  2. {% for dep in dep_list %}循环中,通过{% if emp.dep_id == dep.id %}判断员工记录的外键值与department数据表记录的id值是否一样,如果一样就设置< select>标签中< option>标签为select
  3. 由于employee的group多对多键可以和多条group数据表记录关联,首先要设置< select>标签multiple="true",然后要在循环中通过{% ifgroup in emp.group.all %}语句判断group数据表记录是否存在于多对多键employee.group关联的记录中,如在其中就设置< select>标签中< option>标签为selected。

团体

def edit_group_old(request,group_id):
    if request.method = ='POST':
        id = request.POST.get('id')
        group_name = request.POST.get('group_name')
        group_script = request.POST.get('group_script')
        group_object = group.objects.get(id=group_id)
        group_object.group_name = group_name
        group_object.group_script =group_script
        group_object.save()
        return redirect('/test_orm_old/list_group_old/')
    else:
        group_object = group.objects.get(id=group_id)
        return render(request, 'test_orm_old/edit_group_old.html', {'group': group_object})
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>修改团体</title>
</head>
<body>
    <div align="center">
        <h1>修改团体</h1>
        <hr>
        <form action="" method="post">
            {% csrf_token %}
            <input type="hidden" name="id" id="id" value="{{ group.id }}" >
            <div>
                <label> 团体:</label>
                <input type="text" name="group_name" value="{{ group.group_name }}">
            </div>
            <br>
            <div>
                <label>备注:</label>
                <input type="text" name="group_script"id="group_script" value="{{group.group_script }}">
            </div>
            <br>
            <div>
                <input type="submit" value="保存">
            </div>
        </form>{{ error_info }}
    </div>
</body>
</html>

员工补充信息

def edit_employeeinfo_old(request,info_id):
    if request.method=='POST':
        id = request.POST.get('id')
        phone = request.POST.get('phone')
        address = request.POST.get('address')
        info_object = employeeinfo.objects.get(id=info_id)
        info_object.phone = phone
        info_object.address = address
        info_object.save()
        return redirect('/test_orm_old/list_employeeinfo_old/')
    else:
        info_object = employeeinfo.objects.get(id=info_id)
        return render(request, 'test_orm_old/edit_employeeinfo_old.html', {'employeeinfo': info_object})
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>修改员工信息</title>
</head>
<body>
    <div align="center">
        <h1>修改员工信息</h1>
        <hr>
        <form action="" method="post">
            {% csrf_token %}
            <input type="hidden" name='id' id='id'value={{ emp.id }}>
            <div>
                <label>姓名:</label>
                <input type="text" name="name" value={{emp.name }}>
            </div>
            <br>
            <div>
                <label>邮箱:</label>
                <input type="text" name="email" id="email"value={{ emp.email }}>
            </div>
            <br>
            <div>
                <label>联系信息:</label>
                <select name="info" id="info">
                    {% for info in info_list %}
                        {% if emp.info_id == info.id %}
                            <option value="{{ info.id }}" selected >
                                {{info.phone }}||{{ info.address }}
                            </option>{% else %}
                            <option value="{{ info.id }}">
                                {{ info.phone }}||{{ info.address }}
                            </option>
                        {% endif %}{% endfor %}
                </select>
            </div>
            <br>
            <div>
                <label>部门:</label>
                <select name="dep" id="dep">
                    {% for dep in dep_list %}
                        {% if emp.dep_id == dep.id %}
                            <option value="{{ dep.id }}" selected >
                                {{dep.dep_name }}
                            </option>
                        {% else %}
                            <option value="{{ dep.id }}" >
                                {{dep.dep_name }}
                            </option>
                        {% endif %}
                    {% endfor %}
                </select>
            </div>
            <br>
        <div>
            <label>团体:</label>
            <select name="group" id="group" multiple="true" >
                {% for group in group_list %}
                    {% if group in emp.group.all %}
                        <option value="{{ group.id }}" selected>
                            {{group.group_name }}
                        </option>
                    {% else %}
                        <option value="{{ group.id }}">
                            {{group.group_name }}
                        </option>
                    {% endif %}
                {% endfor %}
            </select>
        </div>
            <br>
            <div>
                <input type="submit" value="保存">
            </div>
        </form>
    </div>
</body>
</html>

3.4 Django ORM 跨表操作

Django中的数据模型关联关系主要是由外键、多对多键、一对一键形成的关联关系,Django数据模型的实例对象中保存着关联关系相关的信息,这样在跨表操作过程中,我们可以根据一个表的记录的信息查询另一个表中相关联的记录,充分利用关系型数据库的特点。

3.4.1 与外键有关的跨表操作

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

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

dep_obj=department.objects.get(id=8)
# 通过dep_obj.employee_set.all()取得一个部门的所有员工名
emp_list=dep_obj.employee_set.all()
外键跨表操作 样例

首先在/test_orm/employee/urls.py文件中加入以下语句,建立路径与视图函数的对应关系,这样网址与视图函数就联系在一起了。

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()
emp_names=[emp.name for emp in emp_list]
return Http Response("1.正向关联:员工名称:{0},所在部门名称:{1}<br> 2.反向查找:部门名称:{2},部门员工:{3}".format(emp.name,dep_name,dep_obj.dep_name,emp_names))
外键跨表查询字段

查询字段的值也分正向操作与反向操作两种形式

  • 正向操作查询字段值,取得字段值的形式为外键+双下划线+关联表的字段名,如下所示。
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字段的值。
values_list()返回的是元组(由字段值组成)列表,所以print(emp)返回的值如下所示

<Query Set[
('李立', '财务部', '财务管理'),
 ('sales', '经营部', '经营、考核'), 
 ('张好人','经营部', '经营、考核'),
  ('张三', '资产管理中心', '资产管理'), 
  ('tom', '资产管理中心','资产管理'), 
  ('刘七云', '审计部', '审计管理1')]>

values()返回的值是字典列表,列表每一项为字典型、键名是字段名、值为字段值,如下所示

<Query Set[
{'name': '李立', 'dep__dep_name': '财务部', 'dep__dep_script': '财务管理'},
{'name': 'sales', 'dep__dep_name': '经营部', 'dep__dep_script': '经营、考核'}
{'name': '张好人', 'dep__dep_name': '经营部', 'dep__dep_script': '经营、考核'}
{'name': '张三', 'dep__dep_name': '资产管理中心', 'dep__dep_script':'资产管理'},
{'name': 'tom', 'dep__dep_name': '资产管理中心', 'dep__dep_script':'资产管理'},
{'name': '刘七云', 'dep__dep_name': '审计部', 'dep__dep_script': '审计管理1'}
]>
  • 反向操作查询字段值,取得字段值的形式为表名+双下划线+字段名,表名是有外键字段的表的名称
dep_emp=models.department.objects.values_list("employee__name")
print(dep_emp)

第二行代码print()返回一个元组(由字段值组成)列表。

<Query Set[
('张好人', 'zhangsan@163.com'), 
('刘七云', 'lqy@126.com'),
('sales','sales@163.com'),
('李立', 'll@163.com'), 
('张三','zhangsan@163.com'),
 ('tom','tom@163.com')]>

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

# 员工的部门、外键,形成一对多的关系,定义了related_name='dep_related'
dep=models.ForeignKey(to="department",to_field="id",related_name='dep_related',on_delete=models.CASCADE)
# 以上外键dep定义了related_name属性,取字段值用以下代码。
dep_emp=models.department.objects.values_list("dep_related__name","dep_related__email")

3.4.2 与多对多键有关的跨表操作

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字段的同名属性意义相同.
提示:如果使用了related_name,在反向操作中就不能用“表名_set”

多对多键跨表关联操作

正向操作指的是从有多对多键的表查找关联表,反向操作指的是从关联表查找有多对多键的表。跨表操作主要用函数进行。

  • **create()**函数,创建一个新的记录并保存在数据库表中,最后将它添加到关联对象集(记录集)之中。
    正向操作先通过models.employee.objects.first()等形式的查询语句取出employee数据表中的记录,再通过group这个多对多键把create()新生成的group记录关联到从employee中取出的表记录,并且将生成的一条group记录自动存到数据表中。
    反向操作先通过models.group.objects.first()或models.group.objects.get(id=4)等形式的查询语句取出group数据表中的记录,且通过employee_set(表名_set)把create()新生成的employee记录关联到从group中取出的记录,并且将生成的一条employee记录自动存到数据表中
# 正向操作
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_list=models.group.objects.filter(id__lt=6)
models.employee.objects.first().group.add(*group_list)
models.employee.objects.first().group.add(*[1,2,6])

models.employee.objects.first().group.add(*group_list)第一行代码先把group的记录取出来放到变量group_list中,第二行代码把取出的记录通过group.add(*group_list)关联到models.employee.objects.first()取出的记录上,注意变量前要加“*”号。同理可以通过id值进行关联,以下代码是把group中id值为1、2、6的记录关联到employee表的第一条记录上,注意列表变量前要加“*”号。

  • 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()函数,从记录对象中删去一切关联记录。以下代码将删去employee数据表中最后一条记录与group数据表中关联的一切记录。
models.employee.objects.last().group.clear()
多对多关联跨表查询字段值
  • 正向操作查询字段值,取得字段值的形式为多对多键+双下划线+关联表的字段名,如下所示
emp_m2m=models.employee.objects.values_list("id","name","group__group_name")
print(emp_m2m)

id和name为employee数据表中字段,group__group_name可以取group数据表中的group_name字段的值,返回值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', '游泳队')]>

可以看到如果一条记录关联多个值,这条记录将形成多条元组。

  • 反向操作查询字段值,取得字段值的形式为表名+双下划线+字段名,表名用的是存在多对多键字段的表的名称
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'},…{'group_name': '登山团队', 'employee__name':'李立', 'employee__email':'ll@163.com'},
...
>

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

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

One to One Field

一对一的关联关系把本来可以存储在一个表的字段拆开分别放置在两个表中,将查询次数多的字段放在一个表中,将查询次数较少的字段放在另一个表中,然后为两个表建立一对一的关联关系
One To One Field字段一般在models.py文件的数据模型类中定义。代码如下所示,我们建立了一个数据模型类employeeinfo。

class employeeinfo(models.Model):phone = models.Char Field(max_length=11)address = models.Char Field(max_length=50)

employeeinfo与employee中的每条记录都是一一对应关系。employee数据模型类有一个字段info,字段类型是One To One Field,通过它与employeeinfo产生一对一关联关系

# 员工数据模型(员工数据表)
class employee(models.Model):# 一对一字段
info = models.One To OneField(to='employeeinfo',related_name="info_related",on_delete=models.CASCADE)
一对一键跨表关联操作

一对一键跨表关联操作也涉及正向操作与反向操作,正向操作从有一对一键的表查找关联表,反向操作从关联表查找有一对一键的表。正向操作和反向操作代码如下,其形式与外键基本一样,只是反向操作不用“表名_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_relate
demp_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
一对一关联跨表查询字段值
# 正向操作查询字段值。
emp_one=models.employee.objects.values("id","name","info__phone","info__address")
# 反向操作查询字段值。
emp_one2 =models.employeeinfo.objects.values("phone","address","employee__name","employee__email")

3.5 Django ORM 聚合和分组查询

能够返回数据库表记录集的语句都是DjangoORM查询语句。Django ORM查询语句支持链式操作,在查询语句的后面加上.aggregate()就是应用聚合查询,在查询语句的后面加上.annotate()就是应用分组查询

3.5.1 聚合查询

聚合查询主要对“.aggregate()”前面的查询语句取得的数据库表记录进行聚合计算。聚合计算主要有求合计值、求平均值、求最大值、求记录数等,因此aggregate()的参数主要是聚合函数Avg()、Sum()、Max()、Min()、Count()等

在employee数据库表中有一个salary字段,其值是数值类型的。

# 员工数据模型(员工数据表)
class employee(models.Model):
…salary=models.DecimalField(max_digits=8,decimal_places=2)
#如下代码取得salary字段值并求合计值。
from django.db.models import Sum
salary_sum=models.employee.objects.filter(id__lt=18).aggregate(Sum("salary"))
print(salary_sum)
  • 用聚合查询,首先要导入与聚合函数相关的模块。
  • models.employee.objects.filter(id__lt=18).aggregate(Sum(“salary”))可以分成两部分。第一部分models.employee.objects.filter(id__lt=18)能够实现id值小于18的记录的查询。第二部分.aggregate(Sum(“salary”))通过聚合函数Sum()把查询到的所有记录的salary字段值加在一起。提示:字段名要包含在引号内。
  • 聚合查询返回一个包含一些键值对的字典,返回值形式如下,这里可以看到返回值键名为“字段名+双下划线+聚合函数”。{‘salary__sum’: Decimal(‘89787.76’)}下面代码为聚合查询返回的字典的键指定一个名称,返回值为{‘salary_hj’:Decimal(‘89787.76’)}。

如果你希望生成不止一个聚合查询值,可以向aggregate()中添加多个聚合函数,如下所示

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')}

3.5.2 分组查询

分组查询对“.annotate()”前面的查询语句返回的数据库表记录进行分组聚合计算,根据前面的查询语句是否含有values()函数进行分组聚合计算

查询语句不含values()函数

(1)以下是分组查询的一个样例,其统计每个员工参加的团体的个数。

emp_list=models.employee.objects.annotate(groupnum=Count("group"))
for emp in emp_list:
print(emp.name,':参加',emp.groupnum,'个团体')

models.employee.objects得到employee中所有的记录(员工记录),有n个员工,就分n个组,每一组再由annotate()中的聚合函数进行分组统计。在annotate()中通过Count(“group”)对每组包含的group个数进行统计,统计值赋给groupnum。models.employee.objects.annotate(groupnum=Count(“group”)),返回employee数据表中全部记录,并且为每一条记录加一个新字段groupnum。这样通过循环就可以得到每一行记录,因此可以打印出每条记录的字段
以上代码打印内容如下。

李立 :参加 3 个团体sales :参加 3 个团体张好人 :参加 2 个团体

(2)统计每一个部门薪水最高值,代码如下

dep_list=models.department.objects.annotate(maxsalary=Max("employee__salary"))
for dep in dep_list:print(dep.dep_name,dep.maxsalary)

Max(“employee__salary”)中的employee__salary通过双下划线取得关联表的字段值
以上代码还有另一种实现方式,可以采用values_list()函数。如下所示代码可实现同样的功能

dep_list=models.department.objects.annotate(maxsalary=Max("employee__salary")).values_list("dep_name","maxsalary")
for dep in dep_list:print(dep)
查询语句包含values()函数

下面代码中“values(‘dep’)”起的作用就是以dep值分组字段,相当于SQL语句中的group bydep。代码实现的功能就是计算每个部门员工的平均工资。

dep_salary=models.employee.objects.values('dep').annotate(avg=Avg("salary")).values('dep__dep_name',"avg")
print(dep_salary)

以上代码通过values()函数返回字典列表,形式如下

<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}
 ]>

3.6 Django ORM 中的F和Q函数

3.6.1 F 函数

在Django ORM查询语句中,要实现字段值与字段值的比较或运算操作等就要用到F函数,在F函数中传入字段名就能取得字段的值。这个函数较易理解,这里只简单介绍。
以下代码实现id值小于30的员工的薪水增加600的功能。提示:传到F函数中的字段名要用引号括起来。

from django.db.models import F
models.employee.objects.filter(id__lt=30).update(salary=F("salary")+600)

3.6.2 Q函数

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

from django.db.models import Q
obj=models.employee.objects.filter(Q(id__lt=30)|Q(salary__lt=1000))

2)查询employee数据表中salary值大于1000并且name字段值开头不是“李”的记录

from django.db.models import Q
obj=models.employee.objects.filter(Q(salary__gt=1000)&~Q(name__startswith='李'))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zyw2002

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值