04 视图层FBV和CBV 源码 setting源码 模板层

一、视图层

1.小白必会三板斧

1.HttpResponse
2.render
3.redirect
django视图函数必须要给返回一个HttpResponse对象(看源码就知道了,三板斧本质都是)

详细内容:https://www.cnblogs.com/xp1315458571/p/11524988.html#_label5

2.前后端分离

前端一个人干
后端另一个干

后端只负责产生接口,前端调用该接口能拿到一个大字典(后端用字典,字典json传给前端,前端接收字典,反json转成自定义对象)
后端只需要写一个接口文档 ,即说明书,里面描述字典的详细信息以及参数的传递

 

只要涉及到数据交互,一般情况下都是用的json格式

JSON.stringify()   ——>对应后端 :json.dumps()

JSON.parse() ——>  对应后端:json.loads()

 

3.JsonReponse

后端数据怎样传到前端呢?用到JsonReponse

#传字典到前端

from django.http import JsonResponse
import json
def index(request):
    data = {'name':'jason好帅哦 我好喜欢','password':123}
    # res = json.dumps(data,ensure_ascii=False) #告诉json不要瞎鸡儿转码,我写什么你加双引号成json类型就行了,不写的话汉字在前端会转成二进制
    # return HttpResponse(res)
    return JsonResponse(data,json_dumps_params={'ensure_ascii':False})  #代替上面两句

#传其他类型到前端

from django.http import JsonResponse

def index(request):
    l = [1,2,3,4,5,6,7,8]
    return JsonResponse(l,safe=False)  # 如果返回的不是字典 只需要修改safe参数为false即可    

4.form表单上传文件

form表单上传文件需要注意的事项
    1.enctype需要由默认的urlencoded变成formdata
    2.method需要由默认的get变成post
    (目前还需要考虑的是 提交post请求需要将配置文件中的csrf中间件注释)
    3.如果form表单上传文件 后端需要在request.FILES获取文件数据 而不再是POST里面

 

 

 #上传文件html

 

 #后端代码

def upload(request):
    """
    保存上传文件前,数据需要存放在某个位置。默认当上传文件小于2.5M时,django会将上传文件的全部内容读进内存。从内存读取一次,写磁盘一次。
    但当上传文件很大时,django会把上传文件写到临时文件中,然后存放到系统临时文件夹中。
    
    """
    if request.method == "POST":
        # 从请求的FILES中获取上传文件的文件名,file为页面上type=files类型input的name属性值
        filename = request.FILES["file"].name
        # 在项目目录下新建一个文件
        with open(filename, "wb") as f:
            # 从上传的文件对象中一点一点读
            for chunk in request.FILES["file"].chunks():
                # 写入本地文件
                f.write(chunk)
        return HttpResponse("上传OK")
上传文件后端代码

 

5.FBV和CBV

视图函数并不只是指函数 也可以是类

只要是处理业务逻辑的视图函数都需要接受一个request参数
FBV(基于函数的视图) 面向函数式编程
CBV(基于类的视图) 面向对象式编程

# render内部原理(FBV)

render返回一个html页面 并且还能够给该页面传数据

 

from django.shortcuts import render,HttpResponse
from django.template import Template,Context
# FBV
def index(request):
    temp = Template('<h3>{{ user }}</h3>')
    con = Context({"user":{"name":'jason',"password":'123'}})
    res = temp.render(con)
    print(res) #<h3>{&#39;name&#39;: &#39;jason&#39;, &#39;password&#39;: &#39;123&#39;}</h3>
    return HttpResponse(res)
Views

# CBV

问题:基于CBV的视图函数

#urls.py中:
    url(r'^login/',views.MyLogin.as_view())
#views.py中:
    from django.shortcuts import render, HttpResponse
    from django.template import Template, Context
    from django.views import View
    class MyLogin(View):  # 这里继承View,下面才接收request参数
        def get(self,request):  # 这里接收request
            print("from MyLogin get方法")
            return render(request,'login.html')
        def post(self,request):
            return HttpResponse("from MyLogin post方法")

 

【问题】FBV中我们通过判断request.method是get还是post来进行不同的操作,那么CBV中get请求来就会自动走走类里面get方法,post请求来就会走类里面post方法 为什么???

#研究方向 
#1.从url入手
url(r'^login/',views.MyLogin.as_view())  #由于函数名加括号执行优先级最高,所以这一句话一写完会立刻执行as_view()方法,

