python - Django

django的生命周期:

发送http请求——>nginx服务器——>uwsgi服务器——>中间件——>路由——>视图——>ORM——>从ORM获取数据返回视图——>视图将数据传递给模板文件——>中间件——>uwsgi——>nginx——>生成响应内容

                                                                        ——Django 3 Web 应用开发实战

MVC 与 MTV模型 

 MVC 模型:

MVC 模式(Model–view–controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。

  • 模型(M)- 项目和数据库交涉的桥梁(ORM)。(models.py)
  • 视图(V)- 负责对用户的请求进行响应处理的东西。(views.py)
  • 控制器(C)- 负责转发请求,将请求转发到对应的处理函数中。(urls.py)

用户操作流程图:

MTV 模型:

Django 的 MTV 分别是指:

  • M 表示模型(models.py):项目可通过这个东西对数据库进行对应的操作(ORM)。
  • T 表示模板 (templates):负责存储HTML模板文件的,可调用指定的HTML文件对其进行修改后将其展示给用户。
  • V 表示视图(views.py):负责对请求进行处理并响应的

除了以上三层之外,还需要一个 URL 分发器,它的作用是将一个个 URL 的页面请求分发给不同的 views 处理,Views 再调用相应的 Model 和 Template,MTV 的响应模式如下所示:

windows下安装Django:

pycharm设置里的python解释器里搜索django,点击下载--下载不了就多试几个下载源

Django常用命令:

django-admin startproject name  # 在自定路径命令行下输入--创建一个名为name的项目
python manage.py startapp branch  # 在name项目下生成一个branch分支业务的app
    - 创建分支业务后需在项目下的settings.py-->INSTALLED_APPS 中加入你的应用名字进行注册安装

python manage.py makemigrations  # 将对models.py文件的设置内容迁移到migrations目录下,一个类对应要生成的一个表
python manage.py migrate   # 将migrations目录下新生成的文件在数据库中执行生成其设置字段信息的表
python manage.py runserver ip:端口  # 在name项目下输入--运行服务器--可指定ip端口--默认为127.0.0.1:8000


python manage.py createsuperuser  # 创建admin/页面登陆的管理员命令

Django-settings.py 设置文件介绍:

import os
from pathlib import Path

# 项目目录的绝对路径。主要通过os模块读取当前项目在计算机系统的具体路径。
# 创建项目时系统自动生成,一般不用改。
BASE_DIR = Path(__file__).resolve().parent.parent

# 密钥配置。用于重要数据的加密处理,提高项目的安全性。
# 密钥主要用于用户密码(Auth认证系统,将用户密码加密)、CSRF机制(表单提交,防窃取用户信息制造恶意请求)和会话Session(存放在Cookie中)等数据加密。
# 创建项目时系统自动生成随机值,一般不用改。
SECRET_KEY = 'django-insecure-70+4nbazxyc#v6l5axu=l29yf-(69)-8&jo4*wu=t^i2vt$_71'

# 调试模式,开发阶段为True,项目部署上线就要改为False,否则会泄露项目相关信息。
DEBUG = True

# 域名访问权限。设置可访问的域名,当DEBUG = True,ALLOWED_HOSTS = []空列表时,项目只允许localhost在浏览器访问。
# 当DEBUG = False,ALLOWED_HOSTS = 必填,否则程序无法启动。如果要所有域名都可以访问可设为ALLOWED_HOSTS = [‘*’]
ALLOWED_HOSTS = []

# App列表
INSTALLED_APPS = [
    'django.contrib.admin',  # 内置的后台管理系统
    'django.contrib.auth',  # 用户认证系统
    'django.contrib.contenttypes',  # 记录项目中所有model元数据(orm框架)
    'django.contrib.sessions',  # 会话功能,标识当前访问网站用户身份,记录相关用户信息
    'django.contrib.messages',  # 消息提示功能
    'django.contrib.staticfiles',  # 查找静态资源路径
    'index'  # 在项目中创建了app,就必须在这列表里添加app名称
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'MyDjango.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],  # 设置模板文件目录
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'MyDjango.wsgi.application'

# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

# LANGUAGE_CODE = 'en-us'#后台管理中的本地化语言,中文:zh-hans
LANGUAGE_CODE = 'zh-hans' # 设置后台管理窗口的显示语言:

# TIME_ZONE = 'UTC' 使用国际标准时间UTC:
# 所有时间在存入数据库前,必须转换成UTC时间。当使用时区时,Django存储在数据库中的所有日期时间信息都以UTC时区为准,在后台使用有时区的datetime,前台用户使用时,在网页上翻译成用户所在的时区。
# 后台向数据库输入日期时间时,日期时间应该带有时区信息,如果没有,输入数据库时会有警告
TIME_ZONE = 'Asia/Shanghai'  # 中国时间:Asia/Shanghai

USE_I18N = True

USE_L10N = True

USE_TZ = True

# # Static files (CSS, JavaScript, Images)。
# # 静态资源的路由地址:通过浏览器访问Django的静态资源。查找功能由App列表——INSTALLED_APPS——staticfiles
STATIC_URL = '/static/'
# # 设置根目录的静态资源文件夹static
STATICFILES_DIRS = [BASE_DIR / 'static',
                    # 设置App(index)的静态资源文件夹Mystatic
                    BASE_DIR / 'index/Mystatic', ]

# 资源部署:在服务器上部署项目,实现服务器和项目之间的映射。STATIC_ROOT主要收集整个项目的静态资源并存放在一个新的文件夹,由该文件夹与服务器之间构建映射关系。
# 项目部署上线DEBUG = False,Django不在提供静态文件代理服务,需要设置STATIC_ROOT。项目开发阶段就不需要设置,会自动提供。
# STATIC_ROOT=BASE_DIR/'AllStatic'

# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

实例:

1、django-admin startproject case    -- 创建case项目

2、在项目的根目录下创建一个 templates 目录,该目录用于存放这个项目共用的模板文件

 接下来要去告诉该django项目我们的模板目录所在的路径,往 case/settings.pyTEMPLATES 变量内的 DIRS 的列表内插入模板所在的路径

 BASE_DIR 变量存放的是当前项目所在的路径,加个"templates"为项目下的那个模板目录名

!!然后!!就可以往 templates 目录内加入自己想要的模板文件了。

3、python manage.py startapp index  -- 在命令行项目目录下输入,创建一个项目业务分支的app

 

 创建完成后要将该app加入到 case/settings.py 下的 INSTALLED_APPS 列表内,告诉该django项目,项目里有一个名为 index 的业务分支app

然后在 index 目录下新建一个 urls.py 文件 ,其主要用途在于,接收 项目下的 urls.py (case/case/urls.py) 发过来的路由寻址请求,因为用户寻址请求会发到项目下的 urls.py 内,在由该文件分发给其他分支业务项目(子项目)下的 urls.py 文件进行处理。

 (二)多 templates 模板配置

模板是Django里面的MTV框架模式的T部分,配置模板路径是在解析模板时,找到模板的所在的位置。

模板配置通常配置DIRS的属性即可。

创建两个templates文件夹,并在文件夹下分别创建两个html

一般根目录的templates存放共用模板文件

settings.py文件模板配置如下:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',       # 定义模板引擎,用于识别模板里面的变量和指令。内置模板引擎有Django Templates和jinja2.Jinja2,每个模板引擎都有自己的变量和指令语法。
       #注册根目录和index的templates文件夹
       'DIRS': [os.path.join(BASE_DIR, 'templates',
                              BASE_DIR, 'index/templates')],  # 设置模板文件路径,每个模板引擎都有自己的变量和指令语法。
        'APP_DIRS': True,   # 是否在App里查找模板文件
        'OPTIONS': {    # 用于填充在RequestContext的上下文(模板里面的变量和指令),一般情况下不做修改
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

先来介绍下快速响应请求的 HttpResponse 模块:

1、在 case/case/urls.py 内输入以下内容:

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),  # 创建项目时自带的后台登陆路由网址

    path('',include(("index.urls")),
    # 当访问首页时,将请求转发到index/urls.py中进行处理   
]

2、在 case/index/urls.py 内输入以下内容:

# -*- coding: UTF-8 -*-
from django.urls import path
from . import views

urlpatterns = [
    path('',views.index),  # 当访问的地址是 '' 网页首页时,就会调用views.index函数来响应本次请求
]

3、在case/index/views.py 内新建一个 index 函数,用来处理响应本次请求:

from django.http import HttpResponse

def index(request):
    return HttpResponse("hello ")
    # HttpResponse 方法可将括号内的信息直接响应给用户

4、然后在项目根目录的命令行内输入:python manage.py runserver ip:端口  # 运行服务器--可指定ip端口--默认为127.0.0.1:8000

!!! 小提示 !!! 项目运行后,项目中如果代码有改动,服务器会自动监测代码的改动并自动重新载入,所以如果你已经启动了服务器则不需手动重启。

5、然后访问自己指定的网址,默认为 http://127.0.0.1:8000/

就可以获得响应数据了。

接下来,来介绍下 模板文件 的使用案例

1、在 case/templates/ 下新建一个 index.html 模板文件 

内容为:

 双引号为声明一个变量的作用,在此处声明一个 hello 变量,后面可以在view.py内传递给该变量,值 。

2、然后在 case/index/views.py 内新建一个函数,内容为:

from django.shortcuts import render
''' 该模块作用为,将指定的字典内容(字典内的键必须为模板内变量的名字,值则为要给其赋的值)
 填充到指定的模板内对应的变量名上。。然后将其响应给用户。。'''


def index(request):
    context = {}  # 创建一个字典文件,用于存放模板文件内变量的名称,和要给其赋的值
    context['hello'] = '我是 index.html 模板的变量的值'
    # 字典中元素的键值 hello 对应了模板中的变量 {{ hello }}。
    return render(request, 'index.html', context)

3、修改 case/case/urls.py 文件内容:

 该行代码用于转发此行代码上没有被处理的路由寻址请求给指定的分支业务(子项目)进行处理。

4、修改case/index/urls.py 文件内容:

当用户访问 index/ 时,因为case/case/urls.py 文件没有该项网址,所以转发请求将本次寻址发给 case/index/urls.py 处理,而case/index/urls.py 有其要的路由地址,然后就调用其的视图函数进行返回响应。

5、服务器开启的情况下访问自己指定的网址,默认为 http://127.0.0.1:8000/index/

就可以获得响应数据了。

这样我们就完成了使用模板来输出数据,从而实现数据与视图分离。介绍具体的模板中常用的语法规则见  django-html 模板语法规则介绍

模板继承

模板可以用继承的方式来实现复用,减少冗余内容。

{% block 名称 %} 
在父模板内插入 block -- endblock 的作用是:
父模板开放可以让继承的子模版自由修改的部分。
在子模版继承父模板后,可以在子模版内插入 {% block 名称 %} 标签,
然后可以在 {% block 名称 %} 标签和 {% endblock 名称 %} 标签,内自由添加自己的内容
{% endblock 名称 %}
{% extends "父模板路径及名称"%}   -- 通过此标签可以让子模版继承指定的父模板

1、在模板文件内创建一个父模板文件

 

插入代码到body标签内:

<body>
    <p>我是父模块的代码</p>
    {% block mainbody %}  -- 开放一个区域让子模版可以自由操纵
       <p>父模块开放让子模块可以修改的部分</p>
    {% endblock %}  -- 结束开放的区域
</body>

2、在子模版中继承父模板:

{%extends "father.html" %}   -- 继承 father.html 这个父模板
{% block mainbody %}   -- 调用父模板内的 block 标签进行替换父模板提供可替换的部分
<p> block 以下是父模块开放让子模块可以自由修改的部分</p>
<h6> 在 endblock 结束可修改的代码块之前,都可以在里面添加自己想要的内容 </h6>
{% endblock %}  -- 结束可添加/替换部分

3、在设置好路由(urls.py)和 views.py 后访问页面:

静态文件配置:static

1、在项目的根目录下创建一个 statics 目录文件

2、在 settings 文件的最下方配置添加以下配置:

STATIC_URL = '/static/' # 别名 
STATICFILES_DIRS = [ 
    os.path.join(BASE_DIR, "statics"),   # 告诉 django 我们的静态目录在哪
]

 3、在 statics 目录下创建 css 目录,js 目录,images 目录,plugins 目录, 分别放 css文件,js文件,图片,插件。

然后在images目录下放入一张照片,方便一会使用

4、在使用的模板文件内插入以下代码:

{% load static %}    -- 使用静态文件时,需要先在模板内载入静态文件,
<img src="{% static 'images/picture.png' %}" alt="the a picture">

 5、修改 case/index/views.py 内容:

from django.shortcuts import render

def index(request):
    return render(request, "index.html")

6、服务器开启的情况下访问自己指定的网址,默认为 http://127.0.0.1:8000/index/

 就可以显示出插入的图片了。

多静态和媒体资源的配置

先建4个文件夹,放一些图片进去,名字如图

这里有两个static文件,系统默认的是index下的static文件

静态资源文件

settings.py文件中告诉django静态文件在哪:

# # Static files (CSS, JavaScript, Images)。
# # 静态资源的路由地址:通过浏览器访问Django的静态资源。查找功能由App列表——INSTALLED_APPS——staticfiles
STATIC_URL = '/static/'
# # 设置根目录的静态资源文件夹static
STATICFILES_DIRS = [BASE_DIR / 'static',
                    # 设置App(index)的静态资源文件夹Mystatic
                    BASE_DIR / 'index/Mystatic', ]

# 资源部署:在服务器上部署项目,实现服务器和项目之间的映射。STATIC_ROOT主要收集整个项目的静态资源并存放在一个新的文件夹,由该文件夹与服务器之间构建映射关系。
# 项目部署上线DEBUG = False,Django不在提供静态文件代理服务,需要设置STATIC_ROOT。项目开发阶段就不需要设置,会自动提供。
# STATIC_ROOT=BASE_DIR/'AllStatic'

STATIC_URL名字(static)就是指浏览器网址地址,所以和网址名字.../static/....对应。

打开浏览器输入网址,就可以看到图片了。

媒体资源文件

先配置媒体资源属性,然后注册到Django里。

settings.py文件下添加以下代码

# 媒体资源配置
# 设置媒体路由地址信息
MeDIA_URL='/media/'
# 获取media文件夹的完整路径信息
MEDIA_ROOT=BASE_DIR/'media'

django -- 模型(ORM):

ORM 为 python 代码和数据库之间的桥梁,python 代码通过 ORM 将代码转化为 SQL 语句,再由pymysql传送到数据库内进行执行SQL语句。

ORM对应数据库的关系对应表:

Django 提供了对 PostgreSQL、MySQL、SQLite、Oracle。数据库的支持。

django.db.backends.postgresql

django.db.backends.mysql

django.db.backends.sqlite3

django.db.backends.oracle

1、接下来主要介绍 MySQL 数据库的设置。先下载mysql驱动

下载 pymysql 模块,该模块会将SQL语句通过 pymysql 传送到数据库服务端,在数据库中执行 SQL 语句并将结果返回。

2、因为ORM 无法操作到数据库级别,只能操作到数据表,所以先创建一个数据库:

create database testmysql default charset=utf8; # 防止编码问题,指定为 utf8

创建了一个名为 testmysql 的数据库后,就将该数据库的信息填入到项目里的 settings.py 文件的 DATABASES 中,告诉django项目我们要连接的数据库。

3、接下来,告诉 Django 使用 pymysql 模块连接 mysql 数据库: 

# 在与 settings.py 同级目录下的 __init__.py 中引入模块和进行配置
import pymysql
pymysql.install_as_MySQLdb()

4、django规定,模型必须在分支业务(子项目)内才能使用。所以到分支业务app case/index/models.py 内进行操作:

from django.db import models

class Test(models.Model):  # 创建一个名为 Test 的表,
    name = models.CharField(max_length=20)  # 往这个表内键入一个 name 字段,最大长度为 20 个字符
    # 注意: django中创建字段,默认会创建一个id字段

5、然后在项目根目录的命令行下输入:

将对models.py文件的设置内容迁移到migrations目录下,一个类对应要生成的一个表

 将migrations目录下新生成的文件在数据库中执行生成其设置的字段信息的表

6、然后在 case/index/ 新建一个 sql.py 文件。该文件主要用于处理sql请求。内容为:

# -*- coding: UTF-8 -*-
from django.http import HttpResponse  # 导入该模块告诉用户说数据添加成功
from .models import Test  # 导入数据表对象

# 数据库操作
def testdb(request):
    test1 = Test(name='数据一')  # 往 Test 数据表中的 name 字段中插入信息
    test1.save()  # 执行sql语句
    return HttpResponse("<p>数据添加成功!</p>")  # 告诉用户数据添加成功

7、修改 case/index/urls.py 内的代码:

# -*- coding: UTF-8 -*-
from django.urls import path
from . import views,sql

urlpatterns = [
    path('index/',views.index),
    path('testdb/',sql.testdb),   # 当有人访问该网址时就调用sql内的testdb返回响应
]

8、在开启服务器的情况下访问 http://127.0.0.1/testdb/

到mysql中查看:testmysql下的index_test表下的字段内容信息。

django中还可对数据库的数据进行增删改查等操作,具体见django数据库操作介绍大全

2、多个数据库的连接方式

# 数据库配置
DATABASES = {
    # 第一个数据库,default是默认的数据库不可删除,可留空{}
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django_db',
        'USER':'root',
        'PASSWORD':'1234',
        'HOST':'127.0.0.1',
        'PORT':'3306',
    },
    # 第二个数据库
    'MyDjango': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mydjango_db',
        'USER': 'root',
        'PASSWORD': '1234',
        'HOST': '127.0.0.1',
        'PORT': '3306',
    },
    # 第三个数据库
    'MySqlite3': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR/'django_db',  # BASE_DIR,数据库在项目路径下生成
    },
}

 使用配置文件动态连接数据库

