django之视图层与模板层


一、伪静态网页

'''其实就是如果一个网页如果是一个静态网页的话 那么浏览器搜索会更容易搜索的到 
而如果一个动态网页想要让浏览器更容易搜索到的话可以在路由匹配的时候修改'''
path('index.html', views.index)  # 只需要在后缀加上.html即可

二、django之视图层

1.函数返回值

'''
如果一个路由匹配上了一个试图函数 
那么这个函数必须要返回一个HttpResponse对象'''

# 我们查看HttpResponse源码发现HttpResponse其实是一个类 然后试图函数返回的是 HttpResponse() 其实就是类加括号 就是产生一个HttpResponse对象
class HttpResponse(HttpResponseBase):
    pass

# 而我们学习三板斧的时候还有render和redirect也可以返回是怎么回事呢?

# 我们查看render函数源码发现其实render是一个函数 而这个函数返回的是一个HttpResponse对象
def render(
    request, template_name, context=None, content_type=None, status=None, using=None
):
    return HttpResponse(content, content_type, status)

# 我们查看redirect发现redirect也是一个函数而该函数返回的是一个三元表达式
def redirect(to, *args, permanent=False, **kwargs):

    redirect_class = (
        HttpResponsePermanentRedirect if permanent else HttpResponseRedirect
    )
    return redirect_class(resolve_url(to, *args, **kwargs))
# 然后三元表达式的两边的源码最后都是继承的HttpResponse对象

# 所以函数返回的是一个HttpResponse对象

2.试图函数返回json格式数据

import json
def index(request):
    user_dict = {'name': 'jason老师', 'pwd': 123, 'hobby': ['read', 'run', 'music']}
    json_dict = json.dumps(user_dict)
    return render(request, 'index.html', {'json_dict': json_dict})
# {"name": "jason\u8001\u5e08", "pwd": 123, "hobby": ["read", "run", "music"]}
'''我们之前将普通数据类型转换json格式类型是需要json模块的 
但是向上述这样直接返回的话 中文会乱码 
而只需要在转json格式类型的时候加上ensure_ascii=False即可'''
json_dict = json.dumps(user_dict, ensure_ascii=False)
# 这个时候网页就可以正常显示中文了

# django有自己的功能转换json格式会简单点  JsonResponse
# 需要导入模块
from django.http import JsonResponse
def index(request):
    user_dict = {'name': 'jason老师', 'pwd': 123, 'hobby': ['read', 'run', 'music']}
    return JsonResponse(user_dict)
# 我们只需要在函数返回值的时候使用JsonResponse 然后把转换json格式的数据类型传入即可
# 但是这个时候的中文也是乱码显示的 该怎么转换呢?
# 我们可以查看源码
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 = {}
        data = json.dumps(data, cls=encoder, **json_dumps_params)
'''我们可以知道JsonResponse其实是一个对象 
然后实例化对象的时候 data是我们传进去的值 而在转换json类型的时候其实也是在调用json方法
而**json_dump_params默认的值为None 所以在做如果这个没值的时候会直接走if分组 而id分支会让这个变量等于一个空字典
而**的用法是把字典中的k:V键值对拆散成K=V的形式当做关键字参数传入
因为是空字典所以没什么作用 但是如果我们用这个方法传入一些值就会有很大的作用  json_dumps_params = {'ensure_ascii':'False'}即可
这个时候就不会再走if分支 就是直接执行下面的代码就会变成 data = json.dumps(data, cls=encoder, ensure_ascii=False) 这个时候中文也就可以显示了'''
def index(request):
    user_dict = {'name': 'jason老师', 'pwd': 123, 'hobby': ['read', 'run', 'music']}
    return JsonResponse(user_dict, json_dumps_params={'ensure_ascii': False})

# 但是如果是非字典类型的类型转json格式 需要加额外的参数 safe=False
# 因为查看源码的时候还有一个if判断
        if safe and not isinstance(data, dict):  # 就是如果safe是True和不是字典类型就会直接报错
            raise TypeError(
                "In order to allow non-dict objects to be serialized set the "
                "safe parameter to False."
            )
