Django-3:django视图层与模板层

16 篇文章 2 订阅

视图层

根据第一章第7节的django请求声明周期流程图来进行理解,先了解如何使用,能看到效果之后,后面再深究。

请求来了之后,先经过urls.py的路由匹配,然后再执行处理返回值的代码,这段代码称做视图类/视图函数,所在的层级在django中叫做视图层

一、“三板斧”

回顾:

"""
HttpResponse
	返回字符串类型
render
	返回html页面 并且在返回给浏览器之前还可以给html文件传值
redirect
	重定向
"""

注意:

  • django视图函数,比如要返回一个HttpResponse对象。

  • 而render和redirect,都是继承了HttpResponse类,那么换句话说就是,render和redirect就是HttpResponse对象。

    可根据查看源码得出结论。

否则报错:

The view 应用名.views.视图函数 didn't return an HttpResponse object. It returned None instead.

二、request对象方法

视图层函数中的request参数:

#如:
from django.shortcuts import render
def index(request):
	# render需要两个参数,一个是request,另外一个是template_name
	return render(request, 'index.html')
  • request:为请求相关的数据对象,里面有很多方法。

    本章节作为基础阶段初次了解,更多方法在后面几期提及。

一、获取请求的方式

格式:

  • request.method
    

返回值:

  • 返回值为请求的方式,并且是全大写的字符串。

示例代码:

  • '''
    views.py
    '''
    from django.shortcuts import render,HttpResponse
    def login(request):
        print(request.method)
        return  render(request,'login.html')
    
    '''
    request.method  返回请求的方式,并且是全大写的字符串,测试结果如下图。
    '''
    
image-20210923003754187

主要应用:

  • 利用 request.method可实现针对不同请求做不同的事情。
'''
当用户为get请求时,返回login.html
当用户为post请求时,返回字符串“提交成功”
'''
def login(request):
    if request.method == 'POST':
        return HttpResponse('提交成功')
    return  render(request,'login.html')

二、获取POST提交的普通数据

获取用户post请求提交的普通数据呢(非文件类型,该类型后续介绍):

request.POST

打印结果示例:

'''
<QueryDict: {
	'Username': ['liuyu'], 
	'Password': ['123'], 
	'Gender': ['boy'], 
	'Birthday': ['2021-09-24'],
	'Area': ['黄埔区'],
	'Hobby': ['Eat', 'Sleep', 'Games']
}>
'''

request.POST将用户post提交的数据封装成一个querydict对象(字典格式,value为列表)

querydict对象两个获取数据的方法

request.POST.get(key)  # 只获取列表最后一个元素,比如get('Hobby'),那么打印的结果就为'Games'
request.POST.getlist()  # 直接将列表取出

二、获取用户get请求提交的

  • 获取用户get请求提交的

    request.GET 
    

    测试时记得把form表单中的method属性改成get
    打印结果与POST是一样的,只不过get请求时在浏览器的URL上,如:
    http://localhost:8000/reg/?Username=liuyu&Password=123&Gender=boy&Hobby=Games

    request.GET返回的同样是querydict对象,也有get和getlist方法

    request.GET.get()  # 只获取列表最后一个元素
    request.GET.getlist()  # 直接将列表取出
    """
    注:
    	get请求携带的数据是有大小限制的,而post请求则没有限制。
    """
    

补充:还有其他常用的方法

request.body  
request.path  # 或  request.path_info   实现的效果都是一样的
request.get_full_path() 
  • request.body 原生的浏览器发过来的二进制数据 ,也包括前端发来的JSON数据
  • request.path 以及 request.path_info ,获取url中路径信息。
  • request.get_full_path() 获取完整的url路径,及问号后面的参数 ,如:/forms_test/?next=123

2.1 前端标签注意事项

HTML文件中表单标签要注意添加name属性和value属性

  • 表单标签的name属性,决定了QueryDict内每个元素的key是多少。

  • 表单标签中,复选框需要添加value属性,否则django将无法收到value

    复选框:

    • select标签(多选)

    • input表的CheckBox类型、radio类型(多选和单选)

前端页面配置好name和value属性后,表单提交后django可以收到的数据为:

image-20210924151206514
<QueryDict: {'Username': ['liuyu'], 'Password': ['123'], 'Gender': ['boy'], 'Birthday': ['2021-09-24'], 'Area': ['黄埔区'], 'Hobby': ['Eat', 'Sleep', 'Games']}>

