Django

请求与响应篇

一、HttpRequest对象

服务器接收到http协议的请求后,会根据报文创建HttpRequest对象。视图函数的第一个参数(request)是HttpRequest对象在django.http模块中定义了HttpRequest对象的API

所谓的API,在django中就是属性、方法。

(一).HttpRequest对象的属性及方法

属性:
path:一个字符串,表示请求的页面的完整路径,不包含域名
method:一个字符串,表示请求使用的HTTP方法,常用值包括:'GET''POST'
encoding:一个字符串,表示提交的数据的编码方式
如果为None则表示使用浏览器的默认设置,一般为utf-8
这个属性是可写的,可以通过修改它来修改访问表单数据使用的编码,接下来对属性的任何访问将使用新的encoding值
GET:一个类似于字典的对象,包含get请求方式的所有参数
POST:一个类似于字典的对象,包含post请求方式的所有参数
FILES:一个类似于字典的对象,包含所有的上传文件
COOKIES:一个标准的Python字典,包含所有的cookie,键和值都为字符串
session:一个既可读又可写的类似于字典的对象,表示当前的会话,只有当Django 启用会话的支持时才可用,详细内容见“状态保持”

方法:
is_ajax():如果请求是通过XMLHttpRequest发起的,则返回True
View Code

(二).QueryDict对象

request对象的GET、POST属性,都是QueryDict类型的对象。它不是python的字典!QueryDict类型的对象用来处理同一个键带有多个值的情况。

(1).get()

根据键获取值,只能获取键的一个值。如果一个键同时拥有多个值,获取最后一个值

(2).getlist()

根据键获取值,将键的值以列表返回,可以获取一个键的多个值。

(3).例

dict.get('',default)  # 或简写为 dict['键']
dict.getlist('',default)
View Code

(三).GET属性

(1).QueryDict类型的对象

(2).包含get请求方式的所有参数

(3).与url请求地址中的参数对应,在url的?后面进行拼接

(4).参数的格式是键值对,如key1=value1

(5).多个参数之间,使用&连接,如key1=value1&key2=value2

(四).POST属性

(1).QueryDict类型的对象

(2).包含post请求方式的所有参数

(3).与form表单中的控件对应

(4).表单中控件要有name属性,则name属性的值为键,value属性的值为键,构成键值对提交

(5).对于checkbox控件,name属性一样为一组,当控件被选中后会被提交,存在一键多值的情况

二、HttpResponse对象

(一).属性及方法

属性:
content:表示返回的内容,字符串类型
charset:表示response采用的编码字符集,字符串类型
status_code:响应的HTTP响应状态码

方法:
init:使用页内容实例化HttpResponse对象
write(content):以文件的方式写
flush():以文件的方式输出缓存区
set_cookie(key, value='', max_age=None, expires=None):设置Cookie
key、value都是字符串类型
max_age是一个整数,表示在指定秒数后过期
expires是一个datetime或timedelta对象,会话将在这个指定的日期/时间过期,注意datetime和timedelta值只有在使用PickleSerializer时才可序列化
max_age与expires二选一
如果不指定过期时间,则关闭浏览器就失效.
delete_cookie(key):删除指定的key的Cookie,如果key不存在则什么也不发生
View Code

(二).cookie的例子

def cookie_test(request):
    response = HttpResponse()
    response.set_cookie("a", "123")  # 设置一个cookie
    cookie = response.COOKIES
    a = cookie.get("a", None)  # 获取cookie
    response.write(a)
    return response
View Code

cookie是以明文的方式保存在客户端的,如果有敏感的信息则不安全。

(三).HttpResponse的子类:JsonResponse

(1).返回json数据

(2).帮助用户创建JSON编码的响应

(3).参数data是字典对象

(4).JsonResponse的默认Content-Type为application/json

(5).例

from django.http import JsonResponse

def jpTest(request):
    return JsonResponse({'ss':'123455'})
View Code

三、状态保持

(1).http协议是无状态的:每次请求都是一次新的请求,不会记得之前通信的状态

(2).客户端与服务器端的一次通信,就是一次会话实现状态保持的方式:在客户端或服务器端存储与会话有关的数据

