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(可执行对象)
三、


浙公网安备 33010602011771号