Django 之 ORM表关系创建、请求生命周期

QuerySet API 参考 | Django 文档 | Django (djangoproject.com)

一、结合前端页面实现ORM对数据的增删改查

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 函数的参数及其作用的简要说明:

  1. request(必需):表示当前的 HTTP 请求对象,包含了客户端发送的所有请求信息,如 GET 或 POST 参数、请求头、用户信息等。

  2. template_name(必需):表示要渲染的模板文件的名称。这里可以是一个包含模板名称的字符串,也可以是一个模板名称列表。Django 将根据给定的模板名称来查找并渲染对应的模板文件。

  3. context(可选):一个字典,包含要传递给模板的上下文数据。字典中的键是模板中使用的变量名,值是要填充到模板中的数据。例如,如果有一个变量 name 需要在模板中显示,那么可以将 {"name": "John"} 作为 context 传递给 render 函数。

  4. content_type(可选):表示 HTTP 响应的内容类型。默认为 None,这将使用 Django 自动推断的内容类型。如果需要指定特定的内容类型(如 "text/html""application/json" 等),可以在这里设置。

  5. status(可选):表示 HTTP 响应的状态码。默认为 None,这将使用 200 表示成功。可以在这里指定其他状态码,如 404 表示页面未找到。

  6. 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') 

注:

这里有渲染两个页面,当用户进入添加页面,为提交数据不走主页面,提交完数据后返回主页面

二、 Django创建表关系

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.ForeignKeyon_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(可执行对象)

三、Django框架的请求生命周期流程图

1、路程图

 

posted @ 2023-07-28 21:10  凡人半睁眼  阅读(12)  评论(0编辑  收藏  举报