(3).存储方式包括cookie、session,会话一般指session对象

(4).使用cookie,所有数据存储在客户端,注意不要存储敏感信息

(5).推荐使用sesison方式,所有数据存储在服务器端,在客户端cookie中存储session_id

(6).状态保持的目的是在一段时间内跟踪请求者的状态,可以实现跨页面访问当前请求者的数据

(7).注意事项:不同的请求者之间不会共享这个数据,与请求者一一对应

四、会话(session)

session是保存在服务端的,使用sessionid对应服务端中的session,sessionid则保存在cookie中。

cookie对应sessionid,sessionid对应服务端的session

(一).启用session

(1).检查settings.py文件

检查settings.py文件是否有下面这些属性

# 没有的话,就添加

# INSTALLED_APPS 列表中:
'django.contrib.sessions',

# MIDDLEWARE_CLASSES 列表中:
'django.contrib.sessions.middleware.SessionMiddleware',
View Code

(二).使用session

(1).启用会话后,每个HttpRequest对象将具有一个session属性,它是一个类字典对象

(2).get(key, default=None):根据键获取会话的值

(3).clear():清除所有会话

(4).flush():删除当前的会话数据并删除会话的Cookie

(5).del request.session['member_id']:删除会话

(三).session保持用户登录的例子

(1).视图函数

from django.shortcuts import render, HttpResponse, redirect, reverse


# Create your views here.

def session_index(request):
    """
    首页
    :param request:
    :return:
    """
    login_status = request.session.get("username", "你未登录,请登录!")
    return render(
        request,
        "ts11/session_index.html",
        context={
            "login_status": login_status,
        },
    )


def session_login(request):
    """
    登录页面
    用session实现登录
    :param request:
    :return:
    """
    if request.method == "GET":
        return render(request, "ts11/session_login.html")
    elif request.method == "POST":
        request.session["username"] = request.POST.get("username")
        return redirect(reverse("index"))
    else:
        return HttpResponse("无效的请求")


def session_login_out(request):
    """
    注销
    清空session
    :param request:
    :return: 清除session后,直接重定向,回到首页
    """
    request.session.flush()
    return redirect(reverse("index"))
View Code

(2).url配置

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r"^session_index/$", views.session_index, name="index"),
    url(r"^session_login/$", views.session_login, name="login_in"),
    url(r"^session_login_out/$", views.session_login_out, name="login_out"),
]
View Code

(3).模板

# session_index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>session_index</title>
</head>
<body>
你好!{{ login_status }}<br>
<a href="{% url "login_in" %}">登录</a>
<br><br>
<a href="{% url "login_out" %}">注销</a>
</body>
</html>


# session_login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>session_login</title>
</head>
<body>
<form action="" method="post">
    {% csrf_token %}
    用户名:<input type="text" name="username"><br>
    <input type="submit" value="提交">
</form>
</body>
</html>
View Code

(四).会话过期时间

(1).set_expiry(value):设置会话的超时时间

(2).如果没有指定,则14天后过期

(3).如果value是一个整数,会话将在values秒没有活动后过期

(4).若果value是一个imedelta对象,会话将在当前时间加上这个指定的日期/时间过期

(5).如果value为0,那么用户会话的Cookie将在用户的浏览器关闭时过期

(6).如果value为None,那么会话永不过期

(7).在视图函数中设置过期的示例

#------------view.py--------------
def login(request):
    if request.method == 'GET':
        return render(request,'login.html')
    elif request.method == 'POST':
        username = request.POST.get('username')
        request.session['username']=username
        request.session.set_expiry(0)  # 关闭浏览器就过期
        return redirect(reverse('ts11_home'))
View Code

(8).settings.py中的配置

# 是否关闭浏览器使得Session过期,默认是False
SESSION_EXPIRE_AT_BROWSER_CLOSE = False   

#是否每次请求都保存Session,默认修改之后才保存
SESSION_SAVE_EVERY_REQUEST = False

# Session的cookie失效日期,默认是2周
SESSION_COOKIE_AGE = 1209600
View Code

(五).如何查看cookie和session

我是chrome的忠实粉,所以只看了Google Chrome的设置。

