Python入门自学进阶-Web框架——4、HttpRequest和HttpResponse及模板

Python入门自学进阶-Web框架——4、HttpRequest和HttpResponse及模板,第1张

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


    欢迎登陆!!

    
{% csrf_token %}

 渲染模板时,会自动生成一个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方法灵活。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/568601.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-09
下一篇 2022-04-09

发表评论

登录后才能评论

评论列表(0条)

保存