Django基础之视图层(views.py)和模板层

目录

  • Django基础之视图层(views.py)和模板层
  • 视图层
    • JsonResponse对象
    • form表单上传文件
    • FBV与CBV 即CBV源码分析
    • django settings源码分析及实际应用
  • 模板层
    • 模板传值
    • 过滤器( | )
      • 常用内置过滤器
      • 其他过滤器(了解)
    • 标签
      • if判断
      • for循环
      • with标签
      • csrf_token标签
    • 自定义过滤器和标签
      • 自定义过滤器
      • 自定义标签
      • 自定义过滤器和标签的区别
    • 模板的继承
    • 模板的导入

Django基础之视图层(views.py)和模板层

视图层

JsonResponse对象

首先提一下,之前讲到过小白必会三板斧(重要三对象):HttpResponse、render、redirect,要注意的是django视图函数必须要返回一个HttpResponse对象

render内部工作原理

from django.template import Template,Context  # 用到两个模块
def index(request):
    res = Template("

{{ user }}

") con = Context({'user':{'username':'jason','pwd':'123'}}) ret = res.render(con) print(ret) return HttpResponse(ret)

用来返回json格式数据的为什么要给前端返回json格式字符串?

因为涉及到一个前后端分离的概念。

前后端分离就是基于json格式传输数据,后端就专门写接口,前端调用你这个接口,就能够拿到一个json格式的字符串,然后前端利用序列化反序列化转换成前端对应的数据类型。

json常用数据类型
数值类型
字符类型
数组 []
自定义对象 {}
undefined与null
布尔值 true false
symbol

前端的序列化 : JSON.stringify() >>>>>>>> json.dumps()

前端的反序列化:JSON.parse() >>>>>>>>> json.loads()

eg:

from django.http import JsonResponse
def index(request):
    data = {'name':'jason好帅哦 我好喜欢','password':123}
    l = [1,2,3,4,5,6,7,8]
    # 后端朝前端返回json格式的字符串方式一
    # res = json.dumps(data,ensure_ascii=False) # 把参数ensure_ascii=False就不会把中文转成json格式
    # return HttpResponse(res)
    # 方式二
    # return JsonResponse(data,json_dumps_params={'ensure_ascii':False})  # 让参数json_dumps_params={'ensure_ascii':False}作用也是不把中文转成json格式
    return JsonResponse(l,safe=False)  # JsonResponse默认只支持序列化字典,如果你想序列化其他类型(json能够支持的类型),需要将safe参数由默认的True改为False。

form表单上传文件

注意事项:

  1. enctype属性需要由默认的urlencoded变成formdata

  2. method属性需要由默认的get变成post

    (目前还需要考虑的是 提交post请求需要将配置文件中的csrf中间件注释)

  3. 如果form表单上传文件 后端需要在request.FILES获取文件数据 而不再是POST里面

eg:

urls.py

from django.conf.urls import url
from django.contrib import admin
from homework import views

urlpatterns = [
    url(r'^file/$', views.file)
]

views.py

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

def file(request):
    if request.method == 'POST':
        print(request.FILES)
        # 获取文件对象
        file_obj = request.FILES.get('myfile')
        print(file_obj.name)
        with open(file_obj.name, 'wb') as f:
            # for line in file_obj:   # file_obj可以直接看成文件句柄f
            #     f.write(line)
            # 也可以用下面一种方法
            for chunk in file_obj.chunks():
                f.write(chunk)
    return render(request, 'file.html')

在templates目录下新建file.html




    
    Title
    
    {% load static %}
    
    


FBV与CBV 即CBV源码分析

FBV(Function Based View):基于函数的视图

CBV(Class Based View):基于类的视图

CBV和FBV在路由匹配上,规则都是一样的,都是路由后面跟的函数的内存地址

在类中写了两个方法 一个叫get一个叫post
为什么前端get请求来就会触发get方法
post请求来就会触发post方法 如何实现的???

这就讲一下CBV源码的实现思路

1、CBV路由层

url(r'^reg/',views.MyReg.as_view())  # 在这必须调用自己写的MyReg类中的as_view方法
    
# as_view的源码
@classonlymethod
def as_view(cls, **initkwargs):
    def view(request, *args, **kwargs):
        self = cls(**initkwargs)  # cls就是我们自己的写的MyReg类
         if hasattr(self, 'get') and not hasattr(self, 'head'):
            self.head = self.get
         self.request = request
         self.args = args
         self.kwargs = kwargs
         # 上面的一通操作 就是给我们自己写的类的对象赋值
         return self.dispatch(request, *args, **kwargs)
         # 对象在查找属性或方法的时候 顺序是什么?  先从自己找 再从产生对象的类中找  再去类的父类中找...
        """也就意味着你在看源码的时候 你一定要牢记上面的话"""
    return view

2、视图层views.py中写上继承view的类

from django.views import View

class MyReg(View):
    def get(self,request):
        return render(request,'reg.html')

    def post(self,request):
        return HttpResponse("我是MyReg类中post方法")

3、在前端发送请求的时候就会进到CBV源码的dispatch方法判断请求方式在不在默认的八个请求方式中。

"""CBV源码最精髓的部分"""
def dispatch(self, request, *args, **kwargs):
    if request.method.lower() in self.http_method_names:  # 判断当前请求方式在不在默认的八个请求方式中
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        # handler = getattr(自己写的类产生的对象,'小写的请求方法(get\post)','获取不到对应的方法就报错')
        # handler就是我们自己定义的跟请求方法相对应的方法的函数内存地址
    else:
        handler = self.http_method_not_allowed
    return handler(request, *args, **kwargs)  # 再调用获取到的方法
        

django settings源码分析及实际应用

django的配置文件有两个
一个是暴露给用户可以自定义配置的
一个是默认的全局配置文件
用户指定了就用用户的
用户没有指定就用默认的

from django.conf import settings
      
settings = LazySettings()

class LazySettings(LazyObject):
    def _setup(self, name=None):
        # os.environ你可以把它看成是一个全局的大字典
        settings_module = os.environ.get(ENVIRONMENT_VARIABLE)  # 从大字典中取值
        # settings_module = 'day59.settings'
        self._wrapped = Settings(settings_module) # Settings('day59.settings')
class Settings(object):
    def __init__(self, settings_module):  # settings_module = 'day59.settings'
        for setting in dir(global_settings):  # 循环获取global_settings文件中所有的名字
            if setting.isupper():  # 在判断名字是否是大写
             # 如果是大写 利用反射 获取到大写的名字所对应的值  不停地添加到对象中
                setattr(self, setting, getattr(global_settings, setting))
                # store the settings module in case someone later cares
                self.SETTINGS_MODULE = settings_module
                mod = importlib.import_module(self.SETTINGS_MODULE)  # 'day59.settings'
                # from day59 import settings
                # mod 指代的就是暴露给用户的配置文件模块名
               
                for setting in dir(mod):  # 循环获取暴露给用户配置文件中所有的名字
                    if setting.isupper():  # 判断是否是大写
                        setting_value = getattr(mod, setting)  # 如果是大写 获取大写的变量名所对应的值
                        setattr(self, setting, setting_value)  # 不停的给对象设置值
        
    

模板层

模板传值

传函数名的时候,会自动加括号调用函数,将函数的返回值展示在html页面上,django模板语法不支持函数传参。

django模板语法在获取容器类型内部元素的值得时候,统一只采用句点符(.)

句点符既可以引用容器类型的元素,也可以引用对象的方法,如下
test.html




    
    Title




{{ msg.upper }}

{{ dic.k1 }}

{{ obj.name }}

{{ li.1.upper }}

{{ li.2.age }}

例如给html页面传了一个列表l,那在前端取值就用

{{ l.1 }}

,就用列表.索引取值。

过滤器( | )

1、优点类似于小的方法,特点:会将|左边的当做过滤器的第一个参数,将
右边的当做过滤器第二个参数。

过滤器类似于python的内置函数,用来把视图传入的变量值加以修饰后再显示,具体语法如下

{{ 变量名|过滤器名:传给过滤器的参数 }}

常用内置过滤器

# 1、default
#作用:如果一个变量值是False或者为空,使用default后指定的默认值,否则,使用变量本身的值,如果value=’‘则输出“nothing”
{{ value|default:"nothing" }}

#2、length
#作用:返回值的长度。它对字符串、列表、字典等容器类型都起作用,如果value是 ['a', 'b', 'c', 'd'],那么输出是4
{{ value|length }}

#3、filesizeformat
#作用:将值的格式化为一个"人类可读的"文件尺寸(如13KB、4.1 MB、102bytes等等),如果 value 是 12312312321,输出将会是 11.5 GB
{{ value|filesizeformat }}

#4、date
#作用:将日期按照指定的格式输出,如果value=datetime.datetime.now(),按照格式Y-m-d则输出2019-02-02
{{ value|date:"Y-m-d" }}  

#5、slice
#作用:对输出的字符串进行切片操作,顾头不顾尾,如果value=“egon“,则输出"eg"
{{ value|slice:"0:2" }} 

#6、truncatechars
#作用:如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾,如果value=”hello world egon 嘎嘎“,则输出"hello...",注意8个字符也包含末尾的3个点
{{ value|truncatechars:8 }}

#7、truncatewords
#作用:同truncatechars,但truncatewords是按照单词截断,注意末尾的3个点不算作单词,如果value=”hello world egon 嘎嘎“,则输出"hello world ..."
{{ value|truncatewords:2 }}

#8、safe
#作用:出于安全考虑,Django的模板会对HTML标签、JS等语法标签进行自动转义,例如value="",模板变量{{ value }}会被渲染成交给浏览器后会被解析成普通字符”“,失去了js代码的语法意义,但如果我们就想让模板变量{{ value }}被渲染的结果又语法意义,那么就用到了过滤器safe,比如value='点我啊',在被safe过滤器处理后就成为了真正的超链接,不加safe过滤器则会当做普通字符显示’点我啊‘
{{ value|safe }}
内置过滤器safe用来前后端取消转义(前端代码并不一定非要在前端写,也可以在后端写好,传递给前端页面)

前端取消转义:|safe

后端取消转义:要先导一个模块:
from django.utils.safestring import mark_safe
sss = "渡我不渡她"
res = mark_safe(sss)
# 再把这个res传到前端

{{ res }}

# 前端就能识别a标签了 # 9、add 给value加上一个add后面的数值 eg:给前端传个n=123,`{{ n|add:100 }}`这个过滤器结果就是n+100,在页面上显示的结果就是223.

其他过滤器(了解)

过滤器 描述 示例
upper 以大写方式输出 {{ user.name | upper }}
add 给value加上一个数值 {{ user.age | add:”5” }}
addslashes 单引号加上转义号
capfirst 第一个字母大写 {{ ‘good’| capfirst }} 返回”Good”
center 输出指定长度的字符串,把变量居中 {{ “abcd”| center:”50” }}
cut 删除指定字符串 {{ “You are not a Englishman” | cut:”not” }}
date 格式化日期
default 如果值不存在,则使用默认值代替 {{ value | default:”(N/A)” }}
default_if_none 如果值为None, 则使用默认值代替
dictsort 按某字段排序,变量必须是一个dictionary {% for moment in moments | dictsort:”id” %}
dictsortreversed 按某字段倒序排序,变量必须是dictionary
divisibleby 判断是否可以被数字整除 `{{ 224 | divisibleby:2 }} 返回 True`
escape 按HTML转义,比如将”<”转换为”<”
filesizeformat 增加数字的可读性,转换结果为13KB,89MB,3Bytes等 `{{ 1024 | filesizeformat }} 返回 1.0KB`
first 返回列表的第1个元素,变量必须是一个列表
floatformat 转换为指定精度的小数,默认保留1位小数 {{ 3.1415926 | floatformat:3 }} 返回 3.142 四舍五入
get_digit 从个位数开始截取指定位置的数字 {{ 123456 | get_digit:’1’}}
join 用指定分隔符连接列表 {{ [‘abc’,’45’] | join:’*’ }} 返回 abc*45
length 返回列表中元素的个数或字符串长度
length_is 检查列表,字符串长度是否符合指定的值 {{ ‘hello’| length_is:’3’ }}
linebreaks 用或
标签包裹变量
{{ “Hi\n\nDavid”|linebreaks }} 返回HiDavid
linebreaksbr
标签代替换行符
linenumbers 为变量中的每一行加上行号
ljust 输出指定长度的字符串,变量左对齐 {{‘ab’|ljust:5}}返回 ‘ab ’
lower 字符串变小写
make_list 将字符串转换为列表
pluralize 根据数字确定是否输出英文复数符号
random 返回列表的随机一项
removetags 删除字符串中指定的HTML标记 {{value | removetags: “h1 h2”}}
rjust 输出指定长度的字符串,变量右对齐
slice 切片操作, 返回列表 {{[3,9,1] | slice:’:2’}} 返回 [3,9] `{{ 'asdikfjhihgie' | slice:':5' }} 返回 ‘asdik’`
slugify 在字符串中留下减号和下划线,其它符号删除,空格用减号替换 `{{ '5-2=3and5 2=3' | slugify }} 返回 5-23and5-23`
stringformat 字符串格式化,语法同python
time 返回日期的时间部分
timesince 以“到现在为止过了多长时间”显示时间变量 结果可能为 45days, 3 hours
timeuntil 以“从现在开始到时间变量”还有多长时间显示时间变量
title 每个单词首字母大写
truncatewords 将字符串转换为省略表达方式 `{{ 'This is a pen' | truncatewords:2 }}返回``This is ...`
truncatewords_html 同上,但保留其中的HTML标签 `{{ '

This is a pen

' | truncatewords:2 }}返回``

This is ...

`
urlencode 将字符串中的特殊字符转换为url兼容表达方式 {{ ‘http://www.aaa.com/foo?a=b&b=c’ | urlencode}}
urlize 将变量字符串中的url由纯文本变为链接
wordcount 返回变量字符串中的单词数
yesno 将布尔变量转换为字符串yes, no 或maybe `{{ True | yesno }}{{ False | yesno }}{{ None | yesno }} ``返回 ``yes``no ``maybe`

标签

if判断,for循环、with标签、csrf_token标签等。

if判断

eg:



{% if xo %}
    

xo有值

{% else %}

xo没有值

{% endif %}
{% if num > 100 or num < 0 %}
    

无效

{% elif num > 80 and num < 100 %}

优秀

{% else %}

凑活吧

{% endif %} #3、if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断。

for循环

#1、遍历每一个元素:
{% for person in person_list %}
    

{{ person.name }}

{% endfor %} #2、可以利用{% for obj in list reversed %}反向循环。 #3、遍历一个字典: {% for key,val in dic.items %}

{{ key }}:{{ val }}

{% endfor %} {% for key in dic.keys %}

{{ key }}

{% endfor %} {% for val in dic.values %}

{{ val }}

{% endfor %} #4、循环序号可以通过{{ forloop }}显示  forloop.counter 当前循环的索引值(从1开始) forloop.counter0 当前循环的索引值(从0开始) forloop.revcounter 当前循环的倒序索引值(从1开始) forloop.revcounter0 当前循环的倒序索引值(从0开始) forloop.first 当前循环是第一次循环则返回True,否则返回False forloop.last 当前循环是最后一次循环则返回True,否则返回False forloop.parentloop 本层循环的外层循环 #5、for标签可以带有一个可选的{% empty %} 从句,在变量person_list为空或者没有被找到时,则执行empty子句 {% for person in person_list %}

{{ person.name }}

{% empty %}

sorry,no person here

{% endfor %}

eg:

urls.py

from django.urls import url
from app01 import views

urlpatterns = [
    url(r'^test/',views.test)
]

view.py

def test(request):
    names=['egon','kevin']
    dic={'name':'egon','age':18,'sex':'male'}

    list1=[]

    return render(request,'test.html',locals())

test.html




    
    Title



{% for name in names %}

{{ forloop.counter0 }} {{ name }}

{% endfor %}
{% for name in names reversed %}

{{ forloop.revcounter0 }} {{ name }}

{% endfor %}
{% for k,v in dic.items %}

{{ forloop.counter }} {{ k }} {{ v }}

{% endfor %}
{% for item in list1 %}

{{ item }}

{% empty %}

sorry,no value here

{% endfor %}

with标签

# with标签用来为一个复杂的变量名起别名,如果变量的值来自于数据库,在起别名后只需要使用别名即可,无需每次都向数据库发送请求来重新获取变量的值
{% with li.1.upper as v %}
    {{ v }}
{% endwith %}

csrf_token标签

# 当用form表单提交POST请求时必须加上标签{% csrf_token%},该标签用于防止跨站伪造请求
{% csrf_token %}

用户名:

密码:

# 具体工作原理为: # 1、在GET请求到form表单时,标签{% csrf_token%}会被渲染成一个隐藏的input标签,该标签包含了由服务端生成的一串随机字符串,如 # 2、在使用form表单提交POST请求时,会提交上述随机字符串,服务端在接收到该POST请求时会对比该随机字符串,对比成功则处理该POST请求,否则拒绝,以此来确定客户端的身份

自定义过滤器和标签

步骤:

  1. 在应用名下面新建一个templatetags文件夹(必须叫这个名字)

  2. 在该文件夹下,新建一个任意名称的py文件

  3. 在该py文件内,固定先写两行代码:

    ​ from django.template import Library

    ​ register = Library()

自定义过滤器

后端my_tag.py

from django.template import Library
register = Library()

@register.filter(name='myplus')
def index(a, b):
return a+b

前端index.html

{% load my_tag %}
{{ 123|myplus:123 }}

这样前端页面显示的结果就是246

自定义标签

my_tag.py

from django.template import Library
register = Library()

@register.simple_tag(name='mysm')
def login(a, b, c, d):
return '%s/%s/%/%s'%(a, b, c, d)

login.html

{% load my_tag %}
{% mysm 1 2 3 4 %}

结果是1/2/3/4

自定义过滤器和标签的区别

#1、自定义过滤器只能传两个参数,而自定义标签却可以传多个参数

#2、过滤器可以用于if判断,而标签不能
{% if salary|my_multi_filter:12 > 200 %}
    

优秀

{% else %}

垃圾

{% endif %}

模板的继承

extends标签和block标签

实现需要在要继承的模板中,通过block划定区域

{% block 区域名字 %}

{% endblock %}

子模板中如何使用

# include有的功能extends全都有,但是extends可以搭配一个block标签,用于在继承的基础上定制新的内容
{% extends '想要继承的模板' %}

{% block 区域名字 %}
    

登录页面

{% endblock %}

一个页面上block块越多,页面的扩展性越高,通常情况下,都应该有三片区域

{% block content %}

{% endblock %}

{% block css %}

{% endblock %}

{% block js %}

{% endblock %}

eg:

Django模版引擎中最复杂且最强大的部分就是模版继承了。我们以先创建一个基本的“骨架”模版,它包含我们站点中的全部元素,并且可以定义多处blocks ,例如我们创建base.html内容如下




    
    
        {% block title %}自定义title名{% endblock %}
    

    
    



{% block sidebar %} 服装城 美妆馆 超市 全球购 闪购 团购 {% endblock %}
{% block content %} base.html页面内容 {% endblock %}

模板base.html 定义了一个可以用于两列排版页面的简单HTML骨架。我们新建子模板index.html的主要工作就是继承base.html然后填充/覆盖其内部的blocks。

{% extends "base.html" %}


{% block title %}
    index页面
{% endblock %}


{% block sidebar %}
    
    {{ block.super }}

    
    拍卖
    金融
{% endblock %}

{% block content %}
    
    

index页面内容

{% endblock %}

我们通过django访问index.html看到内容如下(block标签的内容都完成了替换或更新)




    
    
        index页面
    

    
    



总结与注意

#1、标签extends必须放在首行,base.html中block越多可定制性越强

#2、include仅仅只是完全引用其他模板文件,而extends却可以搭配block在引用的基础上进行扩写

#3、子模板中变量{{ block.super }} 可以重用父类的内容,然后在父类基础上增加新内容,而不是完全覆盖

#4、为了提升可读性,我们可以给标签{% endblock %} 起一个名字 。例如:
    {% block content %}
    ...
    {% endblock content %}  
#5、在一个模版中不能出现重名的block标签。

模板的导入

当你写了一个特别好看的form表单,想在多个页面上都使用这个form表单,你就可以将你写的form表单当做模块的形式导入,导入过来之后,就可以直接展示。

模板导入之include标签,作用:在一个模板文件中,引入/重用另外一个模板文件的内容

语法:{% include '要导入的模板名称' %}

eg:

可以把广告栏写到专门的文件里advertise.html

Panel title

Panel content

Panel title

Panel content

Panel title

Panel content

然后在base.html文件中用include标签引入advertise.html文件的内容




    
    Title
    
    


{% include "advertise.html" %}

你可能感兴趣的:(Django基础之视图层(views.py)和模板层)