请求与响应篇
一、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
(二).QueryDict对象
request对象的GET、POST属性,都是QueryDict类型的对象。它不是python的字典!QueryDict类型的对象用来处理同一个键带有多个值的情况。
(1).get()
根据键获取值,只能获取键的一个值。如果一个键同时拥有多个值,获取最后一个值
(2).getlist()
根据键获取值,将键的值以列表返回,可以获取一个键的多个值。
(3).例
dict.get('键',default) # 或简写为 dict['键'] dict.getlist('键',default)
(三).
属性: 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不存在则什么也不发生
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
cookie是以明文的方式保存在客户端的,如果有敏感的信息则不安全。
(三).HttpResponse的子类:
from django.http import JsonResponse def jpTest(request): return JsonResponse({'ss':'123455'})
三、状态保持
(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',
(二).使用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"))
(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"), ]
(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>
(四).会话过期时间
(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'))
(8).
# 是否关闭浏览器使得Session过期,默认是False SESSION_EXPIRE_AT_BROWSER_CLOSE = False #是否每次请求都保存Session,默认修改之后才保存 SESSION_SAVE_EVERY_REQUEST = False # Session的cookie失效日期,默认是2周 SESSION_COOKIE_AGE = 1209600
(五).如何查看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位", } )
(二).字段类型中的一些参数
这些参数会对页面的输入做一些限制条件
max_length 最大长度
min_length 最小长度
widget 负责渲染网页上HTML 表单的输入元素和提取提交的原始数据
attrs 包含渲染后的Widget 将要设置的HTML 属性
error_messages 报错信息
二、表单小案例
需求:实现注册、登录。
要求:使用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 )
然后进行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位", } )
(三).写视图函数views.py
(1).导包
from django.shortcuts import render, HttpResponse, redirect, reverse from .forms import * from .models import *
(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, }, )
(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")
(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("无效的请求")
(5).logout()
def logout(request): """ 注销页面的视图函数 :param request: :return: 注销后直接回到首页 """ request.session.flush() return redirect(reverse("index"))
(四).写模板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>
(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>
(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>
(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>
(五).注册路由
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"),
]
(六).注意事项
(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
(三).每个中间件组件是一个独立的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对象
(四).注册
将(二)中的两个类,注册到settings.py中间件中
(五).在视图函数中使用中间件
def index(request): """ 首页 :param request: :return: """ login_status = request.my_user return render( request, "ts22/index.html", context={ "login_status": login_status, }, )
(六).作用
后期需要加功能,直接加中间件处理了,不可能一个个视图去改了。
如果有好多视图需要同一个功能,中间件就很好用了。一处写,整个项目都能用了。
上下文处理器篇
实质上就是视图函数中的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": "请登录"}
返回的必须是一个字典!
(三).把这个函数注册进去
(四).使用它
直接去模板中,在你想要的地方,写上{{ login_or_not }}