在MyDjango目录下创建配置文件my.cnf,写入MySOL数据库的连接信息。

[client]客户端设置,即客户端默认的连接参数
[client]
database=django_db
user=root
password=1234
host=127.0.0.1
port=3306

在settings.py写配置信息。default--OPTIONS--read_default_file中设置配置文件my.cnf的路径,Django读取配置文件my.cnf的数据库连接信息,连接数据库。

# 数据库配置
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'OPTIONS': {'read_default_file':str(BASE_DIR / 'my.cnf')},
    },

}

在MyDjango的urls.py中设置路由index,路由的视图函数indexView()在项目应用index的view.py中定义。

from django.urls import path
from django.contrib import admin
from index.views import index  # 导入项目应用index
# 项目的urls文件
urlpatterns = [
    path('admin/', admin.site.urls),
    path('',index,name='index'),
]

在项目应用index的view.py中定义路由index的视图函数indexView()。

 indexView()读取并输出Django内置函数ContentType的数据,请求响应的内容为templates文件夹的app_index.html

from django.shortcuts import render
from django.contrib.contenttypes.models import ContentType
def indexView(request):
    c=ContentType.objects.values_list().all()
    print(c)
    return render(request,'app_index.html')

app_index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello</title>
</head>
<body>
    <span>Hello World!!</span>
