django 之 视图层、模板层

目录

  • 视图层
  • 模板层

视图层

一.视图函数返回的都是一个HttpResponse对象

render 和 redirect 都是直接或者间接传的

二.JsonResponse

下面这个是正常的json的序列化

views.py 层
def test1(request):
    user_dict={'username':'jason 顶天立地 ','password':123}
    json_str= json.dumps(user_dict,ensure_ascii= False)###保证中文不会转成bytes
    return HttpResponse(json_str)
浏览器展示的:
{"username": "jason 顶天立地 ", "password": 123}

JsonResponse 序列化

from django.http import JsonResponse

def test1(request):
    user_dict={'username':'jason 顶天立地 ','password':123}
    # json_str= json.dumps(user_dict,ensure_ascii= False)

    return JsonResponse(user_dict,json_dumps_params={'ensure_ascii':False})#后面的这个代表的是保证中文不会转成bytes
浏览器展示的:
{"username": "jason 顶天立地 ", "password": 123}

如果序列的不是字典样式

def test1(request):
    # user_dict={'username':'jason 顶天立地 ','password':123}
    # json_str= json.dumps(user_dict,ensure_ascii= False)
    l1=[1,'磁场强大','人缘极好']
    return JsonResponse(l1,json_dumps_params={'ensure_ascii':False},safe=False)###最后safe为False这个保证非字典 也可以识别
浏览器展示的:
[1, "磁场强大", "人缘极好"]

三.form表单的上传

home.html




    
    Title
    
    
    


username: password:

views.py

def test1(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:
                f.write(line)   ###把图片保存起来
    return render(request,'home.html')
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
]}>
333.png

FILES可以通过这个拿到文件
注意:form表单上传文件
            1.必须做的事
                method必须是post
                enctype必须是formdata
            2.暂时需要做的 
                提交post请求需要将中间件里面的一个csrfmiddleware注释掉
            
            后端如何获取用户上传的文件
                file_obj = request.FILES.get('前端input框name属性值')
                file_obj.name  # 文件名
                for line in file_obj:
                    print(line)
                
                # django中推荐以下写法
                for chunk in file_obj.chunks():
                    print(chunk)
    

四.ab_render的原理

from django.template import Template,Context

def ab_render(request):
    temp=Template('

{{user_dict}}

') user_dict = Context({'user_dict':{'username':'jason','password':123}}) res= temp.render(user_dict) return HttpResponse(res) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 浏览器显示的: {'username': 'jason', 'password': 123}

五.FBV 和CBV

视图函数并一定就是函数 也可以是类

FBV:基于函数的视图

CBV:基于类的视图

下面是CBV的类的视图

urls

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^ab_render/', views.ab_render),
    url(r'^login/', views.MyLogin.as_view()), #这个是路由与视图函数对应关系相当于url(r'^login/', views.view)
]

views

from django.views import View
class MyLogin(View):
    def get(self,request):
        return render(request,'login.html')

    def post(self,request):
        return HttpResponse('我是类里面post的方法')

login.html




    
    Title
    
    
    


遇到上面的这个我们需要考虑一下,并且考虑 一些源码的问题

朝login提交get请求会自动执行MyLogin里面的get方法
而提交post请求也会自动执行MyLogin里面的post方法
为什么MyLogin针对不同的请求方法能够自动执行对应的方法

 研究源码的突破口
        url(r'^login/',views.MyLogin.as_view())
        猜想
            as_view要么是类里面定义的普通函数 @staticmethod
            要么是类里面定义的绑定给类的方法  @classmethod
        看源码发现是绑定给类的方法

django 之 视图层、模板层_第1张图片
django 之 视图层、模板层_第2张图片