2.2 post请求时,报错403

在提交post请求会遇到下列情况:
image-20210923002709911

原因:

  • django中间件会在请求来和走的时候,进行校验。
  • 报403是因为不符合CSRF跨域校验

解决办法:

  • 第一种,暂时将校验CSRF的中间件注释

    '''settings.py'''
    
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        # 'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    
  • 第二种,form表单符合校验

    <form class="form-horizontal" action=""  method="post">    
        <!--
    		在form表单下面添加这么一行即可
    	-->
        {% csrf_token %}
    
    </form>
    

详情见django-中间件,再次之前可以暂时跳过。

三、JsonResponse对象

主要作用:

  • 用于返回JSON格式的数据。

    json格式数据的作用:

    • 前后端数据交互,需要使用到json作为过渡,实现跨语言传输数据。
1.前端序列化---(JavaScript)

	JSON.stringify()					
	JSON.parse()


2.后端序列化------(Python)

	json.dumps()
	json.loads()

本章节将从两个角度,详细的介绍JsonResponse对象的使用,以及不使用时,如何返回json格式的数据。

3.1 不使用的情况下

在不使用JsonResponse的情况下,返回给前端JSON格式的数据

import json
def json_test(request):
    dic = {'name':'张三','title':'法外狂徒'}
    dic_json = json.dumps(dic)
    return  HttpResponse(dic_json)

访问查看:

image-20211024171500597

此时我们可以看到,中文数据自动转成了unicode。

如何解决上述转换成unicode码的问题?

给json.dumps传入参数 ensure_ascii=False

import json
def json_test(request):
dic = {'name':'张三','title':'法外狂徒'}
# 转成json格式的时候,原封不动,不把中文转成unicode
dic_json = json.dumps(dic,ensure_ascii=False)
return  HttpResponse(dic_json)

3.2 使用的情况下

如何使用:(基本的使用,后续还是需要再传入参数的,本段代码用于推导)

  • from django.http import JsonResponse
    def json_test(request):
        dic = {'name':'张三','title':'法外狂徒'}
        return  JsonResponse(dic)
    

分析JsonResponse源码