</body>
</html>

python manage.py migrate

 python manage.py runserver 8001

5、通过SSH隧道远程连接MySQL

 先在SSH栏设置,然后在常规栏设置。

 pip install sshtunnel

 下载sshtunnel,通过SSH方式连接到目标服务器,生成服务器的SSH连接对象,在settings.py文件的DATABASES中设置数据库连接

# ssh_:实现SSH连接目标服务器,在sshtunnel模块中使用

# 数据库服务器的ip地址或主机名
ssh_host = "192.168.xx.xx"
# 数据库服务器的SSH连接端口号,一般都是22,必须是数字
ssh_port = 22
# 数据库服务器的用户名
ssh_user = "root"
# 数据库服务器的用户密码
ssh_password = "1234"

# mysql_:在目标服务器基础上连接MySQL数据库,在配置属性DATABASES和sshtunnel模块中均被使用

# 数据库服务器的mysql的主机名或ip地址
mysql_host = "localhost"
# 数据库服务器的mysql的端口,默认为3306,必须是数字
mysql_port = 6603
# 数据库服务器的mysql的用户名
mysql_user = "root"
# 数据库服务器的mysql的密码
mysql_password = "1234"
# 数据库服务器的mysql的数据库名
mysql_db = "mydjango"

# 分别定义服务器的SSH连接信息和数据库的连接信息
# 定义服务器的SSH连接函数get_ssh(),使用sshtunnel模块的open_tunnel函数实现,并设置相应的函数参数
from sshtunnel import open_tunnel
def get_ssh():
    server = open_tunnel(
        (ssh_host, ssh_port),
        ssh_username=ssh_user,
        ssh_password=ssh_password,
        # 绑定服务器的MySQL数据库
        remote_bind_address=(mysql_host, mysql_port))#remote_bind_address是绑定服务器的MySQL数据库
    # ssh通道服务启动
    server.start()
    return str(server.local_bind_port)
# 在DATABASES的PORT中调用get_ssh(),Django就会连接到服务器的MySQL数据库
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': mysql_db,
        'USER': mysql_user,
        'PASSWORD': mysql_password,
        'HOST': mysql_host,
        'PORT': get_ssh(),
    }
}

 django表单:

HTML表单是网站交互性的经典方式。 本段将介绍如何用Django对用户提交的表单数据进行处理。

HTTP协议以"请求-回复"的方式工作。客户发送请求时,可以在请求中附加数据。服务器通过解析请求,就可以获得客户传来的数据,并根据URL来提供特定的服务。

  • GET 方法

在 case/index/ 新建一个 form.py 文件。用于接收用户提交的表单的请求和处理。并输入代码:

# -*- coding: UTF-8 -*-
from django.http import HttpResponse
from django.shortcuts import render


# 表单
def search_form(request):  # 将表单模板响应给用户
    return render(request, 'search_form.html')


# 接收请求数据
def search(request):
    request.encoding = 'utf-8'  # 将请求的编码改为 utf-8

    '''判断用户是否提交了表单模板,因为表单模板的name为 q 所以只需要判断 q 这个表单在不在用户提交的请求里面,
    和获取 q 这个信息判断其是不是为空,当其为空,或不在请求里,就返回提交空的表单,否则就将其搜索的内容响应给他'''
    if 'q' in request.GET and request.GET['q']:
        message = '你搜索的内容为: ' + request.GET['q']
    else:
        message = '你提交了空表单'
    return HttpResponse(message)

在模板目录 templates 中添加 search_form.html 表单:内容为:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title> - </title>
</head>
<body>
    <form action="/search/" method="get">  -- action=当提交表单时将表单提交给 网址/search/ 路由解析,method=请求方式,有 get post 等等
        <input type="text" name="q">  -- 创建一个文本输入框,它的名字为 q ,可用这个名字来获取其输入的内容
        <input type="submit" value="搜索">  -- 创建一个提交框,他的按钮名字为 搜索,当点击搜索时会将表单进行提交
    </form>
</body>
</html>

往 case/index/urls.py 路由内加入路由网址:

from django.urls import path
from . import search

urlpatterns = [
    path('search-form/',search.search_form),  # 访问这个路由时,将表单响应回去
    path('search/',search.search),  # 当表单进行提交时,会寻址这个路由,将调用这个路由设置的方法响应回去
]

访问地址 http://127.0.0.1:8000/search-form/ 并搜索,结果如下所示:

  • POST 方法

先修改 case/index/search.py 文件内容:

from django.shortcuts import render
from django.views.decorators import csrf   # 用于防伪判断的模块

# 接收POST请求数据
def search_post(request):
    ctx = {}  # 创建一个字典用于存放要传给模板内变量的值
    if request.POST:  # 判断,当请求不为空时,
        ctx['rlt'] = request.POST['q']  # 插入一个key为rlt(模板内的变量名),值为,表单q输入的内容
    return render(request, "post.html", ctx)  # 将值传递到post.html模板内的变量中,响应给用户

在模板目录内创建一个 post.html 表单文件:内容为:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title> - </title>
</head>
<body>
    <form action="/search-post/" method="post">  -- action=当提交表单时将表单提交给 网址/search-post/ 路由解析,method=请求方式,有 get post 等等
        {% csrf_token %}    -- 这是 Django 提供的防止伪装提交请求的功能。POST 方法提交的表格,必须有此标签。
        <input type="text" name="q">  -- 创建一个文本输入框,它的名字为 q ,可用这个名字来获取其输入的内容
        <input type="submit" value="搜索">  -- 创建一个提交框,他的按钮名字为 搜索,当点击搜索时会将表单进行提交
    </form>

    <p>{{ rlt }}</p>  -- 添加一个标签变量,用于获取post请求后将请求内容响应给该标签
