☆Django☆---FBV与CBV setting源码 模板层 模板的继承与导入
昨日内容回顾 路由层 路由匹配 url(正则表达式,视图函数内存地址) 只要正则匹配到了内容,就不再继续往下匹配,而是直接后面的视图函数 匹配首页 url(r'^$',home) 匹配尾页 url(r'',errors) 路由匹配的规律 第一次按照用户输入什么就匹配什么如果匹配不上 第二次django会让浏览器自动再尾部加斜杠再次匹配 还匹配不上就会报错 上述规律不是浏览器的特点,是django的功能 django settings文件中 APPEND_SALSH = True # 自动加斜杠 """为什么setting文件中变量名必须写全大写""" 无名分组 路由匹配成功之后,如果正则表达式中有分组的,那么在调用视图函数的时候 会将括号内正则表达式匹配到的内容当做位置参数传递给后面的视图函数 url(r'^index/(\d+)/',index) def index(request,args): return 'HttpResponse对象' 有名分组 路由匹配成功之后,如果正则表达式中有分组并且起了别名的,那么在调用视图函数的时候 会将括号内正则表达式匹配到的内容当做关键字参数传递给后面的视图函数 url(r'^index/(?P<year>\d+)/',index) def index(request,year): return 'HttpResponse对象' 无名分组与有名分组不能混合使用(*****) 但是两者单独使用的时候可以有多个无名或者有名 反向解析 由来当有很多html页面和后端代码中频繁的使用了指名道姓的ulr(/index/) 那么一旦路由发生了变化 前后端所有的地方都要修改,造成了可维护性较差 反向解析的本质就是通过某个方法获取的一个能够访问对应url的结果 没有涉及到正则表达式符号情况下 url(r'^index/',index,name='aaa') 前端 {% url 'aaa' %} 后端 from django.shortcuts import HttpResponse,render,redirect,reverse _url = reverse('aaa') 无名和有名分组的情况 无名 url(r'^index/(\d+)/',index,name='bbb') 前端 {% url 'bbb' 1 %} 后端 _url = reverse('aaa',args=(1,)) 有名 url(r'^index/(?P<edit_id>\d+)/',index,name='bbb') 前端 {% url 'bbb' 1 %} # 支持无名的写法 {% url 'bbb' year=1 %} 后端 _url = reverse('aaa',args=(1,)) # 支持无名的写法 _url = reverse('aaa',kwargs={'year':1}) 路由分发 include() django中的每一个app都可以有自己独立的urls.py,static静态文件夹,templates模板文件夹 正是基于上面的特点,django就可以很好的支持多人分组开发,每个人只需要开发各自的app即可 最后只需要创建一个空的django项目,将所有人的app注册进来,总路由中来一个路由分发就可以将多个人的功能整合到一个项目中 当项目比较巨大的时候 如果不使用路由分发 那么中路由的中url代码太多了 不利于维护和更新 from app01 import urls as app01_urls from app02 import urls as app02_urls url(r'应用名前缀/',include(app01_urls)) url(r'应用名前缀/',include(app02_urls)) # 推荐使用下面的方式 url(r'应用名前缀/',include('app01.urls')) url(r'应用名前缀/',include('app02.urls')) """路由分发需要注意的点 1.总路由的中最后千万不要加$符号 """ 名称空间 你在起别名的时候 虽然能够同一个应用下不能重名 但是你没办法实现多个人多个app不重名的情况 一旦重名反向解析并不能自动识别所在的应用 # 总路由中 给每一个应用开设一个名称空间 url(r'应用名前缀/',include('app01.urls',namespace='app01')) url(r'应用名前缀/',include('app02.urls',namespace='app02')) # 子路由 url(r'^index/',index,name='aaa') url(r'^index/',index,name='aaa') # 反向解析的时候 reverse('app01:aaa') reverse('app02:aaa') {% url 'app01:aaa' %} {% url 'app02:aaa' %} # 其实你可以完全不使用名称空间 只需要保证别名不重复 # 你可以在起别名的时候给每一个别名加上所在应用名的前缀 伪静态 就是url后面加了个.html 虚拟环境 不用的项目应该有属于该项目独有的python解释器环境 这样的话可以避免资源浪费以及版本兼容的问题 # 注意虚拟环境虽然好用,但是不要无限制开设 如果你机器上虚拟环境 你可以手动删除 django版本区别 路由2.X里面的是path 第一个参数不再支持正则表达式 写什么就匹配什么 如果你还想使用正则 2.X里面的re_path就是1.X里面的url 虽然path不支持正则,但是它提供了五种转化器(可以将路由匹配到的内容自动转换成对应的数据格式再传递给后面的视图函数) 除了五种转化器之外 你还可以自定义转换器 视图层 如果一个应用下的视图函数特别特别多 你可以新建一个views文件夹,然后根据功能的不同起不同名字的py文件存放不同的功能 实现持续的解耦 可读性 views user.py account.py order.py ... 三板斧 前后端分离 JsonResponse(data,safe=False,json_dumps_params={'ensure_ascii':False}) 如果你传递的不是字典数据,那么需要手动指定safe=False form表单上传文件 需要注意的实现 1.method必须是post 2.enctype必须要指定成formdata 3.目前需要考虑的 需要将settings配置文件中的csrf中间件注释掉 后端不再通过request.POST获取文件数据,而是通过request.FILES file_obj = request.FILES.get('myfile') file_obj.name # 文件名 with open(file_obj.name,'wb') as f: for chunk in file_obj.chunks(): f.write(chunk) request方法总结 request.method request.GET request.POST request.FILES request.body # 原生的二进制数据 """ RBAC (role based access control) 基于角色的权限管理 当你在做权限管理的时候 需要用到 在web领域权限就是一个个的url 简单判断用户是否有权限访问某个url思路 获取用户想要访问的url 与数据库中该用户可以访问的url进行对比 """ request.path # 只拿url request.get_full_path() # url+?后面的参数
render(中文:提供)的内部原理:
只要是视图函数都必须返回一个 Httpresponse
render他返回的是 一个html页面 并且还能够给页面提供数据
导入模块 django.template import Template , Context >>(上下文 环境) context 就相当于我们传入的值 template 相当帮我们渲染了 一张html界面
FBV 与 CBV
视图函数并不单单指的是函数 也可以是类
FBV (function bese views) 基于函数的视图 面向函数式编程
CBV (class bese views) 基于类的视图 面向对象式编程
如何定义一个CBV?
在urls.py 中
url(r'^index/', views.MyLogin.as_view())
在 views 中
from django.views import View
....以及上面代码
问题 :基于cbv视图函数 get请求 来为什么会走get方法?post 请求会走post 方法
研究方向 先从 url 入手
url(r'^login/',views.MyLogin.as_view())
由于函数名加括号执行优先级最高,所以这一句话一写完会立刻执行as_view()方法
as_views 内我们需要了解的源码
看源码看不懂的我们就不看 我们发现as_view 方法是一个 类绑定方法 自动传cls 为第一个参数 看到最后 发现他返回了一个 叫 view的函数内存地址
然后 我们的路由就 变成了 view.view了 接着就是 当浏览器 访问 我们的后缀的时候 会自动加括号调用 此时 as_view 里的 view 方法就相当于 触发了 我们继续看 view 方法 同样 我们 看不懂的就是不看
我们会发现 他自己没有 类没有 但是 继承的view 有一个 我们可以点进去看看
有每日有感觉到 反射额牛逼之处??
Django settings 源码
既然我们都有看源码的能力了 那么 我们引出问题 为什么 settings文件中的 必须是大写才能识别?
导入 前提:
1. django 除了暴露给 用户的一个settings.py的配置文件之外 自己其实还有一个全局的配置文件
2. 我们在使用配置文件的时候可以直接导入暴露给用户的settings 也可以使用django全局的django配置文件 并且后者居多
3 django的启动入口是 manage.py
那么我们可以先找到这个 django的全局配置文件 使用 from django.conf import global_settings
先看settings配置文件 发现他是 一个类实例化出来的 我们点进去这个类中看看
我们在跳转一下 发现django启动的时候 的那个入口文件 manage 里面 有一个 setdefault
django在启动的时候 就会往全局的大字典中设置一个键值对 值是暴露给用户的配置文件的路径字符串
然后我们就真的来看django里的全局配置文件了 点进去发现 有特别多的文件
然后在_setup 最后有一个 设置
settings_module 就是 你manage里传的那个值 然后她又执行了 settings 的一个类 并且把settings_module传了进去 点进去看看
class Settings(object): def __init__(self, settings_module): # settings_module = 'day54.settings' # update this dict from global settings (but only for ALL_CAPS settings) for setting in dir(global_settings): # django全局配置文件 # dir获取django全局配置文件中所有的变量名 if setting.isupper(): # 判断文件中的变量名是否是大写 如果是大写才会执行/生效 setattr(self, setting, getattr(global_settings, setting)) # 给settings对象设置键值对 # 给settings对象设置键值对 settings[配置文件中大写的变量名] = 配置文件中大写的变量名所对应的值 # store the settings module in case someone later cares 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' 用户如果配置了就用用户的 用户如果没有配置就用系统默认的 其实本质就是利用字典的键存在就是替换的原理 实现了用户配置就用用户的用户没配置就用默认的 """ 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
import os import importlib from lib.conf import global_stiings class Settings(object): def __init__(self): for name in dir(global_stiings): if name.isupper(): setattr(self, name, getattr(global_stiings, name)) path = os.environ.get('xxx') module = importlib.import_module(path) #module 就相当于 from lib import settings 的 settings的模块名 for name in dir(module): if name.isupper(): setattr(self, name, getattr(module, name)) settings = Settings()
魔板层
魔板语法分为两大类 变量相关 {{}} 变量相关 {%%} 逻辑相关
前段传{'xxx':xxx}
# 给模板传值的方式 # 方式1 # 通过字典的键值对 指名道姓的一个个的传 # return render(request,'reg.html',{'n':n,'f':f}) # 方式2 # locals会将它所在的名称空间中的所有的名字全部传递给前端 # 该方法虽然好用 但是在某些情况下回造成资源的浪费 return render(request,'reg.html',locals()) # 为了教学方便 我们以后就用locals()
支持 字符串 整形浮点型 列表 字典 集合 元祖 对象 函数
可以传函数 前端会自动调用 你的函数需要有返回值 如果传了参数的话 魔板语法不支持
对象也可以使用点方式取值
模板语法 过滤器
方法是 |
如 {传来的值| length } 内部原理 会将|前的第一个参数传入
{|default:'asdada'} 当左边没有值得时候 会使用 就像get
<p>{{ ss|default:'当|左边的变量为空就会返回|右边的值' }} default跟你后端get方法很想</p> <p>{{ ss|default:'' }} default必须要有两个参数</p> <p>{{ file_size|filesizeformat }}</p> 自动帮你换算 大小 <p>{{ info|truncatewords:3 }} 就是按空格截取 三个点不算</p> <p>{{ info|truncatechars:6 }}按字符截取内容 三个点也算</p> <p>{{ yyy|safe }}</p> 后端传来的值 自动转换 成html 表示安全 后端取消转义 from django.utils.safestring import mark_safe zzz = mark_safe('<h1>阿萨德搜啊第三款垃圾袋</h1>') <p>{{ ctime|date:'Y-m-d' }} 只需要掌握年月日就可以了</p> <p>{{ n|add:100 }}</p> 需要掌握 |safe |filesizeformat |date
魔板语法值标签 (逻辑相关)
{% for foo in l %} <p>{{ forloop }}</p> forloop对象 {% endfor %} {% if '' %} <p>xxx条件为true</p> {% else %} <p>xxx条件为false</p> {% endif %}
{% for foo in '' %}
{% if forloop.first %}
<p>这是我的第一次</p>
{% elif forloop.last %}
<p>这是最后一次了啊</p>
{% else %}
<p>来啊来啊!!!</p>
{% endif %}
{% empty %}
<p>当for循环的对象为空的时候 会走empty</p>
{% endfor %}
<p> django模板语法在取值的时候 统一使用句点符(大白话就是 点号 .) {% with l.6.3.name as ttt %} 可以给一个比较复杂的取值操作取一个别名 之后在with语句中 就可以使用该别名 {{ ttt }} {{ l.6.3.name }} {% endwith %} </p>
{% 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 %}
with 直接使用ttt 就等价于 上面的
自定义标签
自定义标签 过滤器 inclusion_tag 自定义固定的三步走战略: 1.必须在你的应用下新建一个名为templatetags文件夹 2.在该文件夹内新建一个任意名称的py文件 3.在该py文件中固定先写下面两句代码
# 自定义过滤器 @register.filter(name='baby') def index(a,b): # 该过滤器只做一个加法运算 是|add建议版本 """ |length |add |default |filesizeformat |truncatewords |truncatechars |safe |slice :param a: :param b: :return: """ print('下午刚起床 一脸懵逼') return a + b
# 自定义标签 # 支持传多个值 @register.simple_tag(name='jason') def xxx(a,b,c,year): return '%s?%s|%s{%s'%(a,b,c,year)
# 自定义inclusion_tag """ 接收用户传入的参数 然后作用于一个html页面 在该页面上渲染数据 之后将渲染好的页面 放到用户调用inclusion_tag的地方 """ # 自定义inclusion_tag @register.inclusion_tag('bigplus.html') def bigplus(n): l = [] for i in range(n): l.append('第%s项'%i) return {'l':l}
魔板继承和导入
模板的继承 当多个页面整体的样式都大差不差的情况下 可以设置一个模板文件 在该模板文件中 使用block块划分多个预期 之后子版在使用模板的时候 可以通过block块的名字 来选定到底需要修改哪一部分区域 模板一般情况下 应该至少有三个可以被修改的区域 {% block css %} 子页面自己的css代码 {% endblock %} {% block content %} 子页面自己的html代码 {% endblock %} {% block js %} 子页面自己的js代码 {% endblock %} # 模板的继承 使用方式 {% extends 'home.html' %} {% block css %} <style> h1 { color: red; } </style> {% endblock %} {% block content %} <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越多 页面的可扩展性就越强 模板的导入 {% include 'beautiful.html' %} 当你写了一个特别好看的form表单/列表标签等 可以将它当成一个模块 哪个地方需要 就直接导入使用即可
单表操作
1.单表操作 create_time = models.DateField() 关键性的参数 1.auto_now:每次操作数据 都会自动刷新当前操作的时间 2.auto_now_add:在创建数据的时候 会自动将创建时间记录下来 后续的修改不会影响该字段