#进入as_view()源码 @classonlymethod #这里是类绑定 def as_view(cls, **initkwargs): # cls就是我们自己的写的类 MyLogin def view(request, *args, **kwargs): self = cls(**initkwargs) # 实例化产生MyLogin的对象 self = MyLogin(**ininkwargs) 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) # dispatch返回什么 浏览器就会收到什么 # 对象在查找属性或者方法的时候 你一定要默念 先从对象自己这里找 然后从产生对象的类里面找 最后类的父类依次往后 return view

#通过源码发现url匹配关系可以变形成
url(r'^login/',views.view)  # FBV和CBV在路由匹配上是一致的 都是url后面跟函数的内存地址
#2.当浏览器中输入login 会立刻触发view函数的运行,

#dispatch返回什么浏览器就受到什么,我们进入dispatch源码
def dispatch(self, request, *args, **kwargs): # 我们先以GET为例 if request.method.lower() in self.http_method_names:
          # http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] # 反射获取我们自己写的类产生的对象的属性或者方法 # 以GET为例 handler = getattr(self,'get','取不到报错的信息') # handler = get(request) handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs) # 直接调用我们自己的写类里面的get方法 # 源码中先通过判断请求方式是否符合默认的八个请求方法 然后通过反射获取到自定义类中的对应的方法执行

二、django中settings源码解析

1.问题:为什么配置中所有变量名必须大写才能生效?

前提:
1.django通常都会有两个配置,除了暴露给用户一个settings.py配置文件之外  自己内部还有一个默认的配置文件,用户配置了就用用户的,没有配置就用默认的
2.
django中暴露给用户的配置settings.py默认在与项目同名的文件夹中
 django项目内部默认的配置文件 from django.conf import global_settings
3.可以直接导入与项目同名的文件夹中的配置文件 : from 项目名 import settings
  但是通常我们在django中使用配置文件一般都是按照一下方式导入:from django.conf import settings  (自定义+全局的 这样理解吧)

4.django的启动入口是manage.py 
from django.conf import settings #这样导入配置,点进settings
            
class LazySettings(LazyObject):
  def _setup(self, name=None):
       # os.environ你可以把它看成是一个全局的大字典
       settings_module = os.environ.get(ENVIRONMENT_VARIABLE)  # 从大字典中取值键为DJANGO_SETTINGS_MODULE所对应的值:day54.settings
       # settings_module = 'day54.settings'
       self._wrapped = Settings(settings_module)  # Settings('day54.settings')    
  settings = LazySettings()  # settings是lazysettings产生的一个单例对象,我们继续进入lazysettings源码      
settings_module = os.environ.get(ENVIRONMENT_VARIABLE) #这是从全局大字典中取值,那么ENVIRONMENT_VARIABLE是什么呢?点进去会跳到顶部
ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE" #这个字符串是什么呢?我们开始去启动文件中查找
            
            import os
            import sys
            if __name__ == "__main__":
                os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day54.settings")
                # django在启动的时候 就会往全局的大字典(environ)中设置一个键值对  值是暴露给用户的配置文件的路径字符串
                
            
            self._wrapped = Settings(settings_module)# 继续点进Settings
            
            
            class Settings(object):
                def __init__(self, settings_module):  # settings_module = 'day54.settings'
                    for setting in dir(global_settings):  # django全局配置文件
                        # dir获取django全局配置文件中所有的变量名
                        if setting.isupper():  # 判断文件中的变量名是否是大写 如果是大写才会执行/生效
                            setattr(self, setting, getattr(global_settings, setting))  # 给最初我们导入的settings对象设置键值对
                            # 给settings对象设置键值对  settings[配置文件中大写的变量名] = 配置文件中大写的变量名所对应的值
                self.SETTINGS_MODULE = settings_module  # 'day54.settings'

                    mod = importlib.import_module(self.SETTINGS_MODULE)  # mod = 模块settings(暴露给用户的配置文件)
                    for setting in dir(mod):  # for循环获取暴露给用户的配置文件中所有的变量名
                        if setting.isupper():  # 判断变量名是否是大写
                            setting_value = getattr(mod, setting)  # 获取大写的变量名所对应的值
                            setattr(self, setting, setting_value)  # 给settings对象设置键值对
                            """
                            d = {}
                            d['username'] = 'jason'
                            d['username'] = 'egon'
                            用户如果配置了就用用户的
                            用户如果没有配置就用系统默认的
                            其实本质就是利用字典的键存在就是替换的原理 实现了用户配置就用用户的用户没配置就用默认的
                            """