</body>
</html>

表格中 {% csrf_token %} 标签。csrf 全称是 Cross Site Request Forgery。这是 Django 提供的防止伪装提交请求的功能。POST 方法提交的表格,必须有此标签。

往 case/index/urls.py 路由内加入路由网址:

from django.urls import path
from . import search

urlpatterns = [
    path('search-post/',search.search_post),  # 访问这个路由时,将调用设定的方法进行返回
]

访问地址 http://127.0.0.1:8000/search-form/ 并搜索,结果如下所示:

django视图:

一个视图函数,简称视图,是一个简单的 Python 函数,它接受 Web 请求并且返回 Web 响应。

响应可以是一个 HTML 页面、一个 404 错误页面、重定向页面、XML 文档、或者一张图片...

无论视图本身包含什么逻辑,都要返回响应。代码写在哪里都可以,只要在 Python 目录下面,一般放在项目的 views.py 文件中。

每个视图函数都负责返回一个 HttpResponse 对象,对象中包含生成的响应。

视图层中有两个重要的对象:请求对象(request)与响应对象(HttpResponse)

请求对象: HttpRequest 对象(简称 request 对象)

以下介绍几个常用的 request 属性。

  • GET

数据类型是 QueryDict,一个类似于字典的对象,包含 HTTP GET 的所有参数。

取值格式:对象.方法。

get():返回字符串,如果该键对应有多个值,取出该键的最后一个值。

实例:

    path('get/',views.get),  # 将这句话加入到 case/index/urls.py 路由处理文件中

 case/index/views.py 代码为:

from django.http import HttpResponse

def get(request):
    name = request.GET.get('name')  # 获取 get 请求中名为 name 的值
    return HttpResponse('name:{}'.format(name))  # 并将其响应给用户

访问 get/ 请求时可在其后面加入?并在?后面传入你要传递的get参数。 

  • post

数据类型是 QueryDict,一个类似于字典的对象,包含 HTTP POST 的所有参数。

常用于 form 表单,form 表单里的标签 name 属性对应参数的键,value 属性对应参数的值。

取值格式: 对象.方法。

get():返回字符串,如果该键对应有多个值,取出该键的最后一个值。

实例:

修改post.html模板文件,内容为:


    <form action="/form-post/" method="post">  -- action=当提交表单时将表单提交给 /form -post/ 路由解析,method=请求方式,有 get post 等等
        {% csrf_token %}    -- 这是 Django 提供的防止伪装提交请求的功能。POST 方法提交的表格,必须有此标签。
        <input type="text" name="q">  -- 创建一个文本输入框,它的名字为 q ,可用这个名字来获取其输入的内容
        <input type="submit" value="搜索">  -- 创建一个提交框,他的按钮名字为 搜索,当点击搜索时会将表单进行提交
    </form>

    <p>{{ rlt }}</p>  -- 添加一个标签变量,用于获取post请求后将请求内容响应给该标签

在 case/index/urls.py 内插入两个路由:

    path('post/',views.post),  # 路由寻址,调用指定函数方法,处理响应请求
    path('form-post/',views.form_post),

将以下代码插入到 case/index/views.py 中:

def post(request):
    return render(request,'post.html')  # 触发该函数将会将指定的模板文件响应给用户

def form_post(request):
    name = request.POST.get('q')  # 触发该函数将会,获取post内q的值,再将 q 的值传到post.html的模板中的指定变量中,
    return render(request,'post.html',{'rlt':name})  # 然后将其响应给用户
  • path

调用该方法会获取请求的寻址信息,数据类型:字符串。

def index(request):
    name = request.path
    print(name)   # 访问的是 http://127.0.0.1/index/ 路由。获取的path则是 /index/
    return HttpResponse("---")
  • method

获取当前请求使用的方式,GET , POST 等。

def index(request):
    name = request.method
    print(name)  # 获取本次请求使用的是什么请求方式(GET , POST),结果为字符串形式,大写的。
    return HttpResponse("ok")

5、其他

request 中还有很多好用的方法,比如:
    print(request.COOKIES)  # 获取本次请求的 cookies 信息
    等等等等。详情见 https://www.runoob.com/django/django-form.html 中的 django表单

响应对象:Response

响应对象主要有三种形式:HttpResponse()、render()、redirect()。

HttpResponse(): 将文本内容响应给用户。如果字符串里含有 html 标签,也可以渲染。

 return HttpResponse("<a href='https://www.baidu.com/'>点击跳转到百度页面</a>")

render(): 该方法一共有三个参数:

第一个参数为request(携带用户请求信息进行响应)。

第二个参数为字符串格式的,指定的模板文件(*.html)。

第三个参数为字典(可选参数,向模板文件传递参数:键为模板文件的参数名,值为要给其赋的值)。

 return render(request,"index.html",{"name":'姓名'})  
''' 将name的值传到 index.html 模板中进行填充。然后将填充后的模板文件响应给用户。'''

redirect():重定向,跳转新页面。参数为字符串,字符串中填写 路由寻址路径 。一般用于 form 表单提交后,跳转到新页面。如:

    return redirect("/index/") 
''' 将会将 /index/ 响应给用户,然后相当于用户访问了,
http://127.0.0.1/index/ 一样,然后继续继续路由寻址操作'''

重定向:HTTP协议重定向,也称为网页跳转,就是在浏览器访问某个网页的时候,这个网页不提供响应内容,而是自动跳转到其他网址,由其他网址来生成响应内容。
django的网页重定向有两种方式:

  • 第一种方式是路由重定向;如:
  • from django.urls import path
    from . import views
    from django.views.generic import RedirectView
    
    urlpatterns = [
        # 设置路由跳转
        path('tur/', RedirectView.as_view(url='/'), name='tur'),
    ''' 当寻址 tur/ 时,将进行路由重定向,跳转到 '/' 首页 '''
    ]
    
  • 第二种方式是自定义视图的重定向。

两种重定向方式各有千秋,前者是使用django内置的视图类RedirectView实现的, 默认支持HTTP的GET请求;后者是在自定义视图的响应状态设置重定向,能让开发者实现多方面的开发需求。

django路由:

路由简单的来说就是根据用户请求的 URL 链接来判断对应的处理程序、

Django 路由在 urls.py 里配置,urls.py 中的每一条配置对应相应的处理方法。

django路由有两种使用方式,分别是 path() 和 re_path()

path:用于普通路径,不需要自己手动添加正则首位限制符号,底层已经添加。

两个使用的参数完全一样,就 path 不使用正则匹配,而re_path 使用正则匹配而已:

from django.urls import path,re_path
path('index/',views.index,name='index'),
''' 第一个参数:index/ : 为这行代码的路由地址。可以通过寻址这个路由地址从而完成指定的响应处理
    第二个参数:views.index : 当路由寻址为第一个参数时,就会调用该模块内的方法进行处理本次请求,然后进行响应本次请求。
    第三个参数:name='index' : 给这个路由地址起一个名字。当有人调用这个名字时,就会把这个路由地址返回给对方'''

re_path:用于正则路径,需要自己手动添加正则首位限制符号。

正则路径中分为,无名分组和有名分组。
无名分组:
    re_path("^index/([0-9]{4})/$", views.index), 
    ''' 处理该请求时会传递给 views.index 两个参数,按照位置顺序传递参数。
    第一个是request(本次的请求信息),第二个是自定义的 4个正则数字。
    无名分组中 views.index 内的形参可以随意定义名字。'''

有名分组:
    re_path("^index/(?P<ye>[0-9]{4})/(?P<no>[0-9]{2})/$", views.index),
    ''' 语法为:(/?P<名字>[正则规则]{限制个数})/
    处理该请求时会传递给 views.index 三个参数,除了第一个是(request)外 其它的按照组名传递参数。
    相当于使用一个函数,给函数的指定的形参传递指定的参数一样、
    有名分组中 views.index 内的形参可以需要和路由内定义的分组名字一样。'''

路由分发:include

存在问题:Django 项目里多个app目录共用一个 urls 容易造成混淆,后期维护也不方便。

解决:使用路由分发(include),让每个app目录都单独拥有自己的 urls。