# 所以我们可以让safe等于False就可以不走这个if分支了
def index(request):
    user_list = [11, 22, 33, 44, 'jason老师']
    return JsonResponse(user_list, json_dumps_params={'ensure_ascii': False}, safe=False)

3.form表单携带文件

# 之前我们提交input内的数据的时候都是可以直接提交的 但是文件该怎么提交到后端呢?
def index(request):
    if request.method == 'POST':
        file = request.POST.get('file')
        print(file)
    return render(request, 'index.html')

'''如果按照我们正常的获取值操作 那么文件只能获取文件名
文件内容是获取不到的 那这样是没有用的 
我们在后端可以使用request.FILES
前端还需要具备两个条件
1.method属性必须是post请求
2.enctype属性值必须是multipart/form-data
这样才能在后端获取文件内容'''
# request.FILES拿到的相当于是一个字典 所以我们可以使用句点符点出功能
def index(request):
    if request.method == 'POST':
        file_obj = request.FILES.get('file')  # file就是我们在前端form表单下的input中name属性设置的名字
        with open(file_obj.name, 'wb') as f:  # file_obj.name就可以点出文件的名字
            for line in file_obj:
                f.write(line)
    return render(request, 'index.html')
# 视频文件也可以上传

4.FBV和CBV

# FBV和CBV其实就是基于函数和类编写的试图功能
# FBV是基于函数的试图
        def index(request):
            return HttpResponse()
        path('index/', views.index)
# CBV是基于类的试图
# 编写CBV之前要写一些固定代码
    # 视图层:
             from django import views  # 要导入views
            class MyView(views.View):
                def get(self, request):
                    return HttpResponse('我是CBV里面的get方法')
                def post(self, request):
                    return HttpResponse('我是CBV里面的post方法')  # 编写两个固定的get和post功能
    # 路由层
        path('func/', views.MyView.as_view())
# 上面的代码都是固定
"""
CBV会自动根据请求方式的不同匹配类中定义的方法并自动执行
"""    

5.CBV源码分析

# 我们查看CBV的时候发现只有路由层的有点不一样 因为在路由匹配中还要加括号 所以查看源码的入口就是path('func/', views.MyView.as_view())

# 1.有可能是普通函数或则是绑定给类的函数 查看源码可以知道是绑定给类的方法
@classonlymethod  # 可以看作是classmethod
    def as_view(cls, **initkwargs):
          def view(...):
              pass
          return view
'''所以MyView.as_view()在执行的时候是把MyView当做参数传入的'''

# 2.然后对象的查找顺序是 对象本身、产生对象的类、然后是各种父类
    '''所以我们先去MyView查找 没有再去父类View查找是有的  就是上面的as_view函数'''

# 3.path('func/', views.MyView.as_view()) 然后因为函数加括号的执行优先级是最高的所以会先执行as_view函数
'''执行该函数的时候会返回一个view 查看源码知道这个是一个闭包函数名'''
  def as_view(...):
          def view(...):
              pass
          return view
'''所以路由匹配就会变成path('func/', views.view)  所以CBV的路由匹配本质是跟FBV是一样的'''

# 4.路由匹配成功之后就会执行改函数 views.view
        def view(...):
            self = cls()  # 然后因为self 就是类本身 所以是MyView
            return self.dispatch(request, *args, **kwargs)
'''执行改函数发现 改函数返回的是一个对象点名字 
所以我们就要查找这个名称应该在哪 还是名字的查找顺序开始 对象本身、产生对象的类、父类
MyView没有该名称 就去继承类View查找 是有的'''
    def dispatch(...):
        if request.method.lower() in self.http_method_names:
            handler = getattr(
                self, request.method.lower(), self.http_method_not_allowed
            )
        return handler(request, *args, **kwargs)
'''还if判断就是判断CBV发送过的请求把它转换成小写 然后判断在不在http_method_names里面我们可以去该变量名中查找    http_method_names = [
        "get", "post",  "put", "patch", "delete",  "head",  "options", "trace",]因为现在我们接触的只有GET和POST请求所以肯定是在的 就会走该分支
然后该分支下的方法是一个getattr反射方法 该方法就是把请求名称转换成小写 获取该对象内的该请求方法 self又是MyView MyView又是我们自己写的类该类中肯定是有的 然后返回的是该方法加括号会自动执行'''