class JsonResponse(HttpResponse):
    def __init__(self, data, encoder=DjangoJSONEncoder, safe=True,
                 json_dumps_params=None, **kwargs):
		'''
			其他代码略
		'''
        if json_dumps_params is None:
            json_dumps_params = {}
        kwargs.setdefault('content_type', 'application/json')
        data = json.dumps(data, cls=encoder, **json_dumps_params)
        super(JsonResponse, self).__init__(content=data, **kwargs
  • 首先继承了HttpResponse,说明可以直接return,然后根据第二行可以得出,JsonResponse其实还是用的json.dumps进行序列化,而源码中并未传入ensure_ascii=False,所以最终在浏览器展示的json数据,中文依旧是Unicode编码。
  • 解决方法:分析源码中json.dumps的形参****json_dumps_params**,在倒数第五行中,也是if判断,通过这里我们可以知道,json_dumps_params在传进来的时候,应该是字典的格式。
  • " ** "在调用dumps函数中是打散的作用,将传入的字典打散,并作为关键字参数,传入dumps函数中。

所以

解决方案就很明显了,在使用JsonResponse时,传入json_dumps_params参数,值为字典格式的ensure_ascii=False即可。

from django.http import JsonResponse
def json_test(request):
    dic = {'name':'张三','title':'法外狂徒'}
    return  JsonResponse(dic,json_dumps_params={'ensure_ascii':False})

注意:

  • 默认只能序列化字典,序列化列表会报以下错:

    In order to allow non-dict objects to be serialized set the safe parameter to False.
    '''
    翻译过来就是:为了允许序列化非 dict 对象,请将安全参数设置为 False。
    '''
    
  • 所以根据要求,那就再传一个参数。

    from django.http import JsonResponse
    def json_test(request):
        lis = ['李四','法内狂徒']
        return  JsonResponse(lis,safe=False,json_dumps_params={'ensure_ascii':False})    
    

四、form表单上传文件

在django中,普通文本数据会被request.POST所接收,但是一些文件,比如图片等,POST就无法接收了,就需要用到request.FILES

代码示例:

<form action="" method="post" enctype="multipart/form-data">
<p>上传图片:<input type="file" name="photo"></p>
<p><input type="submit"></p>
</form>

form表单上传文件类型的数据:

  • 1.method必须指定成post
  • 2.enctype必须换成formdata,否则只能接收到文件名。

视图层(路由层略)

def form_demo(request):
    if request.method == 'POST':
        # request.POST 只能拿到普通的键值对数据,文件不行
        # request.FILES 专门获取文件数据
        print(request.FILES) 
        '''
        <MultiValueDict: {'photo': [<InMemoryUploadedFile: Sh.jpg (image/jpeg)>]}> #photo对应前端name属性
        '''
        photo_obj = request.FILES.get('photo')  # photo为前端name属性
        #文件对象,get只取括号内该key的value最后一个值。与request.POST.get()相同
        
        # 推荐保存的方式:  
        # 文件对象.name 拿到的是上传文件的名称, 由于当前路径下没有该文件所以创建。
        with open(photo_obj.name,'wb') as f:
            for line in photo_obj.chunks(): # 此处的chunks()可写可不写,因为写不写都是逐行读的,但是官方 推荐使用。
                f.write(line)   # 新建文件,名称为上传文件的名称,随后将数据一行一行以类似于拷贝的方式同步到本地。
                
    return render(request,'form.html')
  • request.FILES返回值为MultiValueDict对象,与Queryset对象大致相同,都是列表套字典的形式,还都有get方法来获取数据对象或文件对象。
  • 获取到文件对象之后,可以通过创建空文件+写入文件数据的方式,将用户上传的文件保存在本地。
  • 在保存时,官方推荐使用chunks()方法,但可用可不用,直接对文件对象进行迭代也是一样的。

五、FBV与CBV

上述案例代码中,写的都是基于函数的view,这种就叫做FBV

把view写成基于类的,就叫做CBV

CBV示例:

  • 路由层

    urlpatterns = [
        url(r'^cbv/',views.Cbv_test.as_view()),   #views.类名.as_view()   这里必须要加括号
    ]
    
  • 视图层

    from django.views import View  # 导入
        
    class Cbv_test(View):  # 继承View
    
        def get(self,request):  # 注意要传入request
            return render(request,'test.html')
    
        def post(self,request):  # 注意要传入request
            return HttpResponse('post')
    

    **CBV特点:**能够直接根据请求方式的不同直接匹配到对应的方法执行

  • 模板层

    <form action="" method="post">
        {% csrf_token %}
        <button type="submit">提交</button>
    </form>
    

最终效果:

​ 1.get请求可以获取到test.html页面。

​ 2.该页面提交之后会展示字符串post。

需要整改的

声明时:

classmethod的第一个参数为类本身(cls),正如实例方法的第一个参数为对象本身(self);
staticmethod第一个参数不需要传入cls或self,故staticmethod中是无法访问类和对象的数据的。
调用时:

都可用类名直接调用
也可用实例对象调用(不推荐,没必要)

1

5.1 CBV源码剖析

本章节将一步步详细的推导出CBV的实现原理

5.1.1 一

寻找突破口,注意观察路由

url(r'^cbv/',views.Cbv_test.as_view()),
# 这里的Cbv_test为视图层的类

发现:

  • 类调用了as_view()方法,说明该方法不是@staicmethod修饰的静态方法就是被@classmethod修饰的类方法,并且还是加了括号直接运行的。

  • 查看源码可得,这是个闭包函数。(下图为源码)

    image-20220907171215767
  • 所以,as_view方法的返回值,取决于内部view函数,所以再去研究下view函数的源码。

    此时:

    url(r'^cbv/',views.Cbv_test.as_view()),
    # 就相当于
    url(r'^cbv/',views.view),
    

    那么这个时候就会发现,CBV和FBV,本质上都是一样的。

5.1.2 二

突破口已经打开,开始研究view函数

源码如下:

image-20220907171555589

首先,需要关注下这一行,先明确这个cls和self是什么?

self = cls(**initkwargs)
  • 由于as_view函数是被@classmethod修饰的类方法,由类调用,并将类自动传入,而view函数又是as_view函数内部的闭包函数,可以访问as_view名称空间,所以这个cls就是我们视图层的那个类本身。

    # 此时,self就变成了
    self = 视图函数类(**initkwargs)
    	# 如:
    	self = Cbv_test(**initkwargs)
    
  • 所以,这个self就是我们创建视图类,所实例化的对象。

再来关注下这一行

return self.dispatch()

根据对象属性查找顺序查找下dispatch方法

  • 1.先从对象自己找 。

  • 2.从类中找。

  • 3.从父类中找。

很明显,最终在父类中才能找到dispatch方法,所以后续要再研究dispath方法的源码。

5.1.3 三

dispatch源码:(CBV的核心)

image-20220907202732748

首先要明确,该方法的self是谁?

  • 是我们视图类,所实例化的对象。

来看这一行

if request.method.lower() in self.http_method_names:
  • request.method.lower() 这个好理解,访问的方式,由于返回的是大写的字符串格式,所以再加上lower方法变成小写。

  • self.http_method_names ? 这个是什么

    image-20220907203011400

    由于该属性对象本身没有,类中也没有,最后在继承的类中,找到了该属性

    http_method_names为列表,其实就是几种HTTP请求方式。

所以,if判断的这行代码,其实就是在做HTTP请求类型的校验

回到源码,接着往下分析

if request.method.lower() in self.http_method_names:
    handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
    handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)