步骤:

  • 1、在每个 app 目录里都创建一个 urls.py 文件。
  • 2、在项目名称目录下的 urls 文件里,统一将路径分发给各个 app 目录。
from django.contrib import admin
from django.urls import path,include # 从 django.urls 引入 include
urlpatterns = [
    path('admin/', admin.site.urls),   # 管理员页面
    path("app01/", include("app01.urls")),  
''' 当页面访问 http://127.0.0.1/app01/index/ 时,进行路由寻址处理,
先在这寻址到 app01/ 然后将剩下的 index/ 传递到 app01 内指定的包含的 urls 中'''
    path("app02/", include("app02.urls")),  # 根上诉一样
]

在各自 app 目录下,写自己的 urls.py 文件,进行路径寻址。

from django.urls import path,re_path 
from app01 import views # 从自己的 app 目录引入 views 
urlpatterns = [ 
    path('index/', views.index, ), # 路由处理在这寻址的 index/路由
    re_path(r'^login/(?P<m>[0-9]{2})/$', views.login, ), # 路由处理在这寻址的 路由
] 

反向解析:reverse()给其传递一个给路由定义的name,然后让其去查找并返回这个路由

如:urls.py 中插

path("login-admin/", views.login, name="login")  
''' name为给这个路由起一个名字,可以通过这个名字去获取这个路由的地址'''

在 views.py 中,从 django.urls 中引入 reverse,利用 reverse("路由别名") 反向解析:

return redirect(reverse("login"))
''' 结果为,reverse去项目全局查找路由的 name 为 login 的路由。
    然后将该路由返回,redirect('login-admin/') 然后将该路径响应给用户进行重定向操作。
'''

也可在模板中使用反向解析,如:

<form action="{% url 'login' %}" method="post">  
''' 当提交表单时,会将表单响应给 {% url 'login' %} 这个路由地址。
    {% url 'login' %} 这个标签会去全局查找路由别名为 login 的路由(login-admin),
然后将其返回。这句话相当于:<form action="login-admin" method="post">  
'''

re_path() 的无名分组在 reverse 中的使用方法:

re_path(r"^login/([0-9]{2})/$", views.login, name="login")
 # name为给这个路由起一个名字,可以通过这个名字去获取这个路由的地址

在 views.py 中,从 django.urls 中引入 reverse,利用

 reverse("路由别名",args=(符合正则匹配的参数,)) 反向解析。

return redirect(reverse("login",args=(10,)))  
# reverse返回的相当于 login/10/ 然后在通过redirect重定向给用户

也可在模板中使用反向解析,如:

<form action="{% url 'login' 10 %}" method="post"> 
''' 当提交表单时,会将表单响应给 {% url 'login' 10 %} 这个路由地址。
    {% url 'login' 10 %} 这个标签会去全局查找路由别名为 login 的路由(login),
    然后将其返回。
    这句话相当于:<form action="login/10/" method="post">  
'''

re_path() 有名分组在 reverse 中的使用方法:

re_path(r"^login/(?P<year>[0-9]{4})/$", views.login, name="login")
    ''' "^login/(?P<year>[0-9]{4})/$" 为,给login路由扩展一个分组,
    它的名字为 year 她可接受 4个随机数值。
    name为给这个路由起一个名字,可以通过这个名字去获取这个路由的地址
    '''

在 views.py 中,从 django.urls 中引入 reverse,

利用 reverse("路由别名",args=(符合正则匹配的参数,)) 反向解析。

return redirect(reverse("login",kwargs={"year":3333}))  
''' reverse返回的相当于 login/3333/ (year是要给这个路由内定义的year参数传值,
相当于使用函数给指定形参传值一样)然后在通过redirect重定向给用户'''

也可在模板中使用反向解析,如:

<form action="{% url 'login' year=3333 %}" method="post">
''' 当提交表单时,会将表单响应给 {% url 'login' year=3333 %} 这个路由地址。
    {% url 'login' year=3333 %} 这个标签会去全局查找路由别名为 login 的路由(login),
    然后将其返回。
    这句话相当于:<form action="login/3333/" method="post">  
'''

命名空间  :  Namespace

存在问题:路由别名 name 没有作用域,Django 在反向解析 URL 时,会在项目全局顺序搜索,当查找到第一个路由别名 name 指定 URL 时,立即返回。当在不同的 app 目录下的urls 中定义相同的路由别名 name 时,可能会导致 URL 反向解析错误。

解决:使用命名空间

path('app01/',include(("app01.urls","app01"),namespace='app01')),
''' 当访问app01/下的内容时,就将请求转发到app01.urls内,
    app01 为,告诉这个路由,他要将请求转发到 app01 这个分支业务内
'''

在 views.py 中使用名称空间,语法格式如下:

return redirect(reverse("app01:login"))
''' reverse("app01:login") 返回的是,app01这个子项目下,路由别名为 login 的路由地址 '''

在 templates 模板的 HTML 文件中使用名称空间,语法格式如下:

<form action="{% url 'app01:login' %}" method="post">
''' {% url 'app01:login' %} 表单会提交到 app01 这个子项目下名为 login 的路由处理,
    相当于:<form action="app01:login" method="post">
'''

Django Admin 管理工具

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),  # 当寻址该路由时,会将django强大的后台工具响应给用户
]

启动开发服务器,然后在浏览器中访问 http://127.0.0.1:8000/admin/,得到如下界面:

你可以通过命令 python manage.py createsuperuser 来创建超级用户,如下所示:

 之后输入用户名密码登录,界面如下:

 为了让 admin 界面管理某个数据模型,我们需要先注册该数据模型到 admin。将之前创建的数据表(case/index/models.py)模型 (Test) 注册到 case/index/admin.py 中:

from django.contrib import admin
from .models import Test

admin.site.register(Test)  
# 将 Test 表,注册到admin后台管理界面中,让管理员可以更直观的控制这个表

这样就可以通过管理界面管理 Test 这个表了。

复杂模型

管理页面的功能强大,完全有能力处理更加复杂的数据模型。

先在 index/models.py 中增加一个更复杂的数据模型:

from django.db import models
 
# Create your models here.
class Test(models.Model):
    name = models.CharField(max_length=20)
 
class Contact(models.Model):
    name   = models.CharField(max_length=200)
    age    = models.IntegerField(default=0)
    email  = models.EmailField()
    def __unicode__(self):
        return self.name
 
class Tag(models.Model):
    contact = models.ForeignKey(Contact, on_delete=models.CASCADE,)
    name    = models.CharField(max_length=50)
    def __unicode__(self):
        return self.name

将创建的数据表(case/index/models.py)模型注册到 case/index/admin.py 中:

from django.contrib import admin
from TestModel.models import Test,Contact,Tag
 
# Register your models here.
admin.site.register([Test, Contact, Tag])

然后将对数据表的操作告诉django一下,并创建对应的表:

python manage.py makemigrations  
python manage.py migrate  

这样就可以通过管理页面管理设定的表了。

自定义表单

我们可以自定义管理页面,来取代默认的页面。比如修改数据添加页面:

修改 index/admin.py:

from django.contrib import admin
from TestModel.models import Test,Contact,Tag
 
# Register your models here.
class ContactAdmin(admin.ModelAdmin):
    fields = ('name', 'email')  # 定义该类只显示,name和email字段
 
admin.site.register(Contact, ContactAdmin)  # 将contact表注册到admin中,并且只显示name和email字段
admin.site.register([Test, Tag]) # 将Test和tag表注册到admin中

显示效果如下:

我们还可以将输入栏分块,每个栏也可以定义自己的格式。修改 index/admin.py为:

from django.contrib import admin
from TestModel.models import Test,Contact,Tag
 
# Register your models here.
class ContactAdmin(admin.ModelAdmin):
    fieldsets = (
        ['Main',{
            'fields':('name','email'),
        }],
        ['Advance',{
            'classes': ('collapse',), # CSS
            'fields': ('age',),
        }]
    )
 
admin.site.register(Contact, ContactAdmin)
admin.site.register([Test, Tag])

上面的栏目分为了 Main 和 Advance 两部分。classes 说明它所在的部分的 CSS 格式。这里让 Advance 部分隐藏:

Advance 部分旁边有一个 Show 按钮,用于展开,展开后可点击 Hide 将其隐藏,如下图所示:

内联(Inline)显示

