04--CBV源码分析 Django的settings源码分析 模板层

CBV源码分析:

前期准备:

urls.py中
   url(r'^login/',views.MyLogin.as_view())
views.py中
   from django.views import View
   class MyLogin(View):
       def get(self,request):
            print("from MyLogin get方法")
            return render(request,'login.html')
        def post(self,request):
            return HttpResponse("from MyLogin post方法")

1-源码入口:

url(r'^login/',views.MyLogin.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返回什么 浏览器就会收到什么    -------入口:dispatch方法
      # 对象在查找属性或者方法的时候 谨记一点 先从对象自己这里找  然后从产生对象的类里面找  最后类的父类依次往后
       return view
# as_view()返回值是一个函数,相当于url(r'^login/',views.view) FBV和CBV在路由匹配上是一致的 都是url+函数的内存地址

2-路由匹配执行view函数:

def dispatch(self, request, *args, **kwargs):
    # 我们先以GET为例
    if request.method.lower() in self.http_method_names:  # 判断当前请求方法是否在默认的八个方法内
      # 反射获取我们自己写的类产生的对象的属性或者方法
      # 以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 源码分析:

前期准备:

#django除了暴露给用户一个settings.py配置文件之外  自己内部还有一个全局的配置文件
#我们在使用配置文件的时候 可以直接直接导入暴露给用户的settings.py也可以使用django全局的配置文件 并且后者居多
from django.conf import settings
#django的启动入口是manage.py
# django在启动的时候 就会往全局的大字典中设置一个键值对  值是暴露给用户的配置文件的路径字符串
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day54.settings")

1-settings 即 LazySettings()对象:

settings = LazySettings()  # 单例模式    

2-LazySettings类内部:


ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
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')

3-Settings 类内部:

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'
              用户如果配置了就用用户的
              用户如果没有配置就用系统默认的
              其实本质就是利用字典的键存在就是替换的原理 实现了用户配置就用用户的用户没配置就用默认的
              """

 参考Django的settings源码的应用:

新建项目:

about_settings
  --|  conf文件夹
    --|  settings.py
  --|  lib文件夹
    --|  conf文件夹
      --| __init__.py       
--| global_settings.py   --| start.py

start.py:

import os
import sys


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

os.environ.setdefault('kkk','conf.settings')

if __name__ == '__main__':
    from lib.conf import settings
    print(settings.NAME)

gloabal_settings.py:

NAME = "我是系统默认的name"

settings.py:

NAME = "我是用户自定义的NAME"

__init__.py:

import os
import importlib
from lib.conf import global_settings
from conf import settings


class Settings(object):
    def __init__(self):
        for setting in dir(global_settings):
            if setting.isupper():
                setattr(self,setting,getattr(global_settings,setting))

        path = os.environ.get("kkk")
        model = importlib.import_module(path)
        for setting in dir(model):
            if setting.isupper():
                setattr(self,setting,getattr(model,setting))


settings = Settings()

 模板层:

传值方式:

# 方式1
    # 通过字典的键值对,以k,v键值对的形式进行传递:
    return render(request,'reg.html',{'n':n,'f':f})

# 方式2
    # locals会将它所在的名称空间中的所有的变量全部传递给前端
    # 该方法虽然好用 但是在某些情况下回造成资源的浪费
    return render(request, 'reg.html', locals())

支持的数据类型:

<p>{{ n }}</p>    # 整型
<p>{{ f }}</p>    # 浮点型
<p>{{ s }}</p>    # 字符串
<p>{{ l }}</p>    # 列表
<p>{{ d }}</p>   # 字典
<p>{{ t }}</p>    # 元组
<p>{{ se }}</p>  # 集合
<p>{{ index }}</p>
# 传函数名,会自动加括号调用该函数,前端展示的是函数执行之后的返回值
# 注意:如果函数需要参数的话  那么不好意思 模板语法不支持

 传递类对象:

后台传入obj对象给前端:

    class Demo(object):

        def get_self(self):
            return '绑定给对象的方法'

        @classmethod
        def get_cls(cls):
            return '绑定给类的方法'

        @staticmethod
        def get_static():
            return '我是静态方法  其实就是函数'
    obj = Demo()

前端调用类中方法:

<p>{{ obj }}</p>
<p>{{ obj.get_self }}</p>
<p>{{ obj.get_cls }}</p>
<p>{{ obj.get_static }}</p>
<p>总结:django的模板语法 不支持给函数传参!!!</p>

 模板常用的标签过滤器:

前端使用:

<h1>模板语法之标签:内部原理(会将|前面的当做第一个参数传入标签中)</h1>
<p>{{ l|length }}列表长度</p>
<p>{{ n|length }}数字求长度不会报错,返回0</p>
<p>{{ ss|default:'当|左边的变量为空就会返回|右边的值' }}  default跟你后端get方法类似</p>
<p>{{ ss|default:'' }} default必须要有两个参数</p>
<p>{{ file_size|filesizeformat }}进行文件大小转换k->M->G</p>
<p>{{ info|truncatewords:3 }} 就是按空格截取,三个点不算</p>
<p>{{ info1|truncatewords:3 }}</p>
<p>{{ info|truncatechars:6 }}按字符截取内容 三个点也算</p>
<p>{{ xxx|safe }}取消转义</p>
<p>{{ yyy|safe }}</p>
<p>{{ zzz }}</p>
<p>{{ ctime }}</p>
<p>{{ ctime|date:'Y-m-d' }}将时间按格式显示</p>
<p>{{ n|add:100 }}数字n增加100</p>
<p>{{ s|add:'hahah 翻车啦' }}字符串拼接</p>

后台传入的值:

file_size = 12312312
info = 'my name is yyj and my age is 18'
info1 = '傻大姐 撒旦 技术 大 萨达 了 奥斯卡 的健康两 三点卡是考虑到'
xxx = '<h1>波波棋牌室</h1>'
yyy = '<script>alert(123)</script>'
# 后台进行取消转义
from django.utils.safestring import mark_safe
zzz = mark_safe('<h1>阿萨德搜啊第三款垃圾袋</h1>')
from datetime import datetime
ctime = datetime.now()

 逻辑相关:

for循环和if判断:

 <p>for循环 </p>
{% for foo in l %}
   <p>{{ forloop }}</p>
{% endfor %}

 <p>if判断</p>
{% if ' ' %}
<p>xxx条件为true</p>

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

 <p>forloop使用,empty使用</p>
{% for foo in '' %}

    {% if forloop.first %}
       <p>第一次循环</p>
    {% elif forloop.last %}
        <p>最后一次循环</p>
    {% else %}
        <p>普通循环</p>
    {% endif %}

    {% empty %}
    <p>当for循环的对象为空的时候 会走empty</p>
{% endfor %}

forloop属性:

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

取别名:

<p>
    django模板语法在取值的时候 统一使用句点符
    {% with l.6.3.name as ttt %}  
可以给一个比较复杂的取值操作取一个别名 之后在with语句中 就可以使用该别名
        {{ ttt }}
        {{ l.6.3.name }}
    {% endwith %}
</p>

keys,values,items操作:

取字典的key
{% for foo in d.keys %}
    <p>{{ foo }}</p>
{% endfor %}

取字典的value
{% for foo in d.values %}
    <p>{{ foo }}</p>
{% endfor %}

取键值对(元组)
{% for foo in d.items %}
   <p>{{ foo }}</p>
{% endfor %}

自定义过滤器、标签、inclution_tag:

自定义固定的三步:
  1.必须在你的应用下新建一个名为templatetags文件夹
  2.在该文件夹内新建一个任意名称的py文件
  3.在该py文件中固定先写下面两句代码:

from django import template
register = template.Library()
# 自定义过滤器
@register.filter(name='baby')
def index(a,b):
# 该过滤器只做一个加法运算 
    print('hello')
    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}

bigplus.html:

<ul>
    {% for foo in l %}
        <li>{{ foo }}</li>
    {% endfor %}
</ul>

自定义过滤器,标签,inclution_tag的使用:

<h1>自定义过滤器的使用
自定义过滤器 只能有两个形参
    但是你可以在给第二个参数传值的时候 传一个字符串
{% load mytag %} 需要先load自己创建的那个py文件
</h1>
{{ 123|baby:1}}
{{ 123|baby:'1|2|3|4|5|6'}}

<h1>自定义标签
支持传多个参数  参数与参数之间 空格隔开
</h1>
{% load mytag %}
{% jason 1 2 3 year=2 %}

<h1>自定义inclusion_tag应用场景:
当你的页面上有一部分html代码需要经常被各个地方使用  并且需要传参才能渲染出来,那么你可以把该html代码部分制作成一个inclusion_tag,任何页面都能使用
</h1>
{% load mytag %}
{% bigplus 5 %}
<br>
{% bigplus 10 %}

模板的继承:

母板:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="x-ua-compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Title</title>
  {% block page-css %}
  
  {% endblock %}
</head>
<body>

<h1>这是母板的标题</h1>

{% block page-main %}

{% endblock %}
<h1>母板底部内容</h1>
{% block page-js %}

{% endblock %}
</body>
</html>

注意:我们通常会在母板中定义页面专用的CSS块和JS块,方便子页面替换。

继承母板:

{# 在子页面中在页面最上方使用下面的语法来继承母板。#}
{% extends 'layouts.html' %}

替换母板中的块:

{% block page-main %}
  <p>世情薄</p>
  <p>人情恶</p>
  <p>雨送黄昏花易落</p>
{% endblock %}

模板的导入:

{% include 'navbar.html' %}
原文地址:https://www.cnblogs.com/yangjiaoshou/p/14234380.html