第二行分析:

  • 首先,当访问的HTTP请求类型正常时,就去判断我们由类,示例出来的对象,有没有属性或方法名,等于这个请求方式名的。 如:请求方式是get,那么就去利用反射,查询对象是否具有get属性或方法,如果没有,就返回后面自定报错, 如果有,那么就返回属性的值,或方法的内存地址。

return分析:

  • 此时的handler,就等于我们类中的get或者post方法的内存地址,至于是get还是post,还是其他的,取决访问的HTTP请求格式。

  • 后续加括号调用,就完成了根据不同请求,执行不同的视图函数。

模板层

六、模版语法传值

模版语法的作用:

  • 可以传递的后端python数据类型

格式:

  • {{}}:变量相关

    如:

    {{ list }}
    
  • {%%}:逻辑相关

    {% if i in user_dict %}
    
    {% endif %} 
    

代码示例

后端(python):
    dic = {'name':'刘','age':'21'}
    lis = [1,2,3]
    tup = (2,2,2)
    set = {1,2,3}

    # return render(request,'index.html',{})  # 一个个传
    return render(request,'index.html',locals())

前端:
	<p>{{ dic }}</p>
    <p>{{ lis }}</p>
    <p>{{ tup }}</p>
    <p>{{ set }}</p>

注意事项:

'''
模板语法也可以传入函数、类和对象。

	1.当传入函数时,会自动加括号执行,但不可以传参数。
	2.当传入类名时,也会加括号实例化
	3.当传入对象或对象.方式时,模板语法内部能够自动判断出当前的变量名是否可以加括号调用,如果可以就会自动执行。
		3.1 当对象具有__str__方法或__repr__方法时,模板语法会打印对象,从而执行内部的代码。      (__str__:打印对象时执行)
		3.2 当对象具有__call__方法时,模板语法会加括号执行。   (__call__: 当对象加括号被执行时,执行。)
	4.需要让前端接收数据时,应该有返回值,不然会None
'''
<p>{{ func }}</p> 		# 传递函数名会自动加括号调用 但是模版语法不支持给函数传额外的参数:
<p>{{ MyClass }}</p> 	 # 传类名的时候也会自动加括号调用(实例化)


# django模板语法,内部能够自动判断出当前的变量名是否可以加括号调用,如果可以就会自动执行。
<p>{{ obj.get_self }}</p>
<p>{{ obj.get_func }}</p>
<p>{{ obj.get_class }}</p>

另外,django模版语法的取值 是固定的格式 只能采用“句点符” .

'''
	也就是说,当前端需要获取列表某个元素、字典某个k的v,都可以使用“.”

'''

	lis[0]   --->     lis.0
	dic['name'] ---> dic.name
	dic['addr'][3]  ---> dic.addr.3
# 在python中利用[]或其他方式取值,到了模板语法中,都要变成.的形式。
    
    
    
'''可以混用'''
<p>{{ d.hobby.3.info }}</p>   # 字典d中,在key为hobby的value中,取索引为3且key为info的值(value)

七、模板语法-过滤器

作用:

  • 类似于python的内置函数,可以对传给Html的数据进行**“二次处理”**,比如统计长度等。

格式:

{{ 变量名 | 过滤器:可选参数 }}

过滤器有六十多个,本章节暂时只介绍部分。

汇总一览:

过滤器名称作用
length统计长度
default当数据的布尔值为False时,返回自定义的默认值,反之则返回数据本身的值
filesizeformat文件大小单位换算,自动算出为多少KB、MB、GB等。
date日期格式化
slice切片操作(支持步长)
truncatechars切取字符
truncatewords按照空格切来切取字符
cut移除特定的字符
join字符串拼接操作(列表转字符串)
add字符串的拼接,或相加。
safe转义(后端使用mark_safe之后,就不需要在前端转义了)

length

  • 作用: 统计长度

    # 路由:
    url(r'test/',views.test),
    
    # 视图:
    def test(request,*args):
        str1 = 'liuyu'
        age = 22
        msg = {'school':'中国地质大学','college':'信息工程学院'}
        return render(request,'test.html',locals())
    
    # 模板
    {{ str1|length }}  # 结果:5
    {{ age|length }}  # 结果:0
    {{ msg|length }}  # 结果:2
    
    '''
    数字无法统计长度,所以结果为0
    字典统计有多少key,所以结果为2
    '''
    

default

  • 作用:当数据的布尔值为False时,返回自定义的默认值,反之则返回数据本身的值

    # 视图
        is_del1 = True
        is_del12 = False
        return render(request,'test.html',locals())
    # 模板
    <p>
       {{ is_del1|default:'已删除' }}  #结果: True
    </p>
    
    <p>
        {{ is_del2|default:'已删除' }} #结果: 已删除
    </p>
    
    '''
    当变量名的值,转换为bool类型为False时,返回自定义值。
    '''
    

filesizeformat

  • 作用:单位换算,自动算出为多少KB、MB、GB,如:2048=2.0KB。

    # 视图(多余代码略)
    size = 10012300
    
    # 模板
    {{ size|filesizeformat }}  # 结果:9.5 MB
    

date

  • 作用:用于格式化时间

    # 视图(多余代码略)
    import  datetime
    current_time = datetime.datetime.now()    # 结果:2021-11-03 22:11:16
    
    # 模板
    {{ current_time }}  # 结果:Nov. 3, 2021, 10:40 p.m.
    {{ current_time|date:'Y-m-d H:m:s' }}  # 结果:2021-11-03 22:11:16
    
    '''
    python后端的时间格式为: 2021-11-03 22:11:16
    直接在前端使用: Nov. 3, 2021, 10:40 p.m.
    	所以为了时间看起来更直观,最好使用data过滤器,将事件进行格式化。
    '''
    

slice

  • 作用:切片,可支持步长

    # 视图(多余代码略)
    lis = [1,2,3,4,5]
    
    # 模板
    {{ lis|slice:'0:4:2' }}  # 结果:[1,3]
    
    '''
    取索引0到4  即:[1,2,3,4]
    然后取每隔2-1位的值,即:[1,3]
    '''
    

truncatechars

  • 作用:取x个字符

    # 视图(多余代码略)
    str1 = '又是元气满满的一天'
    
    
    # 模板
    {{ str1|truncatechars:4 }}   # 结果:又...
    
    '''
    该方法会强制的加上三个点'...'
    	所以,当{{ str1|truncatechars:1 }}时,结果:...
    	     当{{ str1|truncatechars:4 }}时,结果:又...
    '''
    