CBV源码(******)
                MyClass.as_view()
                # 函数名加括号执行优先级最高
                @classonlymethod
                def as_view(...):
                    def view(...):
                        ...
                    return view
                # 变形
                url(r'^index/',views.view)  # CBV与FBV在路由匹配上本质是一样的
                
                def view(...):
                    self = cls(...)  # 生成的是我们自己写的类的对象
                    ...
                    return self.dispatch(...)
                    """
                    当你看到self.属性或方法的时候 不要想当然 
                    一定要遵循对象的属性和方法的查询顺序
                    对象本身  产生对象的类  类的父类
                    """
                def dispatch(...):
                    # 先判断当前请求方式是否在默认的八个合法请求方式内
                    if request.method.lower() in ['get','post','delete','options'...]
                        # 利用反射获取对象中对应的属性
                        handler = getattr(self,request.method.lower(),报错信息)
                    
                    return handler(...)  # 执行获取到的方法
                    

FBV和 CBV在路由匹配上本质是一样的,都是路由和函数内存地址的对应关系

六.importlib

我们正常的通过模块可以获取模块里面的值

conf包 里面有b.py
name= 'david'

a.py
# from conf import b
# print(b.name)
>>>>>>>>>>>>>>>>>>>>
david

上面的我们可以用其他的importlib来写

conf包 里面有b.py
name= 'david'

a.py 
import importlib
res= 'conf.b'
md=importlib.import_module(res)
#该方法的最小单位是模块 不是模块里面的单个名字
print(md.name)
>>>>>>>>>>>>>>>>>>>>
david

模板层

一.模板传值

python基本数据类型全部支持传递给html文件
        函数
        类
            函数和对象会自动加括号
            # 模板语法不支持传参
        对象
            后端给html文件传递数据的两种方式
            1.指名道姓
                return render(request,'index.html',{'n':n,'f':f})###放在一个字典里

            2.locals()  # 会将当前名称空间中所有的变量名全部传递给html页面
                return render(request,'index.html',locals())  
        
            html页面上 如何获取到后端传递过来的数据 
                {{ 变量名 }}
    
        取值
            django模板语法取值 只有一种操作方式  句点符 .
            点索引
            点键
            
            

{{ l.2 }}

{{ d.username }}

{{ d.password }}

{{ d.hobby.1.username.1 }}

8种数据类型和函数 、类 、对象传递给html文件

views

def index(request):
    #python的所有的数据类型都支持传递给html页面
    n=11
    f=11.111
    s='hello world'
    l=[1,2,3,4,5,6]
    d={'username':'jason','password':123,'hobby':['read',{'username':['egon','jason']}]}
    t=(1,2,3,4,5,6,)
    se={1,2,3,4,5}
    b= True
    ff= False
    ss='dsdkljgjsg sjg;d  djle dejs;egl dee kd;le;lk '
    sss='是的K歌 警方介入进来大家多 罚款交了比较 房间里人家给 外婆'
    file_size= 3290237579476
    def func():
        print('func 被执行了')
        return 'from func'

    class MyClass(object):
        def get_self(self):
            return 'from self'

        @staticmethod
        def get_func():
            return 'from func'

        @classmethod
        def get_cls(cls):
            return 'from cls'
    obj = MyClass()

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

index.html




    
    Title
    
    
    


