Django 之 ORM表关系创建、请求生命周期
QuerySet API 参考 | Django 文档 | Django (djangoproject.com)
一、
1、修改和删除功能的逻辑
'''修改功能的逻辑'''
1、 确定修改哪条记录,怎么确定? 通过主键id确定唯一一条记录 2、点击修改的按钮,应该跳转到一个修改的页面 3、应该通过id查询到原来的数据,并且把这个记录的数据展示到修改的页面 4、开始修改,提交到后端的修改数据的方法中
'''删除功能的逻辑'''
1、确定删除哪条记录,怎么确定? 通过主键id确定唯一一条记录 2、点击删除的按钮,请求到后端的删除地址 3、后端拿到id直接做删除操作、跳转到列表页面
2、用户信息展示主页面html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> {% load static %} <link href="{% static 'bootstrap_css/bootstrap.min.css' %}" rel="stylesheet"> <script src="{% static 'jq/jquery.js' %}"></script> <script src="{% static 'bootstrap_js/bootstrap.min.js' %}"></script> </head> <body> <div class="container"> <div class="row"> <h1 class="text-center">用户数据列表</h1> <a href="/user_add/" class="btn btn-info" style="margin-bottom: 22px">添加用户</a> <table class="table table-hover table-striped "> <tr> <th class="text-center">ID</th> <th class="text-center">用户名</th> <th class="text-center">密码</th> <th class="text-center">年龄</th> <th class="text-center">性别</th> <th class="text-center">操作</th> </tr> {# user_list是user_show视图函数中从数据库查到的查询结果集 #} {# 注意: {% %} 写逻辑,{{ }} 写变量#} {% for user in user_list %} <tr> <th class="text-center">{{ user.id }}</th> <th class="text-center">{{ user.username }}</th> <th class="text-center">{{ user.age }}</th> <th class="text-center">{{ user.gender }}</th> <th class="text-center">{{ user.password }}</th> <th class="text-center"> <div class="d-flex justify-content-center"> <a href="/user_edit/?id={{ user.id }}" class="btn btn-success d-flex justify-content-center ">修改</a> <a href="/user_del/?id={{ user.id }}" class="btn btn-danger d-flex justify-content-center ">删除</a> </div> </th> </tr> {% endfor %} </table> </div> </div> </body> </html>
注:
2.1、 动态加载静态文件令牌
{% load static %} <link href="{% static 'bootstrap_css/bootstrap.min.css' %}" rel="stylesheet">
2.2、 a 超链接标签,设置按钮效果,style样式设置 margin-bottom 外边距底部间距宽度
<a href="/user_add/" class="btn btn-info" style="margin-bottom: 22px">添加用户</a>
小技巧:开发者页面找到style样式双击选中,鼠标上下滚动,可以动态的看到效果
2.3、 修改删除按钮的居中效果:
将两个a标签使用div套着,外面使用 class="text-center" 居中类
<th class="text-center"> <div> <a href="/user_edit/?id={{ user.id }}" class="btn btn-success">修改</a> <a href="/user_del/?id={{ user.id }}" class="btn btn-danger">删除</a> </div> </th>
2.4、 for 循环的使用:for + tab
被循环体的选择,这里的user_list 是视图函数里面从数据库查询到的结果集,是一个对象,可以使用点方法取值
{# user_list是user_show视图函数中从数据库查到的查询结果集 #} {# 注意: {% %} 写逻辑,{{ }} 写变量#} {% for user in user_list %} {% endfor %}
2.5、 视图函数
def user_show(request): # 从数据库中查询用户的数据列表 user_list = models.Userinfo.objects.all() # .all() 查完相当于select * from db # print(user_list) print(locals()) # {'user_list': <QuerySet [<Userinfo: zjz>, <Userinfo: ldj>, <Userinfo: ccy>, <Userinfo: xn>, <Userinfo: zfq>, <Userinfo: nfj>]>, 'request': <WSGIRequest: GET '/user_show/'>} return render(request, 'user_show.html', locals())
注:这里打印user_list 查询集能够看到用户名是因为models文件与数据库交互使用了__str__(self) 魔术方法,当打印对象时主动触发执行。
class Userinfo(models.Model): username = models.CharField(max_length=64) password = models.CharField(max_length=64) age = models.IntegerField() gender = models.CharField(max_length=64) # 当打印对象时自动触发,返回用户名 def __str__(self): return self.username
3、前端动态获取后端数据
在 Django 中,render
是一个用于渲染模板并返回 HTTP 响应的函数。它通常用于将数据渲染到指定的模板中,并将渲染后的内容作为 HTTP 响应返回给客户端。
render
函数的第三个参数 context
是一个包含模板上下文数据的字典,它将被用于在模板中渲染动态内容。模板上下文是一个包含变量和值的数据结构,这些变量可以在模板中使用,以便在生成最终的 HTML 内容时填充数据。
以下是 render
函数的参数及其作用的简要说明:
-
request
(必需):表示当前的 HTTP 请求对象,包含了客户端发送的所有请求信息,如 GET 或 POST 参数、请求头、用户信息等。 -
template_name
(必需):表示要渲染的模板文件的名称。这里可以是一个包含模板名称的字符串,也可以是一个模板名称列表。Django 将根据给定的模板名称来查找并渲染对应的模板文件。 -
context
(可选):一个字典,包含要传递给模板的上下文数据。字典中的键是模板中使用的变量名,值是要填充到模板中的数据。例如,如果有一个变量name
需要在模板中显示,那么可以将{"name": "John"}
作为context
传递给render
函数。 -
content_type
(可选):表示 HTTP 响应的内容类型。默认为None
,这将使用 Django 自动推断的内容类型。如果需要指定特定的内容类型(如"text/html"
或"application/json"
等),可以在这里设置。 -
status
(可选):表示 HTTP 响应的状态码。默认为None
,这将使用200
表示成功。可以在这里指定其他状态码,如404
表示页面未找到。 -
using
(可选):表示要使用的模板引擎。默认为None
,这将使用 Django 的默认模板引擎。如果你在项目中使用了多个模板引擎,可以在这里指定要使用的特定引擎的名称。
3.1、修改页面html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> {% load static %} <link href="{% static 'bootstrap_css/bootstrap.min.css' %}" rel="stylesheet"> <script src="{% static 'jq/jquery.js' %}"></script> <script src="{% static 'bootstrap_js/bootstrap.min.js' %}"></script> </head> <body> <div class="container"> <div class="row"> <h1 class="text-center">修改信息界面</h1> {# 空白不写,默认是当前页面,即127.0.0.1/user_edit #} <form action="" method="post"> <input type="hidden" value="{{ userinfo.id }} " name="hidden_id" > <div class="form-group"> username: <input type="text" class="form-control" name="username" value="{{ userinfo.username }}" required> </div> <div class="form-group"> password: <input type="text" class="form-control" name="password" value="{{ userinfo.password }}" required> </div> <div class="form-group"> age: <input type="text" class="form-control" name="age" value="{{ userinfo.age }}" required> </div> <div class="form-group"> gender: <input type="text" class="form-control" name="gender" value="{{ userinfo.gender }}" required> </div> <input type="submit" value="提交" class="btb btn-info" /> </form> </div> </div> </body> </html>
注意:
1. <form action="" method="post"> form表单中aciion不写服务器地址,默认是本地ip:port➕当前页面的拼接。即这个html文件名,127.0.0.1:8000/user_edit/
2. form 表单中required参数要求必须填写数据,不能为空。
3. value="{{ userinfo.username }}" 前端动态加载数据库的信息,这里以用户名为例。
能加载的前提是 return render(request, 'user_edit.html', locals()) ,render的第三个参数 context
是一个包含模板上下文数据的字典,它将被用于在模板中渲染动态内容。这里传一个locals()局部变量。
3.2、修改用户信息的视图函数
def user_edit(request): # 修改数据之前需要做一个原来数据的展示 id = request.GET.get('id') # get 传参?id={{ user.id }}" # userinfo = models.Userinfo.objects.filter(id=id).first() userinfo = models.Userinfo.objects.filter(pk=id).first() # 获取客户端传过来的数据 if request.method == 'POST': hidden_id = request.POST.get('hidden_id') username = request.POST.get('username') password = request.POST.get('password') age = request.POST.get('age') gender = request.POST.get('gender') # 写入数据库,第一种方式 models.Userinfo.objects.filter(pk=hidden_id).update(username=username, password=password, age=age, gender=gender) return render(request, 'user_edit.html', locals())
注意这里的逻辑:
urls 路由过来以后,没有匹配到POST方法,走render渲染的修改页面,一旦在前台修改提交就是POST方法,走写数据的逻辑
第二种修改方式
userinfo.username = username userinfo.password = password userinfo.age = age userinfo.gender = gender userinfo.save() # 保存数据的 return redirect('/userlist/')
4、删除用户视图函数:
def user_del(request): id = request.GET.get('id') models.Userinfo.objects.filter(pk=id).delete() # user_obj = models.UserInfo.objects.filter(pk=id).first() # 第一次先查询 # user_obj.delete() # 在删除 return redirect('/user_show/')
注:删除用户以后这里使用静态页面的令牌直接返回一个主页面
5、添加用户
5.1、 html页面的写法和修改用户页面基本一致
5.2、 视图函数
def user_add(request): if request.method == 'POST': # username = request.POST.get('username') password = request.POST.get('password') age = request.POST.get('age') gender = request.POST.get('gender') models.Userinfo.objects.create(username=username, password=password, age=age, gender=gender) return redirect('/user_show/') return render(request, 'user_add.html')
注:
这里有渲染两个页面,当用户进入添加页面,为提交数据不走主页面,提交完数据后返回主页面
二、
1、以图书表、出版社表、作者表、作者详情表为例创建
分析表关系
分析表关系: 图书表和出版社表是一对多的关系 >>> 外键如何创建:外键字段建在多的一方 图书表和作者表是多对多的关系 >>> 外键如何创建:外键字段建在第三张表中 """ 创建多对多的方式有3种方式,先将一种 """ 作者表和作者详情是一对一的关系 >>> 外键如何创建:外键字段建在查询频率较高的一方 # 创建表关系字段先创建表的基础字段,先创建没有的外键的,最后在写外键字段
2、代码 django1版本
先写各个表及其字段、再写外键的关系
from django.db import models # 图书表 class Book(models.Model): title = models.CharField(max_length=64) price = models.DecimalField(max_digits=8, decimal_places=2) # 一个出版社可以对应多个图书,外键建在多的一方,即图书表 # publish_id = models.ForeignKey(to='Publish', to_fields='id') # to Publish表,to表字段 publish_id = models.ForeignKey(to='Publish') # 外键默认跟表的主键字段,可以省略不写 # 图书和作者是多对多关系, # 多对多外键创建有三种方法,这里authors是一个虚拟字段,在book中不会实际创建出来,它能自动创建出来第三张表 authors = models.ManyToManyField(to='Author') # 省略to_fields='id' # 出版社表 class Publish(models.Model): title = models.CharField(max_length=64) addr = models.CharField(max_length=64) # 作者表 class Author(models.Model): name = models.CharField(max_length=64) # 作者与作者详情表是一对一的关系,外键创建在使用频率搞的一方 author_detail_id = models.OneToOneField(to='Author_Detail') # to_fields='id' 省略 # 作者详情表 class Author_Detail(models.Model): phone = models.CharField(max_length=64) qq = models.CharField(max_length=64)
makemigrations 、migrate后
app01_author app01_author_detail app01_book app01_book_authors # 多对多关系,第三张表 app01_publish auth_group auth_group_permissions auth_permission auth_user auth_user_groups auth_user_user_permissions django_admin_log django_content_type django_migrations django_session
问题:图书表和作者表的外键都拼接了一个_id。
原因:
在 Django 中,当定义一个外键关系时,默认情况下 Django 会自动给外键字段添加 "_id" 后缀。这是 Django 的约定,用于表示该字段是外键,其值是对关联表中的主键字段的引用。
Book
表定义一个外键字段 publish_id
,关联到 Publish表,
由于没有明确指定外键字段的名称,Django 默认会给外键字段加上 "_id" 后缀,以表示它是对 Publish
表主键id的引用。
如果想自定义外键字段的名称,可以在定义外键时使用 db_column
参数,例如:
publish = models.ForeignKey(to='Publish', db_column='publish_id')
3、代码 django2 版本
问题1:
publish = models.ForeignKey(to='Publish') # 外键默认跟表的主键字段,可以省略不写 TypeError: __init__() missing 1 required positional argument: 'on_delete'
原因:
models.ForeignKey
的on_delete
参数是可选的,如果未提供它,它会默认为CASCADE
。
然而,从Django 2.0版本开始,on_delete
参数变为了必需的,如果不提供它将会导致一个TypeError
错误。
解决
author_detail = models.OneToOneField(to='Author_Detail', on_delete='models.CASCADE')
补充:
django2.x 中on_delete
参数的可选参数
models.CASCADE
删除关联数据,与之关联也删除
models.DO_NOTHING
删除关联数据,引发错误IntegrityError
models.PROTECT
删除关联数据,引发错误ProtectedError
models.SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
models.SET_DEFAULT
删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
models.SET
删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
三、