truncatewords

  • 作用:按照空格切片(该方法不会强制加上三个点’…')

    # 视图(多余代码略)
    str1 = '又 是 元 气 满 满 的 一 天'
    
    # 模板
    {{ str1|truncatewords:4 }}	 # 结果:又 是 元 气 ...
    

cut

  • 作用:移除特定的字符

    # 视图(多余代码略)
    str1 = '又 是 元 气 满 满 的 一 天'
    str2 = '1-2-3'
    
    # 模板
    <p>{{ str1|cut:' ' }}</p>  	 # 结果:又是元气满满的一天
    <p>{{ str2|cut:'-' }}</p>    # 结果:123
    

join

  • 作用:列表转字符串,拼接

    # 视图(多余代码略)
    lis = ['A','B','C']
    
    # 模板
    {{ lis|join:'-' }}  # 结果:A-B-C
    

add

  • 作用:字符串相加或者拼接

    # 视图(多余代码略)
    str1 = '123'
    str2 = 'abcd'
    
    # 模板
    <p>{{ str1|add:'456' }}</p>  # 结果:579
    
    <p>{{ str2|add:'efg' }}</p>  # 结果:abcdefg
    
    '''
    当变量的值,和add参数的值,都是数字组成时,add表示相加运算
    反之则是字符串拼接。
    '''
    

safe

  • 作用:转义,将字符串中的标签变得有意义

    # 视图(多余代码略)
    info = '<h1>转义</h1>'
    
    # 模板
    {{ info|safe }}  # 结果: h1标题样式的 转义  二字
    
    '''
    在不使用safe时,前端会原封不动的将字符串中的数据展示在页面上,HTML格式的字符串,并不会被当做代码去渲染在页面上。
    '''
    

    扩展: python如何实现在后端完成转义

    # 视图
    def test(request,*args):
        from django.utils.safestring import mark_safe
        res = mark_safe('<h1>标题</h1>')
        return render(request,'test.html',locals())
    
    # 模板
    {{ res }}
    
    '''
    python完成了转义的工作,到了HTML页面,就不需要再使用safe过滤器了。
    '''
    

7.1 自定义过滤器

分三步走:

  • 第一:在应用下创建一个名字”必须“叫templatetags文件夹,具体是app01还是02,要看是哪个应用的视图做处理

  • 在该文件夹内创建“任意”名称的py文件,如;myfilter.py、mytag.py等

  • 在该py文件内"必须"先书写下面两句话(单词一个都不能错)

    from django import template		
    register = template.Library() # 注意加括号
    
image-20211108163857721

代码示例:

'''
templatetags/mytag.py    (该py文件的名称可自定义)
'''
from django import template
register = template.Library() # 注意加括号

# tag1_test为自定义过滤器的名字,后续调用使用的就是该名字
@register.filter(name='tag1_test')  
def my_count(v1, v2):	# 函数名my_count可任意取
    return v1 + v2

# tag2_test作为过滤器的唯一标识
@register.filter(name='tag2_test')  
def my_count(v1, v2):  # 函数名重复也无所谓的,因为调用时作为唯一标识的不是它,而是上面指定的过滤器名字
    return v1 - v2
'''
views.py
'''
def test(request):
    int1 = 998
    int2 = 1999
    return render(request,'test.html',locals())
'''
test.html
'''
{# 自定义过滤器 #}
{% load mytag %}  # 引入,mytag为templatetags文件夹下的py文件。
<p>{{ int1|myfilter1:int2 }}</p>
<p>{{ int1|myfilter2:int2 }}</p>

'''
int1,会被过滤器的第一个形参所接收,所以{{ int1|myfilter1:int2 }} = int1+int2

过滤器也就只支持两个参数。
'''

所以最终实现的效果应该是,int1+int2 以及 int1-int2,浏览器展示:

image-20211108170338162

八、模板语法-标签

普通for循环

<ul>
{% for user in user_list %}   <!--直接打for,基本的格式会tab补全出来-->
    <li>{{ user.name }}</li>
{% endfor %}
</ul>

forloop

观察下列代码的结果

# 视图
def test(request,*args):
user_list = ['张三','李四','王五','马六','侯七']
return render(request,'test.html',locals())

# 模板
<ul>
{% for user in user_list %}
  <li>
      {{ forloop }}
  </li>
{% endfor %}
</ul>


# 输出结果:
'''
{'parentloop': {}, 'counter0': 0, 'counter': 1, 'revcounter': 5, 'revcounter0': 4, 'first': True, 'last': False}
{'parentloop': {}, 'counter0': 1, 'counter': 2, 'revcounter': 4, 'revcounter0': 3, 'first': False, 'last': False}
{'parentloop': {}, 'counter0': 2, 'counter': 3, 'revcounter': 3, 'revcounter0': 2, 'first': False, 'last': False}
{'parentloop': {}, 'counter0': 3, 'counter': 4, 'revcounter': 2, 'revcounter0': 1, 'first': False, 'last': False}
{'parentloop': {}, 'counter0': 4, 'counter': 5, 'revcounter': 1, 'revcounter0': 0, 'first': False, 'last': True}
'''

对应关系如下表:

forloop.counter当前循环的索引值(从1开始)
forloop.counter0当前循环的索引值(从0开始)
forloop.revcounter当前循环的倒序索引值(从1开始)
forloop.revcounter0当前循环的倒序索引值(从0开始)
forloop.first当前循环是不是第一次循环(布尔值)
forloop.last当前循环是不是最后一次循环(布尔值)
forloop.parentloop本层循环的外层循环

forloop可以结合if判断,比如是否是第一次循环,是否为第最后一次循环,等。

if判断

{% if 条件 %}
    <p>XXX</p>
{% elif 条件 %}
    <p>XXX</p>
{% else %}
    <p>XXX</p>
{% endif %}

for与if可混合使用

{% for name in lis %}
	# 第一次循环时执行
    {% if forloop.first %}
        <p>第一位同学为:{{ name }}</p>
	# 最后一次循环时执行
    {% elif forloop.last %}
        <p>最后一位同学为:{{ name }}</p>
    {% else %}
        <p>{{ name }}</p>
    {% endif %}
{% endfor %}

模板语法处理字典的方法

  • 迭代取出字典的key

    <ul>
    	#     {% for user in user_dict.keys %}
        {% for user in user_dict %}
            <li>{{ user }}</li>
        {% endfor %}
    </ul>
    
    '''
    因为默认就是key,所以可以不在.keys
    '''
    
  • 迭代取出字典的value

    <ul>
        {% for user in user_dict.values %}
            <li>{{ user }}</li>
        {% endfor %}
    </ul>
    
  • 迭代取出字典的key和value

    <ul>
        {% for user in user_dict.items %}
            <li>{{ user }}</li>
        {% endfor %}
    </ul>
    
    '''
    元祖格式,例如:
    	('name', 'liuyu')
    	('age', 22)
    '''
    

8.1 自定义标签

作用:

  • 类似于函数,后端定义之后,前端传入参数调用。
  • 相比过滤器来说,自定义标签是可以像函数一样,传入多个参数的。

格式:

{% 自定义标签名 '参数1' '参数2' '参数3' '参数4' ...... '参数x'  %}

使用:

后端定义

# templatetags/mytag.py文件
@register.simple_tag(name='tag1_test')
def my_tag(name,year,month,day):
return f'姓名:{name},生日:{year}-{month}-{day}'

前端引入使用

# 前端html文件
{% load mytag %}
<p>{% tag1_test 'liuyu' '2000' '7' '19'  %} </p>

类似于linux系统敲命令一样,直接输入命令(函数)+参数,如 ls /home

九、自定义inclusion_tag

作用:

  • 当html某一个地方的页面,需要传参数才能够动态的渲染出来,并且在多个页面上都需要使用到该局部,那么就考虑将该局部页面做成inclusion_tag形式

代码示例:

定义inclusion_tag

from django import template
register = template.Library()

@register.inclusion_tag('menu.html')
def menu(num):
data = [f'第{i}项' for i in range(1,num+1)]

# 将值,传给menu.html
return locals()

index页面

# mytag为自定义模板语法文件的文件名
{% load mytag %}


# 调用menu函数,传入值为10
{% menu 10 %}

menu页面

<ul>
{% for i in data %}
  <li>{{ i }}</li>
{% endfor %}
</ul>

流程:

  • 浏览器访问index页面,经过路由层、视图层将Index.html返回。
  • 在index页面中,引入了mytag,并且后续的{% menu 10 %},调用了模板语法中自定义menu函数,并且传入参数 10
  • mytag.py文件,执行menu函数,利用index.html中传入的10,做一些处理,最后将数据传值到menu.html
  • menu.html获取到数据后完成渲染,但,是渲染在了调用它的位置,也就是index.html上
'''
所以最后的效果就是:
	index.html中{% menu 值 %},后面的参数值为多少,最后展示在页面上的li标签就有多少。
'''

十、总结

@register.filter(name='xxx')   # 自定义过滤器
@register.simple_tag(name='xxx') # 自定义标签
@register.inclusion_tag('xxx.html') # 自定义inclusion_tag

'''
在使用方式上:
'''
	1.自定义过滤器:
    	{% load xxx %}  # 先引入
		{{ a|xxx:b }} # a和b将作为参数传入到自定义过滤器函数的参数。
        
	2.自定义标签
    	{% load xxx %} # 名称根据实际情况来
		{% xxx '参数1' '参数2' '参数3' %} 
        
    3.自定义inclusion_tag
    	定义函数,然后在模板语法中传入参数使用

十一、模版的继承

作用:

  • 可以实现页面的局部变化,如点击导航条,只有面板区域进行变化等。

使用方法

'''
子页面继承主页面
'''
{% extends 'home.html' %}    #home.html为主页面,该行表示子页面继承主页面,继承之后与主页面一模一样。

由于继承了之后,子页面跟模版页面(主页面)长的是一模一样的,所以需要在主页面上提前划定可以被修改的区域

'''
主页面
'''
{% block content %}
	模版HTML代码
    <h1>主页面</h1>
{% endblock %}

# 这里的content代表这一块区域的HTML代码,后续子页面可以对其进行自定义修改。
'''
子页面:
	 修改模板指定区域的代码
'''

{% block content %}
	子页面内容	
    <h1>子页面</h1>
{% endblock %}

子页面和主页面都书写完毕之后,当我们触发请求,执行视图函数并返回子页面时,不会再重新新建标签页,而是在主页面中提前划定好的区域做页面内容展示区域。

案例:

  • 页面登陆与注册局部刷新。

一、布局如下:

image-20211112174933198

二、在主页面上划定区域

  • 划定两个区域(其实一个区域就够了,但为了加深印象,此处划了2个)
image-20211112175646327

三、子页面继承,并修改区域独有的代码

'''
子页面 login.html
'''
{% extends 'home.html' %}  # 继承主页面

{% block title %}  # 修改主页面划定的区域代码。
    <h1>登陆</h1>
{% endblock %}


# 修改主页面中的标签区域代码,换成表单。
{% block input %}
<form class="form-horizontal" action="" method="post">
    <div class="form-group">
        <label for="inputEmail3" class="col-sm-2 control-label">Uname</label>
        <div class="col-sm-10">
          <input type="text" class="form-control" id="inputEmail3" placeholder="Uname" name="username" >
        </div>
    </div>
    <div class="form-group">
        <label for="inputPassword3" class="col-sm-2 control-label">Password</label>
        <div class="col-sm-10">
          <input type="password" class="form-control" id="inputPassword3" placeholder="Password" name="password">
        </div>
    </div>
    <div class="form-group">
        <div class="col-sm-offset-2 col-sm-10">
          <button type="submit" class="btn btn-success ">Sign in</button>
        </div>
    </div>
</form>
{% endblock %}
image-20211112180218824
  • 在主页面中,左侧面板的两个Li标签,也就是登陆和注册按钮,都是a链接标签,所以点击之后会朝后端发送请求,路由通过之后,执行视图函数,此时只要视图函数返回的HTML页面就是“子页面”时,则会在原来划定的区域内展示页面,如上图所示。

11.1 其他

一般情况下模版页面上应该至少有三块可以被修改的区域

  • css区域
  • html区域
  • js区域
  {% block css %}
  {% endblock %}
  
  {% block content %}
  {% endblock %}
  
  {% block js %}
  {% endblock %}
  # 每一个子页面就都可以有自己独有的css代码 html代码 js代码
    
"""
一般情况下 模版的页面上划定的区域越多 那么该模版的扩展性就越高
但是如果太多 那还不如自己直接写
"""

十一、模版的导入

"""
将页面的某一个局部当成模块的形式
哪个地方需要就可以直接导入使用即可
"""
{% include 'xxxx.html' %}
  • 导入模板(HTML)之后,会在导入的地方,插入模板内的所有代码。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,关于Django的模型、视图模板,我可以做个简单的介绍: Django是一个MVC框架,其中的M、V、C分别指的是模型视图和控制器(其中控制器Django中对应的是URLconf),而这里的MVC与传统的MVC有所不同,更倾向于MVT的架构,即模型(Model)、视图(View)和模板(Template)。 - 模型(Model):模型主要是把应用中需要用到的数据以类似面向对象的方式进行定义、管理和操作,通常对应database中的表。Django中的ORM(Object-relational mapping)对开发者屏蔽了底的SQL操作,开发者可以直接以Python语言去操作数据库,而不需要关心底SQL的实现细节。 - 视图(View):视图最主要的作用是处理用户的请求,响应相应的结果给用户。一般来说,视图会从数据库、缓存等数据源中获取数据,然后将结果进行组装,返回HttpResponse给用户。Django视图可以通过函数或者类的方式来定义,对外提供一个可被URLconf调用的可调用对象。 - 模板(Template):模板视图生成响应结果的主要组成部分,可以理解为一个动态生成的HTML页面,其中包含了数据展示、控制逻辑、页面渲染等元素。Django中的模板提供了超过100个内置的指令和过滤器,开发者可以非常方便的实现模板的渲染和页面的实现。 总结一下,模型主要和数据打交道,视图主要和操作和生成Http Response联系在一起,模板主要负责页面的渲染和数据展示。希望这个介绍可以帮到你。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿煜酱~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值