步骤:设置 - 高级 - 内容设置 - Cookie - 查看所有Cookie和网站数据 - 找到你自己的IP或域名


表单篇

django中的表单不是html中的那个<form>表单。而是app中的forms.py这个文件所生成的(这个文件名不像templates那样严格,可以写form.py也可以写forms.py,但基本是写forms)

django的表单通常是用来验证数据的合法性。很少用来生成HTML代码(django生成的表单较难调整样式)

一、使用表单

(一).表单常用的属性、方法

(1).创建一个forms.py的文件,放在app当中,然后在里面写表单

(2).表单是通过类实现的,继承自forms.Form,然后在里面定义要验证的字段

(3).在表单中,创建字段跟模型是一模一样的,但是没有null=True或者blank=True等这几种参数了,有的参数是required=True/False

(4).使用is_valid()方法可以验证用户提交的数据是否合法,而且HTML表单元素的name必须和django中的表单的name保持一致,否则匹配不到

(5).is_bound属性:用来表示form是否绑定了数据,如果绑定了,则返回True,否则返回False

(6).cleaned_data:这个是在is_valid()返回True的时候,保存用户提交上来的数据.

(7).示例

# forms.py

from django import forms


class RegisterForm(forms.Form):
    """
    验证注册页面的form
    """
    username = forms.CharField(
        max_length=100, min_length=6,
        error_messages={
            "min_length": "用户名不能少于6位",
        }
    )
    password = forms.CharField(
        max_length=100, min_length=6,
        widget=forms.PasswordInput(),
        error_messages={
            "min_length": "密码不能少于6位",
        }
    )
    password_repeat = forms.CharField(
        max_length=100, min_length=6,
        widget=forms.PasswordInput(),
        error_messages={
            "min_length": "密码不能少于6位",
        }
    )
    email = forms.EmailField()


class RegisterLogin(forms.Form):
    """
    验证登录页面的form
    """
    username = forms.CharField(
        max_length=100, min_length=6,
        error_messages={
            "min_length": "用户名不能少于6位",
        }
    )
    password = forms.CharField(
        max_length=100, min_length=6,
        widget=forms.PasswordInput(),
        error_messages={
            "min_length": "密码不能少于6位",
        }
    )
View Code

(二).字段类型中的一些参数

这些参数会对页面的输入做一些限制条件

max_length  最大长度
min_length  最小长度
widget  负责渲染网页上HTML 表单的输入元素和提取提交的原始数据
attrs  包含渲染后的Widget 将要设置的HTML 属性
error_messages 报错信息
View Code

二、表单小案例

需求:实现注册、登录。

要求:使用djang的form进行表单验证。

(一).写模型

from django.db import models


# Create your models here.

class Register(models.Model):
    """
    注册页面的模型
    """
    username = models.CharField(max_length=100, unique=True)
    password = models.CharField(max_length=100)
    email = models.EmailField()

    def __str__(self):
        return "Register <username:{},password:{},email:{}>".format(
            self.username, self.password, self.email
        )
View Code

然后进行makemigrations和migrate

(二).写forms.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# __author__ = "Blue Margaret"

from django import forms


class RegisterForm(forms.Form):
    """
    验证注册页面的form
    """
    username = forms.CharField(
        max_length=100, min_length=6,
        error_messages={
            "min_length": "用户名不能少于6位",
        }
    )
    password = forms.CharField(
        max_length=100, min_length=6,
        widget=forms.PasswordInput(),
        error_messages={
            "min_length": "密码不能少于6位",
        }
    )
    password_repeat = forms.CharField(
        max_length=100, min_length=6,
        widget=forms.PasswordInput(),
        error_messages={
            "min_length": "密码不能少于6位",
        }
    )
    email = forms.EmailField()


class RegisterLogin(forms.Form):
    """
    验证登录页面的form
    """
    username = forms.CharField(
        max_length=100, min_length=6,
        error_messages={
            "min_length": "用户名不能少于6位",
        }
    )
    password = forms.CharField(
        max_length=100, min_length=6,
        widget=forms.PasswordInput(),
        error_messages={
            "min_length": "密码不能少于6位",
        }
    )
View Code

(三).写视图函数views.py

(1).导包