上面的 Contact 是 Tag 的外部键,所以有外部参考的关系。

而在默认的页面显示中,将两者分离开来,无法体现出两者的从属关系。我们可以使用内联显示,让 Tag 附加在 Contact 的编辑页面上显示。

修改 index/admin.py:

from django.contrib import admin
from TestModel.models import Test,Contact,Tag
 
# Register your models here.
class TagInline(admin.TabularInline):
    model = Tag
 
class ContactAdmin(admin.ModelAdmin):
    inlines = [TagInline]  # Inline
    fieldsets = (
        ['Main',{
            'fields':('name','email'),
        }],
        ['Advance',{
            'classes': ('collapse',),
            'fields': ('age',),
        }]
 
    )
 
admin.site.register(Contact, ContactAdmin)
admin.site.register([Test])

显示效果如下:


列表页的显示

在 Contact 输入数条记录后,Contact 的列表页看起来如下:

我们也可以自定义该页面的显示,比如在列表中显示更多的栏目,只需要在 ContactAdmin 中增加 list_display 属性:

from django.contrib import admin
from TestModel.models import Test,Contact,Tag
 
# Register your models here.
class TagInline(admin.TabularInline):
    model = Tag
 
class ContactAdmin(admin.ModelAdmin):
    list_display = ('name','age', 'email') # list
    inlines = [TagInline]  # Inline
    fieldsets = (
        ['Main',{
            'fields':('name','email'),
        }],
        ['Advance',{
            'classes': ('collapse',),
            'fields': ('age',),
        }]
 
    )
 
admin.site.register(Contact, ContactAdmin)
admin.site.register([Test])

刷新页面显示效果如下:

搜索功能在管理大量记录时非常有,我们可以使用 search_fields 为该列表页增加搜索栏:

from django.contrib import admin
from TestModel.models import Test,Contact,Tag
 
# Register your models here.
class TagInline(admin.TabularInline):
    model = Tag
 
class ContactAdmin(admin.ModelAdmin):
    list_display = ('name','age', 'email') # list
    search_fields = ('name',)
    inlines = [TagInline]  # Inline
    fieldsets = (
        ['Main',{
            'fields':('name','email'),
        }],
        ['Advance',{
            'classes': ('collapse',),
            'fields': ('age',),
        }]
 
    )
 
admin.site.register(Contact, ContactAdmin)
admin.site.register([Test])

在本实例中我们搜索了 name 为 runoob 的记录,显示结果如下:

设置管理页面的头信息等:添加下面代码到 index/admin.py 中:

admin.site.site_title = '待办事项标签页'  # 将网页的标题设为指定内容
admin.site.site_header = '待办事项系统'  # 将网页的头设为指定内容
admin.site.index_title = '内容管理'  # 将网页首页的标题设为指定内容

访问 http://127.0.0.1:8000/admin/  :

改变str返回值

models.py里重写方法

class BookInfo(models.Model):#继承于models模块里的Model类
    #图书名称,CharField说明是一个字符串,max_length指定字符串的最大长度
    btitle=models.CharField(max_length=20)
    #出版日期,DateField说明是一个日期模型
    bpub_date=models.DateField()

    def __str__(self):
        # 返回书名
        return self.btitle
右侧可增加BOOK INFO

​​

Django Form 组件

Django Form 组件用于对页面进行初始化,生成 HTML 标签,此外还可以对用户提交的数据进行校验(显示错误信息)。

报错信息显示顺序:

  • 先显示字段属性中的错误信息,然后再显示局部钩子的错误信息。
  • 若显示了字段属性的错误信息,就不会显示局部钩子的错误信息。
  • 若有全局钩子,则全局钩子是等所有的数据都校验完,才开始进行校验,并且全局钩子的错误信息一定会显示。

使用 Form 组件,需要先导入 forms:

from django import forms

首先,我们先在子项目下创建一个:My_forms.py 文件,该文件用于对输入的数据进行限制设置以及返回的错误信息设置。

# -*- coding: UTF-8 -*-
from django import forms  # 该模块可以对表单进行设置
from django.core.exceptions import ValidationError
from . import models

class EmpForm(forms.Form):
    # 限制最小输入长度为4个字符,标签的名字为:姓名。设置当长度低于4个字符时,提示你太短了,和字段为空时的提示信息。
    name = forms.CharField(min_length=4, label="姓名", error_messages={"min_length": "你太短了", "required": "该字段不能为空!"})

    # 创建一个整数的输入标签,标签名:年龄
    age = forms.IntegerField(label="年龄")

    # 创建一个浮点数标签,标签名:工资
    salary = forms.DecimalField(label="工资")

字段属性:

  • label:输入框前面的文本信息。
  • error_message:自定义显示的错误信息,属性值是字典, 其中 required 为设置不能为空时显示的错误信息的 key。

