HTTP请求中产生两个核心的对象:
http请求:HttpRequest对象
http响应:HttpResponse对象
所在位置django.http,前边用的reques就是HttpRequest对象。
HttpRequest对象的属性:
path:请求页面的全路径,不包括域名和端口
method:请求中使用的HTTP方法的字符串表示,全大写表示,如POST、GET等
GET:包含所有HTTP GET参数的类字典对象
POST:包含所有HTTP POST参数的类字典对象。服务器收到空的POST请求的情况也是可能发生的,即表单form通过HTTP POST方法提交请求,但是表单中可能没有数据,因此不能使用
if req.POST来判断是否使用了HTTP POST方法;应该使用 if req.method == 'POST'
COOKIES:包含所有cookies的标准python字典对象;keys和values都是字符串。
FILES:包含所有上传文件的类字典对象;FILES中的每一个Key都是标签中name属性的值,FILES中的每一个value同时也是一个标准的python字典对象,包含下面三个Keys:
filename:上传文件名,用字符串表示
content_type:上传文件的Content Type
content:上传文件的原始内容
user:是一个django.contrib.auth.models.User对象,代表当前登陆的用户。如果访问用户当前没有登陆,user将被初始化为django.contrib.auth.models.AnonymouseUser的实例。可以通过user的is_authenticated()方法来辨别用户是否登陆:if req.user.is_authenticaed();只有激活Django中的AuthenticationMiddleware时该属性才能用
session: 唯一可读写的属性,代表当前会话的字典对象;自己有激活Django中的session支持时该属性才可用。
HttpRequest对象的方法:get_full_path(),比如:http://127.0.0.1:8000/index33/?name=123 ,req.get_full_path()得到的结果就是/index33/?name=123
req.path得到的是:/index33
HttpResponse对象:
对于HttpRequest对象,是Django自动创建的,但是HttpResponse对象就必须自己创建。每个views请求处理方法必须返回一个HttpResponse对象。
HttpResponse类在django.http.HttpResponse
在HttpResponse对象上扩展的常用方法有:
页面渲染:render(),render_to_response()
页面跳转:redirect()
locals():可以直接将函数中所有的变量传给模板
render()与redirect()的区别,render是直接渲染一个页面文件,而redirect是重新提交一个请求路径。
测试:
登录页面:
Title
欢迎登陆!!
{{ msg }}
路由:
from django.contrib import admin
from django.urls import path,re_path,include
from app01 import views,views_app01
urlpatterns = [
path('new/story/',views.app_story),
path('login/',views_app01.login),
]
视图函数:
from django.shortcuts import render,HttpResponse,redirect,reverse
from pathlib import Path
import os
import datetime
from app01 import models
def login(req):
msg = ""
if req.method == "GET":
msg = "经过了if逻辑判断!"
return render(req,"login.html",{"msg":msg})
if req.method == "POST":
login_fail = 1
if login_fail:
return render(req,"login.html",{"msg":msg})
# return redirect("/app01/login/")
第一次进入:
视图函数使用render,点击submit按钮,这时只是渲染了login.html页面,这时的结果 :
没有经过if逻辑判断,直接渲染的login.html, 所以,msg是空,注意,这里的空,不是msg=“”赋值的,而是根本没有定义,渲染就没有经过msg=""这里。
视图函数使用redirect,点击submit,使用redirect是提交的路径,相当于又走了一遍请求,经过了if
模板:Template和Context
不使用模板直接将HTML硬编码到视图里,也可以实现内容反馈前端,但是却并不是一个好主意。 主要原因:
1、对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改往往比底层 Python 代码的修改要频繁得多,因此如果可以在不进行 Python 代码修改的情况下变更设计,那将会方便得多。
2、Python 代码编写和 HTML 设计是两项不同的工作,由不同的人员(或部门)来完成。 设计者和HTML/CSS的编码人员不应该被要求去编辑Python的代码来完成他们的工作。
3、程序员编写 Python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另一个人完成对某个既包含 Python又包含 HTML 的文件的编辑工作。
将页面的设计和Python的代码分离开会更干净简洁更容易维护,于是出现了模板
模板就是HTML+逻辑控制语句
对于前面views中的函数,最后的render(req,“index.html”,{“abc”:times}),第二个参数叫做模板,第三个参数,即字典叫做Context上下文。
在Django中,模板和上下文都是一个对象,在Python交互窗口中演示:
视图文件中函数最后返回的: render(req,“index.html”,{“abc”:times}),实际就是先读取index.html模板文件,生成一个魔板对象,像上图中的t,然后将第三个参数{“abc”:times}封装成上下文对象,然后魔板对象调用render,使用上下文对象渲染模板。
一个模板可以反复使用多次,被不同的Context上下文渲染:
在Django中视图函数的各种写法:
def current_time(req):
# 原始的视图函数,视图函数的作用就是最后反馈内容给前端浏览器,实际就是反馈HttpResponse对象
now=datetime.datetime.now()
html="现在时刻:%s.
" %now
# 使用字符串格式化命令,替换处理后反馈给浏览器的内容
return HttpResponse(html)
def current_time(req):
# django模板修改的视图函数
now=datetime.datetime.now()
t=Template('现在时刻是:{{current_date}}
')
# t=get_template('current_datetime.html') 上面的写法可以将参数写在html文件中,然后以加载文件的方式生成模板,效率高,又进行了分离
c=Context({'current_date':now})
html=t.render(c)
return HttpResponse(html)
#另一种写法(推荐)
def current_time(req):
now=datetime.datetime.now()
return render(req, 'current_datetime.html', {'current_date':now})
前面的例子 context 传递的简单参数值主要是字符串,然而,模板系统能够非常简洁地处理更加复杂的数据结构,例如list、dictionary和自定义的对象。
模板中万能的句点号:替换变量{{ 变量名 }}
列表,views中最后render的Context参数:{“list”:[111,222,333]},在模板中{{ list.2 }}返回列表的第三项
字典,views中最后render的Context参数:{“dict”:{“a”:111,“b”:222}},在模板中{{ list.a }}返回字典的a键的值
对象,views中最后render的Context参数:{“obj”:times},times = datetime.datetime.now(),在模板中可以使用句点号返回时间的属性,如{{ obj.year }}返回年
# app01项目下的urls.py
from django.urls import path,re_path,include
from app01 import views,views_app01
urlpatterns = [
path('new/story/',views.app_story),
path('login/',views_app01.login),
path('index/',views_app01.index),
]
# app01项目下的views_app01.py视图
from django.shortcuts import render
from pathlib import Path
import datetime
def index(req):
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
p1 = Person('张三',32)
times = datetime.datetime.now()
return render(req,"index.html",{"obj1":times,"obj2":p1})
Title
{{ obj1 }}------{{ obj1.year }}
{{ obj2.name }}==>{{ obj2.age }}
{{ obj2 }}
模板中的语句:
if语句:
{% if %}
{% elif %}
{% else %}
{% endif %}
for语句:
{% for item in obj %}
obj是列表,item是每项的值,想要取得索引,使用{{ forloop.counter }},默认counter是从1开始的
obj3=["a","b","c"]
Title
{% for item in obj3 %}
{{ forloop.counter }}===>{{ item }}
{% endfor %}
{% for item in obj3 %}
{{ forloop.counter0 }}===>{{ item }}
{% endfor %}
{% for item in obj3 %}
{{ forloop.revcounter }}===>{{ item }}
{% endfor %}
{% for item in obj3 %}
{{ forloop.revcounter0 }}===>{{ item }}
{% endfor %}
如果obj3是字典,遍历的是Key,想要值需要使用句点号。
模板中的过滤器filter:使用管道符|
后台obj=“hello”
模板{{ obj | upper }}即可将小写变大写。lower、first 、capfirst、default等等。
# 1 add : 给变量加上相应的值
# 2 addslashes : 给变量中的引号前加上斜线
# 3 capfirst : 首字母大写
# 4 cut : 从字符串中移除指定的字符
# 5 date : 格式化日期字符串
# 6 default : 如果值是False,就替换成设置的默认值,否则就是用本来的值
# 7 default_if_none: 如果值是None,就替换成设置的默认值,否则就使用本来的值
#value1="abcDe"
{{ value1|upper }}
#全部转换为大写ABCDE
#value2=5
{{ value2|add:3 }}
#5+3=8
#value3='he llo wo r ld'
{{ value3|cut:' ' }}
#去掉空格helloworld
#import datetime
#value4=datetime.datetime.now()
{{ value4|date:'Y-m-d' }}
#格式化日期
#value5=[]
{{ value5|default:'空的' }}
#如果为空,就输出default后的值
#value6='跳转'
{{ value6 }} #整体作为字符串跳转
{% autoescape off %}
{{ value6 }} #字符串作为html代码,即作为了标签
{% endautoescape %}
{{ value6|safe }}
#等同于上面,作为标签
{{ value6|striptags }}
#value7='1234'
{{ value7|filesizeformat }}
#将1234作为数字换算为KB或MB等表示
{{ value7|first }}
#第一个字符
{{ value7|length }}
#长度
{{ value7|slice:":-1" }}
#切片
#value8='http://www.baidu.com/?a=1&b=3' #转换为urlencode编码,http%3A//www.baidu.com/%3Fa%3D1%26b%3D3
{{ value8|urlencode }}
# value9='hello I am yuan'
{{ value9|truncatechars:'6' }}
#按字符进行截取
{{ value9|truncatewords:'2' }} #按字(单词)进行截取
{% csrf_token %}:
当form表单提交的时候,如果中间件,即在settings.py中的MIDDLEWARE中,'django.middleware.csrf.CsrfViewMiddleware',没有被禁用,form提交后页面会出现Forbidden错误
在form表单中增加{% csrf_token %}:
Title
欢迎登陆!!
渲染模板时,会自动生成一个input标签,type为hidden,value是一个随机的长字符串,这个键值对会被后台保存,在前端submit提交时会一同提交,后台(实际上应该是中间件django.middleware.csrf.CsrfViewMiddleware)获取并进行比较,一致就认为是安全的。生成的csrf_token标签,用于防治跨站攻击验证。注意如果在view的index里用的是render_to_response方法,不会生效。
{% url %}: 引用路由配置的地址,{% url "name的值" %},这里name的值就是urls中每个路由的name参数的值,如path(“login/”,views.login,name="abc"),这时,模板中{% url "abc"%}
{% with %}:用更简单的变量名替代复杂的变量名
{% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}
{% verbatim %}: 禁止render,即禁止渲染{% verbatim %}
{{ hello }}
{% endverbatim %}
在渲染后的结果是显示{{ hello }}本身。
{% load %}: 加载标签库
学习这个之前,要了解自定义标签和过滤器
自定义filter和simple_tag:
1)、在app中创建templatetags模块(必须的)
2)、创建任意 .py 文件,如:my_tags.py
需要注意的是自定义标签不能使用在if语句的条件中,而过滤器可以。使用自定义标签和过滤器,必须在文件第一行使用{% load mytag%}引入相应模块,即相应的py文件。
extend模板继承:
问题的提出:多个页面,大部分内容相同,如下,只有中间右半部分内容有差异
Title
页头部分
菜单一内容部分
Title
页头部分
菜单二内容部分
# urls.py
from django.contrib import admin
from django.urls import path,re_path,include
from app01 import views,views_app01
urlpatterns = [
path('new/story/',views.app_story),
path('login/',views_app01.login),
path('index/',views_app01.index),
path('caidan1/',views_app01.caidan1),
path('caidan2/',views_app01.caidan2),
]
# views_app01.py
from django.shortcuts import render,HttpResponse,redirect,reverse
from pathlib import Path
def caidan1(req):
return render(req,"caidan1.html")
def caidan2(req):
return render(req,"caidan2.html")
caidan1.html和caidan2.html代码有大量的重复,解决的方法是将重复部分抽取,写在一个公共的模板上,不同的部分使用{% block 名称%}取代:
Title
页头部分
{% block content %}
{% endblock %}
{% extends "base.html" %}
{% block content %}
菜单一内容部分
{% endblock %}
{% extends "base.html" %}
{% block content %}
菜单二内容部分
{% endblock %}
公共部分的内容只写一份放在base.html中,在caidan1.html和caidan2.html中通过{% extend %}引入母版,将不同的部分用{% block %} {% endblock%}包围起来,以进行替换。
解决该问题的传统做法是使用 服务器端的 includes ,你可以在 HTML 页面中使用该指令将一个网页嵌入到另一个中。 事实上, Django 通过 {% include %} 支持了这种方法。 但是 Django 解决此类问题的首选方法是使用更加优雅的策略,即上面说的—— 模板继承 。
本质上来说,模板继承就是先构造一个基础框架模板,而后在其子模板中对它所包含站点公用部分和定义块进行重载。要比include方法灵活。