from django.shortcuts import render, HttpResponse, redirect, reverse

from .forms import *
from .models import *
View Code

(2).index()

def index(request):
    """
    首页
    :param request:
    :return:
    """
    login_status = request.session.get("username", "请登录")
    return render(
        request,
        "ts22/index.html",
        context={
            "login_status": login_status,
        },
    )
View Code

(3).register()

def register(request):
    """
    注册页面的视图函数
    :param request:
    :return:
    """
    if request.method == "GET":
        return render(request, "ts22/register.html")
    elif request.method == "POST":
        form = RegisterForm(request.POST)  # 使用django的form进行验证
        if form.is_valid():
            username = request.POST["username"]
            password = request.POST["password"]
            password_repeat = request.POST["password_repeat"]
            email = request.POST["email"]
            if password == password_repeat:
                Register.objects.create(username=username, password=password_repeat, email=email)
            else:
                return render(
                    request,
                    "ts22/register.html",
                    context={"error_message": "两次密码输入的不一致,请检查!", },
                )
        else:
            return render(request, "ts22/register.html", context={"error_message": form.errors, }, )
        return render(request, "ts22/register_success.html")  # 直接渲染模板,进行跳转
    else:
        return render(request, "ts22/register.html")
View Code

(4).login()

def login(request):
    """
    登录页面的视图函数
    :param request:
    :return:
    """
    if request.method == "GET":
        return render(request, "ts22/login.html")
    elif request.method == "POST":
        form = RegisterLogin(request.POST)
        if form.is_valid():
            form_data = form.cleaned_data
            username = form_data["username"]
            password = form_data["password"]
            try:
                Register.objects.get(username=username, password=password)  # get()没有返回值是会报错的
            except Register.DoesNotExist:
                return HttpResponse("登录失败,请检查用户名或密码!")  # 用异常捕获来检验是否登录成功
            else:
                request.session["username"] = username  # 保存session
                return redirect(reverse("index"))
        else:
            return render(request, "ts22/login.html", context={"error_messages": form.errors}, )
    else:
        return HttpResponse("无效的请求")
View Code

(5).logout()

def logout(request):
    """
    注销页面的视图函数
    :param request:
    :return: 注销后直接回到首页
    """
    request.session.flush()
    return redirect(reverse("index"))
View Code

(四).写模板templates/ts22

(1).index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
你好,{{ login_status }}!
<br>
<a href="{% url "register" %}">注册</a>
<br>
<a href="{% url "login" %}">登录</a>
<br>
<a href="{% url "logout" %}">注销</a>
</body>
</html>
View Code

(2).login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
<form action="" method="post">
    {% csrf_token %}
    用户名:<input type="text" name="username">
    <br>
    密码:<input type="password" name="password">
    <br>
    <input type="submit" value="登录">
    {{ error_messages }}
</form>
</body>
</html>
View Code

(3).register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>register</title>
</head>
<body>
<form action="" method="post">
    {% csrf_token %}
    用户名:<input type="text" name="username" placeholder="请输入用户名">
    <br>
    密码:<input type="password" name="password" placeholder="请输入密码">
    <br>
    确认密码:<input type="password" name="password_repeat" placeholder="请确认密码">
    <br>
    邮箱:<input type="email" name="email" placeholder="请输入邮箱地址">
    <br>
    <input type="submit" value="我要注册">
    {{ error_message }}
</form>
</body>
</html>
View Code

(4).register_success.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册成功</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .bigbox {
            width: 400px;
            height: 200px;
            text-align: center;
            border: 2px solid rgb(180, 9, 28);
            border-radius: 7px;
            position: absolute;
            margin: auto;
            top: 0;
            right: 0;
            bottom: 0;
            left: 0;
            box-shadow: lightgrey 3px 3px 3px;
        }

        .cell {
            position: relative;
            top: 50%;
            transform: translateY(-50%);
        }
    </style>
</head>
<body>
<div class="bigbox">
    <div class="cell">
        注册成功!感谢您注册成为我们的会员!
        <br>
        <span id="countdown_seconds">5</span>秒后,将会自动回到首页!
        <br>
        如果浏览器没有自动跳转,<a href="{% url "index" %}">点此回到主页</a>
    </div>