2.补充 importlib 模块

 

 

 

 3.根据settings源码原理自定义一个实例

要求:正常启动输出‘我是自定义配置’,去掉自定义配置则输出‘我是默认配置’

 

  

 

 

 三、模板层

1.基本语法

只需要记两种特殊符号:

{{  }}和 {% %}

变量相关的用{{}},逻辑相关的用{%%}。
1.views中传值:
render(request, "template_test.html", {"l": l, "d": d, "person_list": person_list})

2.html模板中就收使用:
{
# 取列表l中的第一个参数 #} {{ l.0 }} {# 取字典中key的值 #} {{ d.name }} {# 取对象的name属性 #} {{ person_list.0.name }} {# .操作只能调用不带参数的方法 #} {{ person_list.0.dream }}

{# 我是模板语法的注释方法#}

2. 视图给模板传值

1.基本的数据类型都可以传给模板

2.传递函数的时候  会自动加括号调用  前端展示的是函数调用的之后的返回值,没有返回值就收到None,

注意:django的模板语法 不支持给函数传参!!!
方式1
     通过字典的键值对 指名道姓的一个个的传

     return render(request,'reg.html',{'n':n,'f':f})
方式2
    locals会将它所在的名称空间中的所有的名字全部传递给前端
    该方法虽然好用 但是在某些情况下会造成资源的浪费,比如你只用一个就可以了,一下子全丢给你
   return render(request, 'reg.html', locals())

3.过滤器

<h1>模板语法之标签:内部原理(会将|前面的当做第一个参数传入标签中)</h1>
<p>{{ l|length }}获取列表或者字符串长度</p> 7<p>{{ ss|default:'nothing' }}当|左边变量为false或空则使用|右边默认值,自己有用自己的</p>

<p>{{ file_size|filesizeformat }}获得大小</p> 11.7M

<p>{{ info|truncatewords:3 }} 就是按空格截取  三个点不算</p> my name is ...
<p>{{ info1|truncatewords:3 }}</p> 傻大姐 撒旦 技术 ...

<p>{{ info|truncatechars:6 }}按字符截取内容 三个点也算</p>my...

<p>{{ ctime }}</p> Sept. 18, 2019, 9:19 p.m.
<p>{{ ctime|date:'Y-m-d' }} 只需要掌握年月日就可以了</p> 2019-09-18

<p>{{ n|add:100 }}数值加减</p> 100(n原本为0)
<p>{{ s|add:'hahah 翻车啦' }}字符串拼接</p> 你妹的hahah 翻车啦

<p>{{ l|slice:'0:3' }}支持切片</p> [1,2,3]
<p>{{ l|slice:'0:5:2' }}支持步长</p> [1,3,5]

safe

Django的模板中会对HTML标签和JS等语法标签进行自动转义(字符串化),原因显而易见,这样是为了安全。

yyy = '<script>alert(123)</script>'
<p>{{ yyy|safe }}</p> 如果不转义,标签生效,会出现弹窗,类似这个有些人利用这一特性标签内写一个死循环那服务器就炸了
<p>{{ yyy }}</p>  转义后输出字符串'<script>alert(123)</script>'就安全了

但是有的时候我们可能不希望这些HTML元素被转义,比如(我们显然是需要这个样式生效)

<p>{{ xxx}}</p>  会得到字符串 '<h1>波波棋牌室</h1>'
<p>{{ xxx|safe }}</p> 波波棋牌室

为了在Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义。

比如:

前后端取消转义:(******)
                前端:
                    |safe
                后端:
                    from django.utils.safestring import mark_safe
                    zzz = mark_safe('<h1>啊啊啊</h1>')
                    <p>{{ zzz }}</p> 啊啊啊

4.标签

# for

{% for foo in t %}
    <p>{{ forloop }}</p>
{% endfor %}

 

 

 # if ... else

{% if '' %}
<p>xxx条件为true</p>
    {% else %}
    <p>xxx条件为false</p>
{% endif %}

输出:xxx条件为false

# for  if  嵌套

{% for %}
                {{ forloop }}
                {{ forloop.first }}
                {{ forloop.last }}
                {{ forloop.counter0 }}
                {{ forloop.counter }}  数据库中的数据它的主键值 不可能是依次递增的  有可能被你删除了几条数据  主键值就不再连续的了,但是你又不想让用户看出来,就这样搞
            
                {% empty %}
                    当for循环对象不能被for循环的时候会走empty逻辑
            {% endfor %}

 

{% for foo in '' %}
{% if forloop.first %}
<p>这是我的第一次</p> {% elif forloop.last %} <p>这是最后一次了啊</p> {% else %} <p>来啊来啊!!!</p> {% endif %}
{% empty %}
<p>当for循环的对象为空的时候 会走empty</p> {% endfor %}

输出:

这是我的第一次


来啊来啊!!!


这是最后一次了啊


#循环字典

d = {"name": 'jason', 'password': 123}
{% for foo in d.keys %}
    <p>{{ foo }}</p>
{% endfor %}
{% for foo in d.values %}
<p>{{ foo }}</p> {% endfor %}
{% for foo in d.items %}
<p>{{ foo }}</p> {% endfor %}

输出:

name

password

jason

123

('name', 'jason')

('password', 123)


#句点符取值,with取别名

如果你想在前端获取后端传递的某个容器类型中的具体元素
那么你可以通过句点符来获取具体的元素

可以给一个比较复杂的取值操作取一个别名 之后在with语句中 就可以使用该别名(外部不能使用)
django模板语法在取值的时候 统一使用句点符(大白话就是 点号   .)
    l = [1, 2, 3, 4, 5, 6, [12, 3, 4, {'name': 'heiheihei'}]]
    {% with l.6.3.name as ttt %}  可以给一个比较复杂的取值操作取一个别名 之后在with语句中 就可以使用该别名
        {{ ttt }} heiheihei
        {{ l.6.3.name }} heiheihei
    {% endwith %}

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

自定义标签固定的三步走战略:
                    1.必须在你的应用下新建一个名为templatetags文件夹
                    2.在该文件夹内新建一个任意名称的py文件
                    3.在该py文件中固定先写下面两句代码
                        from  django import template
                        register = template.Library()

#自定义过滤器

{% load mytag %}
{{ 123|baby:1}}
结果: 124 自定义过滤器 只能有两个形参,但是你可以在给第二个参数传值的时候 传一个字符串 {% load mytag %} {{ 'abc'|baby:'d-e=f+'}}
结果: abcd-e=f+

#自定义标签

#支持传多个值

{% load mytag %} {% jason 1 2 3 year=2 %}
结果: 1?2|3{2

# 自定义inclusion_tag

当你的页面上有一部分html代码需要经常被各个地方使用  并且需要传参才能渲染出,
那么你可以把该html代码部分制作成一个inclusion_tag
任何页面都能使用

任何html文件中都可以调用
{% load mytag %} {% bigplus 5 %}

 6.模板的继承

当多个页面整体的样式都大差不差的情况下 可以设置一个模板文件
在该模板文件中 使用block块划分多个区域,并起个名字,比如css块
之后子版在使用模板的时候 可以通过选择block块的名字,来修改对应区域分区域

 

#模板文件分块处理

模板一般情况下 应该至少有三个可以被修改的区域(这些标记本身就是注释语法在前端不会有任何显示,更不会对模板本身产生任何影响)
                {% block css %}
                    父页面自己的css代码
                {% endblock %}
                
                {% block content %}
                    父页面自己的html代码
                {% endblock %}
                
                {% block js %}
                    父页面自己的js代码
                {% endblock %}
#模板中必须有相应的块,子版才能修改,比如模板中有了css块即使为空,子版的样式才能修改,否则不能

# 模板的继承  使用方式

{{ block.super }}
                {% extends 'home.html' %} #起手就是继承

                {% block css %}
                    <style>
                        h1 {color: red;}
                    </style>
                {% endblock %}


                {% block content %}
          # {{block.super}} 加上这句话会原封不动的生成一个原来这个块的界面,最后会出现上面是home主页下面是登录界面
<h1>登陆页面</h1> <form action=""> <p>username:<input type="text" class="form-control"></p> <p>password:<input type="text" class="form-control"></p> <input type="submit" class="btn btn-danger"> </form> {% endblock %} {% block js %} {% endblock %} # 一般情况下 模板上的block越多 页面的可扩展性就越强

 

 

 

 

7.模板的导入

当你有个特别好看的表单或者样式别的地方需要使用,直接以模块导入即可

 {% include 'beautiful.html' %}

#beautiful.html

<h1>我是导入的</h1>

 

posted @ 2019-09-22 21:59  www.pu  Views(518)  Comments(0Edit  收藏  举报