# 所以CBV就会自动判断请求方式匹配类中定义的方法并执行

三、django之模板层

1.模板语法传值

"""
在模板层中 django提供的模板语法只有两个符号
{{ }}:主要用于变量相关操作(引用)               就是使用数据的时候
{% %}:主要用户逻辑相关操作(循环、判断)    就是使用方法的时候
"""
'''模板语法传值有两种方式'''
# 方式1:指名道姓的传   适用于数量较少的情况下   节省资源
# 后端:
    name = 'jason'
    return render(request, 'ab_temp.html', {'name': name})
# 前端:
{{ name }}  # 就可以直接使用name 是传过来的字典中使用K
# 方式2:关键字locals传参    适用于数量较多的情况下    浪费资源 因为locals会把该函数内的局部名称空间全部传到前端
# 后端
    def index(request):
        name = 'jason'
        age = 18
        gender = 'male'
        return render(request, 'index.html', locals())
# 前端
{{ name }}  {{ age }}  {{gender}} {{request}}
# 前端都可以直接使用

2.传值范围

'''
1.基本数据类型是直接传递使用
2.函数名
    模板语法会自动加括号调用并把返回值展示到页面上
    模板语法不支持传参 会自动忽略有参函数
3.类名
    模板语法会自动加括号产生对象并展示到页面上
4.对象名
    直接传递对象的内存地址  并且具备调用属性和方法的能力
5.文件名
    直接显示文件IO对象'''
# django模板语法针对容器类型的取值 只有一种方法>>>:句点符
   既可以点key也可以点索引 django内部自动识别
   嵌套也可以取值
    data1 = {'info': {'pro': [1, 2, 3, {'name': 'jason', 'msg': '好好学习'}]}}
    {{ data1.info.pro.3.msg }}  # 可以一直点

3.模板语法之过滤器

# 类似python的内置方法
    <p>统计长度:{{ s|length }}</p>  # 计算s的长度
    <p>加法运算:{{ i|add:123 }}、加法运算:{{ s|add:'heiheihei' }}</p>  # 加法运算 可以将字符串拼接
    <p>日期转换:{{ s|date:'Y-m-d H:i:s' }}</p>  # 将日期按照后面的参数展示
    <p>文件大小:{{ file_size|filesizeformat }}</p>  # 将文件的大小按照最接近的单位加上
    <p>数据切片:{{ l|slice:'0:10' }}</p>  # 就是按照索引0到索引10切片取值 顾头不顾尾
    <p>字符截取(三个点算一个):{{ s1|truncatechars:6 }}</p>  # 取六个字符
    <p>单词截取(空格):{{ s1|truncatewords:6 }}</p>  # 取六个单词其实就是按照空格取值
    <p>{{ s1|default:'是否是False' }}</p>  # 就是如果s1是True的话就是展示t1的数据 如果是false那就展示后面的数据
    <p>移除指定的字符:{{ info|cut:'|' }}</p>  # 就是将前面的字符中的|取出掉
    
    后端编写:
    from django.utils.safestring import mark_safe
    script_tag1 = '<script>alert(666)</script>'
    res = mark_safe(script_tag1)
    <p>语法转义:{{ script_tag|safe }}</p>  # 就是执行后端的字符串中前端的标签
    <p>语法转义:{{ res|safe }}</p>  # 两种都可以转义
'''所以有时候html页面上的数据不一定非要在html页面上编写了 也可以后端写好传入'''  

4.模板语法之标签

# 类似于python中的流程控制
# 1.if判断
    {% if 条件 %}  条件一般是模板语法传过来的数据  直接写名字使用即可
        条件成立执行的代码
    {% elif 条件1 %}
           条件1成立执行的代码    
    {% else %}
            条件都不成立执行的代码
    {% endif %}