</div>
<script>
    window.onload = function () {
        let time = document.getElementById("countdown_seconds").innerText;
        let set = setInterval(function () {
            time--;
            if (time < 1) {
                {#<1就不会出现0了#}
                window.location = "{% url "index" %}";
            } else {
                document.getElementById("countdown_seconds").innerText = time;
            }
        }, 1000);
    }
</script>
</body>
</html>
View Code

(五).注册路由

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r"^index/$", views.index, name="index"),
    url(r"^register/$", views.register, name="register"),
    url(r"^login/$", views.login, name="login"),
    url(r"^logout/$", views.logout, name="logout"),
]
View Code

(六).注意事项

(1).新的app不要忘记去总url中注册

(2).新的app不要忘记去settings.py中的INSTALLED_APPS中注册


中间件篇

中间件,顾名思义:中途作处理。可以介入Django的请求和响应处理过程,修改Django的输入或输出。它针对的是request、response

一、自定义中间件

(一).在settings.py同级目录下创建myself_middleware.py文件(中间件的命名随意,但要符合python变量命名规则)

(二).写入代码

Ps:模型直接沿用了表单篇的小案例

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# __author__ = "Blue Margaret"

from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin

from ts22.models import *


class MyExceptionMiddleware(MiddlewareMixin):
    def process_exception(self, request, exception):
        return HttpResponse("程序出错了:" + str(exception))


class RegisterMiddleware(MiddlewareMixin):
    def __init__(self, get_response):
        # 接收一个请求对象,也就是request
        self.get_response = get_response

    def __call__(self, request):
        # request到达view之前执行的代码
        username = request.session.get("username", "")
        user = Register.objects.filter(username=username).first()
        if user:
            if not hasattr(request, "my_user"):
                setattr(request, "my_user", user.username)
        else:
            setattr(request, "my_user", "请登录.this is from middleware")

        response = self.get_response(request)  # 返回响应

        """
        这块代码是response对象,到达浏览器之前执行的代码
        """

        return response
View Code

(三).每个中间件组件是一个独立的Python类,可以在类中定义下面方法中的一个或多个

注意:它们的方法名都是固定的!

_init _:无需任何参数,服务器响应第一个请求的时候调用一次,用于确定是否启用当前中间件

process_request(request):执行视图之前被调用,在每个请求上调用,返回None或HttpResponse对象

process_view(request, view_func, view_args, view_kwargs):调用视图之前被调用,在每个请求上调用,返回None或HttpResponse对象

process_template_response(request, response):在视图刚好执行完毕之后被调用,在每个请求上调用,返回实现了render方法的响应对象

process_response(request, response):所有响应返回浏览器之前被调用,在每个请求上调用,返回HttpResponse对象

process_exception(request,response,exception):当视图抛出异常时调用,在每个请求上调用,返回一个HttpResponse对象
View Code

(四).注册

将(二)中的两个类,注册到settings.py中间件中

(五).在视图函数中使用中间件

def index(request):
    """
    首页
    :param request:
    :return:
    """
    login_status = request.my_user
    return render(
        request,
        "ts22/index.html",
        context={
            "login_status": login_status,
        },
    )
View Code

(六).作用

后期需要加功能,直接加中间件处理了,不可能一个个视图去改了。

如果有好多视图需要同一个功能,中间件就很好用了。一处写,整个项目都能用了。


上下文处理器篇

实质上就是视图函数中的render(context={}),上下文处理器就是个模板变量,只针对模板。

一、自定义上下文处理器

(一).在settings.py同级目录下创建myself_contextprocessor.py文件(命名随意,但要符合python变量命名规则)

(二).写入代码

Ps:还是沿用了表单篇的models

from ts22.models import *


def is_my_user_login(request):
    username = request.session.get("username", "")
    user = Register.objects.filter(username=username).first()

    if user:
        return {"login_or_not": user.username}
    else:
        return {"login_or_not": "请登录"}
View Code

返回的必须是一个字典!

(三).把这个函数注册进去

(四).使用它

直接去模板中,在你想要的地方,写上{{ login_or_not }}

原文地址:https://www.cnblogs.com/quanquan616/p/9138748.html