然后设置以下模板文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<h3>添加员工</h3>
<!--{#2、通过form对象的as_p方法实现,将在My_Forms中对标签的设置信息填充到 as_p 变量中#}-->
<!--<form action="" method="post" novalidate>-->
<!--    {% csrf_token %}-->
<!--    {{ form.as_p }}-->
<!--    <input type="submit">-->
<!--</form>-->

<!--{#3、手动获取form对象的字段,获取 My_Forms 中对指定标签的设置将其填充到指定的标签中#}-->
<form action="" method="post" novalidate>
    {% csrf_token %}
    <div>
        <label for="id_{{ form.name.name }}">姓名</label>
        {{ form.name }} <span>{{ form.name.errors.0 }}</span>
    </div>
    <div>
        <label for="id_{{ form.age.name }}">年龄</label>
        {{ form.age }} <span>{{ form.age.errors.0 }}</span>
    </div>
    <div>
        <label for="id_salary">工资</label>
        {{ form.salary }} <span>{{ form.salary.errors.0 }}</span>
    </div>
    <input type="submit">
</form>


{#4、用for循环展示所有字段#}
{#<form action="" method="post" novalidate>#}
{#    {% csrf_token %}#}
{#    {% for field in form %}#}
{#        <div>#}
{#            <label for="id_{{ field.name }}">{{ field.label }}</label>#}
{#            {{ field }} <span>{{ field.errors.0 }}</span>#}
{#        </div>#}
{#    {% endfor %}#}
{#    <input type="submit">#}
{#</form>#}

</body>
</html>

在设置以下 case/index/views.py 文件:

# decoding:utf-8
from django.shortcuts import render, HttpResponse
from .My_Forms import EmpForm
from . import models
from django.core.exceptions import ValidationError

def add_emp(request):
    if request.method == "GET":  # 当请求是 GET 时,将页面和对表单的设置响应回去
        form = EmpForm()
        return render(request, "add_emp.html", {"form": form})
    else:  # 当用户请求的是 POST 时就代表其提交了表单,那就对其提交的表单进行信息的核查处理
        form = EmpForm(request.POST)  # 将数据传回到限制的类中
        if form.is_valid():  # 进行数据校验,当其是有效的,符合EmpForm中设的规则时进入下一步
            # 校验成功
            data = form.cleaned_data  # 校验成功的值,会放在cleaned_data里。
            print(data)  # {'name': '这个标签输入的值', 'age': 这个标签输入的值, 'salary': Decimal('这个标签输入的值')}
            models.Emp.objects.create(**data)  # 调用数据表,将本次访问的数据进行加载/创建
            return HttpResponse('ok')  # 当数据全部符合规则时就执行响应提示信息告诉用户成功创建
            # return render(request, "add_emp.html", {"form": form})
        else:  # 当数据效验失败时,就代表其输入的内容不符合设定的规则,所以执行以下操作
            print(form.errors)    # 打印错误信息
            clean_errors = form.errors.get("__all__")  # 获取设定标签全局的错误信息
        return render(request, "add_emp.html", {"form": form, "clean_errors": clean_errors})
        # 将数据响应回去,并响应其的全局错误信息,

在 case/index/models.py 中创建一个表:用于存放效验成功的数据的。

class Emp(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    salary = models.DecimalField(max_digits=8, decimal_places=2)
    dep = models.CharField(max_length=32)
    province = models.CharField(max_length=32)

最后将路由添加到 case/index/urls.py 中:

    path('add_emp/', views.add_emp)

进行访问:

在form组件中还提供了对数据局部和全部的效验方法,如以下实例:

# -*- coding: UTF-8 -*-
from django import forms  # 该模块可以对表单进行设置
from django.core.exceptions import ValidationError
from . import models

class EmpForm(forms.Form):
    # 限制最小输入长度为4个字符,标签的名字为:姓名。设置当长度低于4个字符时,提示你太短了,和字段为空时的提示信息。
    name = forms.CharField(min_length=4, label="姓名", error_messages={"min_length": "你太短了", "required": "该字段不能为空!"})

    # 创建一个整数的输入标签,标签名:年龄
    age = forms.IntegerField(label="年龄")

    # 创建一个浮点数标签,标签名:工资和,请再输入工资,后续进行输入的值对比查看是否一致
    salary = forms.DecimalField(max_digits=5, decimal_places=2, label="工资")
    r_salary = forms.DecimalField(max_digits=5, decimal_places=2, label="请再输入工资")

    def clean_name(self):  # 局部钩子,,对单个数据进行判断,看其是否符合要求
        val = self.cleaned_data.get("name")

        if val.isdigit():  # 获取到name值,判断其是否为数字,或者其是否存在
            raise ValidationError("用户名不能是纯数字")
        elif models.Emp.objects.filter(name=val):
            raise ValidationError("用户名已存在!")
        else:  # 当其输入的值不存在和名字符合规则时,将name的值进行返回给字典中key名为name的值
            return val

    def clean(self):  # 全局钩子 确认两次输入的工资是否一致。
        val = self.cleaned_data.get("salary")
        r_val = self.cleaned_data.get("r_salary")


        if val == r_val:  # 获取两次工资的值进行判断,一致则将两值如约返回,不一致就返回提示信息
            return self.cleaned_data
        else:
            raise ValidationError("请确认工资是否一致。")

views.py 文件代码:

# decoding:utf-8
from django.shortcuts import render, HttpResponse
from .My_Forms import EmpForm
from . import models
from django.core.exceptions import ValidationError

def add_emp(request):
    if request.method == "GET":  # 当请求是 GET 时,将页面和对表单的设置响应回去
        form = EmpForm()
        return render(request, "add_emp.html", {"form": form})
    else:  # 当用户请求的是 POST 时就代表其提交了表单,那就对其提交的表单进行信息的核查处理
        form = EmpForm(request.POST)  # 将数据传回到限制的类中
        if form.is_valid():  # 进行数据校验,当其是有效的,符合EmpForm中设的规则时进入下一步
            # 校验成功
            data = form.cleaned_data  # 校验成功的值,会放在cleaned_data里。
            print(data)  # {'name': '这个标签输入的值', 'age': 这个标签输入的值, 'salary': Decimal('这个标签输入的值')}
            data.pop("r_salary")  #将用来对比用的工资的key和值删掉
            models.Emp.objects.create(**data)  # 调用数据表,将本次访问的数据进行加载/创建
            return HttpResponse('ok')  # 当数据全部符合规则时就执行响应提示信息告诉用户成功创建
            # return render(request, "add_emp.html", {"form": form})
        else:  # 当数据效验失败时,就代表其输入的内容不符合设定的规则,所以执行以下操作
            print(form.errors)    # 打印错误信息
            clean_errors = form.errors.get("__all__")  # 获取设定标签全局的错误信息
        return render(request, "add_emp.html", {"form": form, "clean_errors": clean_errors})
        # 将数据响应回去,并响应其的全局错误信息,

模板文件代码如下:

<form action="" method="post" novalidate>
    {% csrf_token %}
    <div>
        <label for="id_{{ form.name.name }}">姓名</label>
        {{ form.name }} <span>{{ form.name.errors.0 }}</span>
    </div>
    <div>
        <label for="id_{{ form.age.name }}">年龄</label>
        {{ form.age }} <span>{{ form.age.errors.0 }}</span>
    </div>
    <div>
        <label for="id_salary">工资</label>
        {{ form.salary }} <span>{{ form.salary.errors.0 }}{{ clear_errors.0 }}</span>
    </div>
    <div>
        <label for="id_r_salary">请再输入工资</label>
        {{ form.r_salary }} <span>{{ form.r_salary.errors.0 }}{{ clear_errors.0 }}</span>
    </div>
    <input type="submit">
</form>

访问结果:

Django 用户认证(Auth)组件

Django 用户认证(Auth)组件一般用在用户的登录注册上,用于判断当前的用户是否合法,并跳转到登陆成功或失败页面。

Django 用户认证(Auth)组件需要导入 auth 模块:

# 认证模块
from django.contrib import auth

# 对应数据库
from django.contrib.auth.models import User

返回值是用户对象。

创建用户对象的三种方法:

  • create():创建一个普通用户,密码是明文的。
  • create_user():创建一个普通用户,密码是密文的。
  • create_superuser():创建一个超级用户,密码是密文的,要多传一个邮箱 email 参数。

参数:

  • username: 用户名。
  • password:密码。
  • email:邮箱 (create_superuser 方法要多加一个 email)。
from django.contrib.auth.models import User 
User.objects.create(username='run',password='123')  # 创建普通用户

User.objects.create_user(username='root',password='123') # 创建普通用户,密码是密文的

# 创建超级用户,密码是密文的
User.objects.create_superuser(username='runboooo',password='123',email='runboo@163.com')

执行上诉代码可创建三个用户,root用户是最初创建的。

注销用户使用 logout() 方法,需要清空 session 信息,将 request.user 赋值为匿名用户。

使用前要导入:

from django.contrib import auth

参数:

  • request:用户对象

返回值:None

实例

def logout(request):
    ppp = auth.logout(request)
    print(ppp) # None
    return redirect("/login/")

Django cookie 与 session

Cookie 是存储在客户端计算机上的文本文件,并保留了各种跟踪信息。

识别返回用户包括三个步骤:

  • 服务器脚本向浏览器发送一组 Cookie。例如:姓名、年龄或识别号码等。
  • 浏览器将这些信息存储在本地计算机上,以备将来使用。
  • 当下一次浏览器向 Web 服务器发送任何请求时,浏览器会把这些 Cookie 信息发送到服务器,服务器将使用这些信息来识别用户。

HTTP 是一种"无状态"协议,这意味着每次客户端检索网页时,客户端打开一个单独的连接到 Web 服务器,服务器会自动不保留之前客户端请求的任何记录。

但是仍然有以下三种方式来维持 Web 客户端和 Web 服务器之间的 session 会话:

Cookies

一个 Web 服务器可以分配一个唯一的 session 会话 ID 作为每个 Web 客户端的 cookie,对于客户端的后续请求可以使用接收到的 cookie 来识别。

在Web开发中,使用 session 来完成会话跟踪,session 底层依赖 Cookie 技术。

 Django 中 Cookie 的语法:

rep = HttpResponse || render || redirect 

# 设置cookie:
rep.set_cookie(key,value) # 输入要给这个cookie设的键和他的值
rep.set_signed_cookie(key,value,salt='加密盐')

# 获取cookie:
request.COOKIES.get(key)  # 输入指定的key键来获取其的cookie值

# 删除cookie:
rep.delete_cookie(key)  # 输入指定的key键来删除这个session和cookie

Session(保存在服务端的键值)

服务器在运行时可以为每一个用户的浏览器创建一个其独享的 session 对象,由于 session 为用户浏览器独享,所以用户在访问服务器的 web 资源时,可以把各自的数据放在各自的 session 中,当用户再去访问该服务器中的其它 web 资源时,其它 web 资源再从用户各自的 session 中取出数据为用户服务。

工作原理

  • a. 浏览器第一次请求获取登录页面 login。

  • b. 浏览器输入账号密码第二次请求,若输入正确,服务器响应浏览器一个 index 页面和一个键为 sessionid,值为随机字符串的 cookie,即 set_cookie ("sessionid",随机字符串)。

  • c. 服务器内部在 django.session 表中记录一条数据。

    django.session 表中有三个字段。

    • session_key:存的是随机字符串,即响应给浏览器的 cookie 的 sessionid 键对应的值。
    • session_data:存的是用户的信息,即多个 request.session["key"]=value,且是密文。
    • expire_date:存的是该条记录的过期时间(默认14天)
  • d. 浏览器第三次请求其他资源时,携带 cookie :{sessionid:随机字符串},服务器从 django.session 表中根据该随机字符串取出该用户的数据,供其使用(即保存状态)。

注意: django.session 表中保存的是浏览器的信息,而不是每一个用户的信息。 因此, 同一浏览器多个用户请求只保存一条记录(后面覆盖前面),多个浏览器请求才保存多条记录。

cookie 弥补了 http 无状态的不足,让服务器知道来的人是"谁",但是 cookie 以文本的形式保存在浏览器端,安全性较差,且最大只支持 4096 字节,所以只通过 cookie 识别不同的用户,然后,在对应的 session 里保存私密的信息以及超过 4096 字节的文本。

session 设置:

request.session["key"] = value
'''1、生成随机字符串 
   2、把随机字符串和设置的键值对保存到 django_session 表的 session_key 和 session_data 里 
   3、设置 cookie:set_cookie("sessionid",随机字符串) 响应给浏览器
'''

session 获取:

request.session.get('key') 
''' 1、从 cookie 中获取 sessionid 键的值,即随机字符串。
    2、根据随机字符串从 django_session 表过滤出记录。
    3、取出 session_data 字段的数据。
'''

request.session.flush()
# 删除整条记录(包括 session_key、session_data、expire_date 三个字段)

删除 session_data 里的其中一组键值对:

del request.session["key"] 
''' 1、从 cookie 中获取 sessionid 键的值,即随机字符串。
    2、根据随机字符串从 django_session 表过滤出记录。
    3、删除过滤出来的记录
'''

Django 中间件

Django 中间件可以理解为是介于 HttpRequest 与 HttpResponse 处理之间的一道处理过程。

浏览器从请求到响应的过程中,Django 需要通过很多中间件来处理,可以看如下图所示:

Django 中间件作用:

  • 修改请求,即传送到 view 中的 HttpRequest 对象。
  • 修改响应,即 view 返回的 HttpResponse 对象。

中间件组件配置在 settings.py 文件的 MIDDLEWARE 选项列表中。

配置中的每个字符串选项都是一个类,也就是一个中间件。

Django 默认的中间件配置:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

自定义中间件 # 实例

在 app 目录下新建一个 py 文件,名字自定义,并在该 py 文件中导入 MiddlewareMixin:

from django.utils.deprecation import MiddlewareMixin

​ 创建一个类,用来自定义中间件,该类要继承父类 MiddlewareMixin :

class MID_1(MiddlewareMixin): # 定义一个名为 MID_1 的中间件
    def process_request(self, request):  # 定义请求经过该中间件时的处理方式
        pass #在视图之前执行

    def process_response(self,request, response):  # 定义响应经过该中间件时的处理方式
        return response #在视图之后

    def process_view(self,request, view_func, view_args, view_kwargs):
        ''' 定义一个视图处理函数,当请求经过该中间件时可让其直接执行视图函数进行请求响应 '''
        return view_func(request)  #在视图之前执行 顺序执行


    def process_exception(self, request, exception):# 在视图之后,响应之前执行
        ''' 当在视图函数中引发错误时,响应经过该方法时可以修改响应信息从而让其不报错 。
            在 process_view 中引发的报错响应不经过该方法,而是直接响应给用户报错信息''' 
        return HttpResponse(exception) # 返回错误信息,也可返回指定的正确信息

 在 settings.py 中的 MIDDLEWARE 里注册自定义的中间件类:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
   
    'index.middleware.MID_1',
]

有四个方法可以自定义中间件,分别是:

process_request(self,request)  方法

process_request 方法有一个参数 request,这个 request 和视图函数中的 request 是一样的。

process_request 方法的返回值可以是 None 也可以是 HttpResponse 对象。

  • 返回值是 None 的话,按正常流程继续走,交给下一个中间件处理。
  • 返回值是 HttpResponse 对象,Django 将不执行后续视图函数之前执行的方法以及视图函数,直接以该中间件为起点,倒序执行中间件,且执行的是视图函数之后执行的方法。

process_request 方法是在视图函数之前执行的。

当配置多个中间件时,会按照 MIDDLEWARE中 的注册顺序,也就是列表的索引值,顺序执行。

不同中间件之间传递的 request 参数都是同一个请求对象。


process_view(self, request, view_func, view_args, view_kwargs)  方法

process_view 方法有四个参数:

  • request 是 HttpRequest 对象。
  • view_func 是 Django 即将使用的视图函数。
  • view_args 是将传递给视图的位置参数的列表。
  • view_kwargs 是将传递给视图的关键字参数的字典。

view_args 和 view_kwargs 都不包含第一个视图参数(request)。

process_view 方法是在视图函数之前,process_request 方法之后执行的。

返回值可以是 None、view_func(request) 或 HttpResponse 对象。

  • 返回值是 None 的话,按正常流程继续走,交给下一个中间件处理。
  • 返回值是 HttpResponse 对象,Django 将不执行后续视图函数之前执行的方法以及视图函数,直接以该中间件为起点,倒序执行中间件,且执行的是视图函数之后执行的方法。
  • 返回值是 view_func(request),Django 将不执行后续视图函数之前执行的方法,提前执行视图函数,然后再倒序执行视图函数之后执行的方法。
  • 当最后一个中间件的 process_request 到达路由关系映射(urls.py)之后,返回到第一个中间件 process_view,然后依次往下,到达视图函数。

process_exception(self, request, exception)  方法

参数介绍:

  • request 是 HttpRequest 对象。
  • exception 是视图函数异常产生的 Exception 对象。

process_exception 方法只有在视图函数中出现异常了才执行,按照 settings 的注册倒序执行。

在视图函数之后,在 process_response 方法之前执行。

process_exception 方法的返回值可以是一个 None 也可以是一个 HttpResponse 对象。:

  • 返回值是 None,页面会报 500 状态码错误。然后再倒序执行 process_response 方法。
  • 返回值是 HttpResponse 对象,页面不会报错,返回状态码为 200。该中间件后续的 process_exception 方法也不执行,直接从最后一个中间件的 process_response 方法倒序开始执行。

若是 process_view 方法返回视图函数,提前执行了视图函数,且视图函数报错,则无论 process_exception 方法的返回值是什么,页面都会报错, 且视图函数和 process_exception 方法都不执行。(在process_view 方法中执行视图函数报错的话就直接响应报错,不交给process_exception 方法,而是直接交给响应中间件进行响应)

process_response(self, request, response)  方法

process_response 方法有两个参数,request 是请求对象,response 是视图函数返回的 HttpResponse 对象,该方法必须要有返回值,且必须是response。

process_response 方法是在视图函数之后执行的。

当配置多个中间件时,会按照 MIDDLEWARE 中的注册顺序,也就是列表的索引值,倒序执行。

从下图看,正常的情况下按照绿色的路线进行执行,假设中间件1有返回值,则按照红色的路线走,直接执行该类下的 process_response 方法返回,后面的其他中间件就不会执行。

Django 视图 - FBV 与 CBV

FBV

基于函数的视图,就是使用了函数来处理用户的请求,urls.py 中配置信息为:

    path("login/", views.login),

views.py 中配置信息为:

from django.shortcuts import render,HttpResponse

def login(request):
    return HttpResponse("--")

CBV

基于类的视图,就是使用了类来处理用户的请求,不同的请求我们可以在类中使用不同方法来处理,这样大大的提高了代码的可读性。

定义的类要继承父类 View,所以需要先引入库:

from django.views import View

父类(View)中提供了一个静态方法 as_view() 方法是基于类的外部接口, 他返回一个视图函数,调用后请求会传递给 dispatch() 方法,dispatch 方法再根据不同请求来处理不同的方法。

urls.py中配置:

    path("login/", views.Login.as_view()),
''' 请求 login/ 时会将请求传给 as_view() 这个方法处理,as_view 这个方法会将
    该次请求传递给 dispatch() 方法。dispatch 方法在进行判断,根据对应的请求
    会到views.Login 这个类中调用对应的处理方法进行响应请求。
'''

views.py中设置对应的类:

from django.shortcuts import render,HttpResponse
from django.views import View

class Login(View):  # dispatch方法,会根据对应的请求来调用对应的处理方法进行响应。
    def get(self,request):  # 当用户请求的是 GET 方法时,就调用该函数进行响应请求
        return HttpResponse("GET")

    def post(self,request):  # 当用户请求的是 POST 方法时,就调用该函数进行响应请求
        return HttpResponse("POST")

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值