# 2.for循环
{% for i in s %}
      <p>{{ i}}</p>  # 就可以把后端传来的数据循环出来并使用
{% endfor %}
# for循环还提供了forloop关键字
{'parentloop': {}, 'counter0': 0, 'counter': 1, 'revcounter': 3, 'revcounter0': 2, 'first': True, 'last': False}
'''counter0:就是该数据是第几个索引
counter:就是该数据是该容器中第几个
revcounter和reccounter0就是反着
first:判断该数据是不是第一个    是就返回True
last:判断该数据是不是最后一个  是就返回True'''
# for循环还提供了empty关键字
# 就是判断传入的值是不是为空 如果是空就会自动执行
# 也可以用作循环到最后如果没有值的时候也会自动执行
 ps:针对字典同样提供可keys、values、items方法

5.自定义过滤器、标签、inclusion_tag

"""
如果想自定义 必须先做以下三件事
    1.在应用下创建一个名为templatetags文件夹
    2.在该文件夹创建任意名称的py文件
    3.在该py文件内编写自定义相关代码
        from django.template import Library
        register = Library()
    然后在该py文件下就可以自定义
"""
# 1.自定义过滤器
    @register.filter(name='myfilter')  # filter就是自定义过滤器的名字
    def my_add(a, b):  # 过滤器最多只能传两个参数 不管是自定义还是原本的过滤器
        return a + b  
# 然后在前端必须提前导入
{% load mytag %}  # 然后在下面就可以使用自定义过滤器了
{{ i|myfilter:1 }}  # 也是前面的参数 然后自己传的参数1  让两者相加

# 2.自定义标签函数
    @register.simple_tag(name='mt')  # 标签函数名称
    def func(a, b, c, d):  # 没有参数限制
        return a + b + c + d
# 前端也是必须先导入
{% load mytag %}
{% my_Tag 1 2 3 4 %}  # 参数之间用空格隔开

# 自定义inclusion_tag
@register.inclusion_tag('left.html')  
def func2(n):
    l1 = []
    for i in range(1, n+1):
        l1.append(f'第{i}页')
    return locals()
# 前端:
{% load mytag %}
{% func2 5 %}  # 调用自定义方法
# left.html:
<ul>  # 现在这个页面执行改方法然后将结果放到前端调用的地方
    {% for m in l1 %}
        <li>{{ m }}</li>
    {% endfor %}
</ul>
'''该方法就是后端写好了方法之后 会先作用在另一个html页面(left.html)
然后将渲染的结果放到 前端调用的地方'''

6.模板的继承

# 类似于面向对象的继承:继承了某个页面就可以使用该页面上所有的资源

'''就是如果有一个html页面什么都没有 
但是这个页面继承了一个非常牛逼的一个页面  
那么什么都没有页面就有了该页面的所有功能
然后我们还可以基于这个页面修改一些地方'''

# 我们可以叫继承的页面为子页面  被继承的页面叫主页面

# 我们只需在子页面上这样写
{% extends 主页面.html %}
# 这样子页面就有了主页面所有的功能

# 当我们想在子页面上有自己的一些功能
# 主页面:
{% block content %}
    # 就是将需要修改的地方放到这里面
{% endblock %}  # 固定写法
# 子页面:
{% extends 'home.html' %}
{% block content %}
    # 自己编写html代码 主页面需要修改的地方就会被这个替代 其他还是主页面的样式
{% endblock %}
# 子页面还是可以使用主页面的内容
{{ block.super }}  # 这样子页面就会即拥有自己写的页面内容也有租页面的内容

7.模板的导入

# 类似于将html页面上的局部页面做成模块的形式 哪个地方想用直接导入即可展示

eg:有一个非常好看的获取用户数据的页面 需要在网站的多个页面上使用
    方式1:拷贝多份即可  # 拷贝态浪费时间
    方式2:模板的导入

# 使用方式:
    {% include 'menu.html' %}   # 直接在自己的html上使用即可
    # 想在哪使用就是用这句代码

 

posted @ 2022-09-02 19:40  stephen_hao  阅读(77)  评论(0编辑  收藏  举报