{#

{{ n }}

#} {#模板语法的注释 这个注释前端浏览器检查是看不见的#} {#

{{ f }}

#} {#

{{ s }}

#} {#

{{ l }}

#} {#

{{ d }}

#} {#

{{ t }}

#} {#

{{ se }}

#} {#

{{ b }}

#} {#

函数名会自动加括号执行 展示的是函数的返回值:{{ func }} 模板语法不支持给函数传参

#} {#

传类名 也会自动加括号实例化 拿到的是对象地址{{ MyClass }}

#} {#

{{ MyClass }}

#} {#

{{ MyClass.get_func }}

#} {#

{{ MyClass.get_cls }}

#} {#

{{ MyClass.get_self }}

#} {#

{{ obj }}

#} ##得到的是对象的地址 {#

{{ obj.get_func }}

#} {#

{{ obj.get_self }}

#} {#

{{ obj.get_cls }}

#} {#

取值

#} {#

{{ l.2 }}

#} {#

{{ d.username }}

#} {#

{{ d.password }}

#} {#

{{ d.hobby.1.username.1 }}

#}

二.模板的过滤器

    如下是具体的语法
    |length:求长度
    |add:增加值
    |default :设置默认值 如果是True 返回True  如果是false  返回默认值
    |truncatechars: 截取字符  后面加3个点
    |truncatewords:截取单词  后面加3个点
    |filesizeformat:文件大小 换算成多少B
    |slice :切片
    |date:  转化成日期格式
    |safe:转义  可以把css js等样式渲染到前端

views

def index(request):
    from datetime import datetime

    s= 'hello world dvdlej  vdnelegjn  nej  dje vnle vgjnlew ldeg gel eglkkeg '
    a= 12
    b= True
    ff = False
    file_size= 298355976576
    ddd= datetime.now()
    res= '

你好啊

' res1= '' return render(request,'index.html',locals())

index.html

{#

过滤器 |左边的会当做过滤器的第一个参数 过滤器名右边的会当做过滤器的第二个参数

#} {#

求数据长度:{{ s|length }}

#} {#

加法运算:{{ n|add:10 }}、{{ s|add:13132 }}、{{ s|add:'DSB' }}

#} {#

默认值(判断值是否为空):{{ b|default:'这个b布尔值是True' }}、{{ ff|default:'这个ff布尔值是Flase' }}

#} {#

截取字符(截取5个字符 三个点也算):{{ s|truncatechars:8 }}

#} {#

截取单词(截取8个单词 三个点不算):{{ ss|truncatewords:8 }}、{{ sss|truncatewords:4 }}

#} {#

文件大小:{{ file_size|filesizeformat }}

#} {#

切片操作:{{ s|slice:'0:2' }}、{{ s|slice:"0:8:2" }}

#} {#

日期格式化:{{ ddd|date:'Y年/m月/d日' }}

#} {#

转义:{{ res|safe }}、{{ res1 }}、后端直接标识安全:{{ res2 }}

#}

浏览器的显示结果

70

True、2222ff

he...

hello world dvdlej vdnelegjn nej dje vnle vgjnlew ...

277.9 GB

he

2020年/01月/07日

你好啊

前后端取消转义
前端
|safe
后端
from django.utils.safestring import mark_safe
mark_safe('

安全滴

')
总结:前端代码不一定非要在前端页面写,可以在后端写好传递给前端页面使用
这样的话 你就可以利用到后端更加多的逻辑语法
下面是举例

from django.utils.safestring import mark_safe
res2= mark_safe('

积极阳光帅气

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

{{ res2 }}

三.模板语法逻辑相关

forloop 对象

判断for循环的开始结束的位置

索引 计数

如下是在html页面上

{# {% for foo in l %}#}
{#{{ forloop }}#}
{# {% endfor %}#}
    
    
    打印的结果是:
    {'parentloop': {}, 'counter0': 0, 'counter': 1, 'revcounter': 6, 'revcounter0': 5, 'first': True, 'last': False} {'parentloop': {}, 'counter0': 1, 'counter': 2, 'revcounter': 5, 'revcounter0': 4, 'first': False, 'last': False} {'parentloop': {}, 'counter0': 2, 'counter': 3, 'revcounter': 4, 'revcounter0': 3, 'first': False, 'last': False} {'parentloop': {}, 'counter0': 3, 'counter': 4, 'revcounter': 3, 'revcounter0': 2, 'first': False, 'last': False} {'parentloop': {}, 'counter0': 4, 'counter': 5, 'revcounter': 2, 'revcounter0': 1, 'first': False, 'last': False} {'parentloop': {}, 'counter0': 5, 'counter': 6, 'revcounter': 1, 'revcounter0': 0, 'first': False, 'last': True}
    
    ###第一个是True  最后一个是True

for 循环 if 判断

views.py 里面的是 l=[1,2,3,4,5,6]

html页面
{% for foo in l %}
{% if forloop.first %}
    

这是我的第一次

{% elif forloop.last %}

这个是最后一次

{% else %}

{{ foo }}

{% endif %} {% endfor %}

浏览器结果

这是我的第一次

2

3

4

5

这个是最后一次

如果是空的 可以用下面的empty来判断

views.py 里面的是 l=[]

html页面
{% for foo in l %}
{% if forloop.first %}
    

这是我的第一次

{% elif forloop.last %}

这个是最后一次

{% else %}

{{ foo }}

{% endif %} {% empty %} {# 这个是用来的判断的 如果容器内没有值#}

for循环的对象没有值

{% endfor %}

四.对于多用和常用的可以起个别名

这个别名只能在with里面使用

views.py
def index(request):
d = {"username": 'jason', 'password': 123, 'hobby': ['read', {'username': ['jason', 'egon']}]}
return render(request,'index.html',{'l':l,'d':d} )


index.html
{% with d.hobby.1.username.1 as eg %}
    

别名{{ eg }}

{% endwith %}

五.自定义的过滤器、标签 、inclusion_tag

自定义过滤器 标签 inclusion_tag
先完成以下前期准备工作
1.在应用名下新建一个名字必须叫templatetags文件夹
2.在该文件夹内新建一个任意名称的py文件(eg:mytag)
3.在该文件内 必须先写以下两句代码
from django.template import Library
register= Library()

总结 页面上使用他们 统一先导入

{% load mytag %}

mytag.py

from django.template import Library
register= Library()

#自定义的过滤器
@register.filter(name= 'my_sum')
def index(a,b):
    return a +b


#自定义的标签
@register.simple_tag(name= 'my_baby')
def index1(a,b,c,d):
    return '%s?%s?%s?%s?'%(a,b,c,d)


#自定义inclusion_tag
@register.inclusion_tag('demo.html',name='myin')
def index(n):
    l=[]
    for i in range(n):
        l.append(i)
    #将列表传递给demo.html ## 这个return给demo.html在上面@register这个里面
    return {'l':l}

index.html



自定义的过滤器

{% load mytag %}

{{ 10|my_sum:90 }}

{% if 10|my_sum:100 %}

条件成立

{% endif %} >>>>>>>>>>>>>>>>>>>>>>> 100

自定义的标签

{% load mytag %}

{% my_baby 1 2 3 'hello world' %}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 1?2?3?hello world?

自定义的inclusion_tag的使用

{% load mytag %} {% myin 5 %} >>>>>>>>>>>>>>>>>>>>>>>>>>>>> 0 1 2 3 4

demo.html

    {% for foo in l %}
  • {{ foo }}
  • {% endfor %}

六.模板的继承

​ 某一个页面大部分区域都是公用的 那这个页面就可以作为模板页面
​ 当别人继承这个页面之后 如何修改对应的区域

先在模板页面上通过block实现划定区域
{% block content %} 
模板页面内容
{% endblock %}

子页面中先导入整个模板
{% extends '模板页面.html'%}
修改特定的区域  通过实现划定好的区域名称
{% block content %}
子页面内容
{% endblock %}

通常情况下 模板页面页面应该起码有三块区域
{% block css %} 
模板页面内容
{% endblock %}
{% block content %} 
模板页面内容
{% endblock %}
{% block js %}  
模板页面内容
{% endblock %}
# 模板的block块越多 可扩展性越高



还支持子页面调用父页面对应区域的内容 并且可以无限次调用
{{ block.super }}

urls.py

 #模板的导入
    url(r'^mdzz/',views.mdzz),
    url(r'^loginn/',views.loginn),
    url(r'^register/',views.register)

views.py

def mdzz(request):    return render(request,'mdzz.html')def loginn(request):    return render(request,'loginn.html')def register(request):     return render(request,'register.html')

mdzz.html




    
    Title
    
    
    
    {% block css %}

    {% endblock %}




Panel title

{% block content %}

Hello, world!

...

Learn more

{% endblock %}
{% block js %} {% endblock %}

loggin.html

{% extends 'mdzz.html' %}

{% block css %}
    
{% endblock %}
{% block content %}
    {% include 'left.html' %}
    

登录页面

username:

password:

提交:
{{ block.super }} {% endblock %} {% block js %} {% endblock %}

register.html

 {% extends 'mdzz.html' %}

{% block css %}
    
{% endblock %}

{% block content %}

注册页面

username:

password:

{{ block.super }} {{ block.super }} {{ block.super }} {% endblock %} {% block js %} {% endblock %}

七.模板的导入

将html页面当做模块使用 哪里需要导哪里 这个html页面通常都不是完整的 只是一个局部样式
{% include '要导入的模块名' %}

举例

left.html

这是我做的很美的一个模块

将left.html模块导入到这个loginn.html中,直接在loginn.htm 要插入的位置放上{% include 'left.html' %} 即可

八.基于django settings源码实现项目配置文件的 插拔式设计

conf.settings.py

 NAME='我是暴露给用户的配置文件'

lib.conf.--init--.py

import importlib
import os
from lib.conf import global_settings
class Settings(object):
    def __init__(self):
        #先遍历出项目默认的全局匹配值的文件
        for  name in dir(global_settings):
            #判断变量名是否大写
            if name.isupper():
                #键值对设置对象
                k = name
                v= getattr(global_settings,name)
                setattr(self,k,v)

        #先获取暴露给用户的配置文件的字符串路径
        module_path = os.environ.get('xxx')
        #里面的importlib模块 导入到settings文件
        md= importlib.import_module(module_path)
        #md能拿到settings里面的所有的名字
        #同上操作
        for name in dir(md):
            #判断变量名是否大写
            if name.isupper():
                #键值对设置给对象
                k= name
                v= getattr(md,name)
                setattr(self,k,v)

settings = Settings()

lib.conf.global_settings.py

NAME ='我是项目默认的配置文件'

start.py

import os
import sys
BASE_DIR = os.path.dirname(__file__)
sys.path.append(BASE_DIR)

if __name__ == '__main__':
    #项目启动 就应该朝全局大字典中设置键值对
    os.environ['xxx']= 'conf.settings'
    from lib.conf import settings
    print(settings.NAME)

暴露给用户的配置文件,如果用户的存在 就用用户的, 如果用户的没有 ,就是用全局系统默认的

django其实有两个配置文件 一个是暴露给用户的可以自定义的配置 一个是项目默认的配置
        用户如果配置了就用用户的 没有配置就用默认的
        
        from django.conf import global_settings,settings
        
        settings = LazySettings()
        
        
        class LazySettings(...):
            def _setup(...):
                # 获取暴露给用户的配置文件字符串路径
                setting_module = os.environ.get(纯大写变量名)
                """
                manage.py
                os.environ.setdefault(纯大写变量名,'暴露给用户的配置文件字符串路径')
                """
                
                Settings(setting_module)
        def Settings(...)
            # 先遍历全局默认的配置文件       给对象设置键值对
            for setting in dir(global_settings):
                if setting.isupper():
                    setattr(self,setting,getattr(global_settings,setting))
                    
            
            # 再遍历暴露给用户的配置文件     给对象设置键值对
            md = importlib.import_module(setting_module)
            for setting in dir(md):
                if setting.isupper():
                    setattr(self,setting,getattr(md,setting))
            """
            利用的其实就是字典的键存在和不存在 下面语句的作用
            dict[key] = value
            """

你可能感兴趣的:(django 之 视图层、模板层)