当前位置 : 主页 > 网页制作 > html >

day54

来源:互联网 收集:自由互联 发布时间:2021-06-12
render返回一个html页面 并且还能够给该页面传数据 render内部原理 from django.template import Template,Contextdef index(request): temp = Template('h1{{ user }}/h1') # ① con = Context({"user":{"name":'jason',"password":'

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

render内部原理

from django.template import Template,Context
def index(request):
        temp = Template('<h1>{{ user }}</h1>')  # ①
        con = Context({"user":{"name":'jason',"password":'123'}})  # ②
        # 上面两部操作就是render方法内部原理 ① 先把所有内容转成字符串 再用②里的context把 转后的信息传进去
        res = temp.render(con)
        print(res)
        return HttpResponse(res)

FBV与CBV
视图函数并不只是指函数 也可以是类
FBV ( function based views 基于函数的视图 ) 面向函数式编程
CBV ( class based views 基于类的视图 ) 面向对象式编程

问题:基于CBV的视图函数
"""get请求来就会走类里面get方法,post请求来就会走类里面post方法 为什么???"""

分享图片

上面的.as_view()应该是被@classmethod修饰的

面向对象里比较经典的装饰器有哪几个?

@ classmethod
?   修饰一个函数,这个函数是绑定给类的,要将类当做第一个参数传入

@ staticmethod
?   静态方法,相当于普通函数,无论对象来,还是类来,有几个参数就传几个参数

@ property
?   将一个方法伪装成一个属性,从而可以通过`.`来访问

分享图片

通过查询源码得知:类.as_view()最后返回的就是view方法
分析可得:FBV和CBV在路由匹配上是一致的,都是url+函数的内存地址

分析下面的源码,就能解释,你在写CBV的时候,GET方法来就走GET,POST请求方法来就走POST

CBV源码 as_view源码


( 要求 : 流畅讲出过程 )

总览

@classonlymethod
    def as_view(cls, **initkwargs):  # 这里的cls就是我们自己写的类 MyLogin
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)  # view是函数(as_view)内部的函数,他引用了外部函数(as_view)的名称空间或者作用域(**initkwargs)
            # 上面的self就是实例化产生MyLogin的对象 也就是 self = MyLogin(**initkwargs)
            # 说明view()是闭包函数!!!!!!
            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方法返回什么,浏览器就会收到什么
            # (比如urls.py里面index返回什么,浏览器就收到什么)
            # (所以view返回什么,浏览器就拿到什么,因为view返回的是self.dispatch)
            # (这就意味着self.dispatch返回什么,浏览器就拿到什么)
            # 上面的self是MyLogin的对象
            # 那self.dispatch的查找顺序是什么:
                # 1.先从对象自己找 (view) 
                # 2.再从产生对象的类里面找 (MyLogin) (view方法是定义在MyLogin里面的方法)
                # 3.最后从这个类的父类依次往后找 (View) (因为MyLogin(View))
            # 一定要默念上面的查找顺序
            # 根据上面的查找顺序,1 对象 和 2 类 都没有dispatch方法,最后只能去父类里面找
        view.view_class = cls
        view.view_initkwargs = initkwargs

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view

    def dispatch(self, request, *args, **kwargs):
    # 这个就是父类(View)里的dispatch
        # 我们先以GET为例
        if request.method.lower() in self.http_method_names:  
        # 判断当前请求方法是否在默认的八个方法内
        # 就是下面这八个方法
        # http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
            handler = getattr(self, request.method.lower(), 
            # getattr就是反射,即通过字符串,来操作对象的属性或者方法
            # self谁来调,就是谁,这里是我们的MyLogin类产生的实例化对象
            # 反射获取我们自己写的类产生的对象的属性或者方法
            # 以GET为例 handler = getattr(self,'get','取不到报错的报错信息')
            # handler = get(request)
            self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)  # 直接调用我们自己写的类里面的GET方法

总结上面源码,就是用了:闭包+反射

分析

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入手

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)  
            # view是函数(as_view)内部的函数,
            # 他引用了外部函数(as_view)的名称空间或者作用域
            # 也就是(**initkwargs)
            # 上面的self就是实例化产生MyLogin的对象 
            # 也就是 self = MyLogin(**initkwargs)
            # 说明view()是闭包函数!!!!!!
            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方法返回什么,浏览器就会收到什么
            # (比如urls.py里面index返回什么,浏览器就收到什么)
            # (所以view返回什么,浏览器就拿到什么,
            # 因为view返回的是self.dispatch)
            # (这就意味着self.dispatch返回什么,浏览器就拿到什么)
            # 上面的self是MyLogin的对象
            # 那self.dispatch的查找顺序是什么:
                # 1.先从对象自己找 (view) 
                # 2.再从产生对象的类里面找 (MyLogin) 
                # (view方法是定义在MyLogin里面的方法)
                # 3.最后从这个类的父类依次往后找 (View) 
                # (因为MyLogin(View))
            # 一定要默念上面的查找顺序
            # 根据上面的查找顺序,1 对象 和 2 类 都没有dispatch方法,
            # 最后只能去父类里面找
        return view

通过源码发现url匹配关系可以变形成

url(r'^login/',views.view) 
# FBV和CBV在路由匹配上是一致的 都是url后面跟函数的内存地址

2.当浏览器中输入login 会立刻触发view函数的运行

def dispatch(self, request, *args, **kwargs):
    # 这个就是父类(View)里的dispatch
    # Try to dispatch to the right method; if a method doesn't exist,
        # defer to the error handler. Also defer to the error handler if the
        # request method isn't on the approved list.
        # 我们先以GET为例
        if request.method.lower() in self.http_method_names:  
        # 判断当前请求方法是否在默认的八个方法内
        # 就是下面这八个方法
        # http_method_names = 
        # ['get', 'post', 'put', 'patch', 
        # 'delete', 'head', 'options', 'trace']
            handler = getattr(self, request.method.lower(), 
            # getattr就是反射,即通过字符串,来操作对象的属性或者方法
            # self谁来调,就是谁,这里是我们的MyLogin类产生的实例化对象
            # 反射获取我们自己写的类产生的对象的属性或者方法
            # 以GET为例 handler = getattr(self,'get','
            # 取不到报错的报错信息')
            # handler = get(request)
            self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)  
        # 直接调用我们自己写的类里面的GET方法
        # 源码中先通过判断请求方式是否符合默认的八个请求方法
        # 然后通过反射获取到自定义类中的对应的方法执行

截图说明

( 待补充 ,,,,)

每一步都截个图,第一步干嘛了,第二步干嘛了。。。

截了图写注释

django settings源码

疑问:为什么 settings.py 里的 键(本质是py 文件里的一个个变量名) 都必须是全大写,否则配置了也无效?为什么不配置就用默认值?

前提:
1.django除了暴露给用户一个settings.py配置文件之外 自己内部还有一个全局的配置文件
2.我们在使用配置文件的时候 可以直接直接导入暴露给用户的settings.py也可以使用django全局的配置文件 并且后者居多

from django.conf import settings

3.django的启动入口是manage.py

mange.py

#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
    # django在启动的时候 就会往全局的大字典中设置一个键值对  值是暴露给用户的配置文件的路径字符串
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day54.settings")
    # 此时 os.environ['DJANGO_SETTINGS_MODULE'] 就已经有值 day54.settings 了
    # 后续再运行代码,执行到 settings 模块的导入
    try:
        from django.core.management import execute_from_command_line
    except ImportError:
        # The above import may fail for some other reason. Ensure that the
        # issue is really that Django is missing to avoid masking other
        # exceptions on Python 2.
        try:
            import django
        except ImportError:
            raise ImportError(
                "Couldn't import Django. Are you sure it's installed and "
                "available on your PYTHONPATH environment variable? Did you "
                "forget to activate a virtual environment?"
            )
        raise
    execute_from_command_line(sys.argv)

from django.conf import settings # , global_settings 可以导一下全局配置文件,然后进去看看都有哪些配置

点到 settings 里去看源码

千万不要改第三方模块或者内置模块的源码!!!这里的注释是手动加过来的

怎么就到了 _setup 方法? 参考这个博客:Django 源码阅读(二): settings懒加载 (中间一大段,最后到了 _setup 函数)

settings = LazySettings() --> 直接看 LazySettings类 的 _setup方法(怎么走到这的,上面有文章) --> 根据代码一步步走进去看

settings 点进去的文件...

"""
Settings and configuration for Django.

Values will be read from the module specified by the DJANGO_SETTINGS_MODULE environment
variable, and then from django.conf.global_settings; see the global settings file for
a list of all possible variables.
"""

import importlib
import os
import time

from django.conf import global_settings
from django.core.exceptions import ImproperlyConfigured
from django.utils.functional import LazyObject, empty

ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"


class LazySettings(LazyObject):
    """
    A lazy proxy for either global Django settings or a custom settings object.
    The user can manually configure settings prior to using them. Otherwise,
    Django uses the settings module pointed to by DJANGO_SETTINGS_MODULE.
    """
    def _setup(self, name=None):
        """
        Load the settings module pointed to by the environment variable. This
        is used the first time we need any settings at all, if the user has not
        previously configured the settings manually.
        """
        # os.environ 可以看成是一个全局的大字典
        settings_module = os.environ.get(ENVIRONMENT_VARIABLE)  # 从大字典中取键为 DJANGO_SETTINGS_MODULE 的值(ENVIRONMENT_VARIABLE 的值是 "DJANGO_SETTINGS_MODULE")
        # manage.py 是程序的入口文件,默认第一个执行,在 mange.py 里面就已经 设置os.environ['DJANGO_SETTINGS_MODULE'] 的值是 'day54.settings' 了,所以这里取到的就是 'day54.settings' (django 暴露给用户可配置的配置文件 settings.py)
        # settings_module = day54.settings
        if not settings_module:
            desc = ("setting %s" % name) if name else "settings"
            raise ImproperlyConfigured(
                % (desc, ENVIRONMENT_VARIABLE))

        self._wrapped = Settings(settings_module)  # Settings(day54.settings),再进入到 Settings 中去

    def __repr__(self):
        # Hardcode the class name as otherwise it yields 'Settings'.
        if self._wrapped is empty:
            return '<LazySettings [Unevaluated]>'
        return '<LazySettings "%(settings_module)s">' % {
            'settings_module': self._wrapped.SETTINGS_MODULE,
        }

    def __getattr__(self, name):
        """
        Return the value of a setting and cache it in self.__dict__.
        """
        if self._wrapped is empty:
            self._setup(name)
        val = getattr(self._wrapped, name)
        self.__dict__[name] = val
        return val

    def __setattr__(self, name, value):
        """
        Set the value of setting. Clear all cached values if _wrapped changes
        (@override_settings does this) or clear single values when set.
        """
        if name == '_wrapped':
            self.__dict__.clear()
        else:
            self.__dict__.pop(name, None)
        super(LazySettings, self).__setattr__(name, value)

    def __delattr__(self, name):
        """
        Delete a setting and clear it from cache if needed.
        """
        super(LazySettings, self).__delattr__(name)
        self.__dict__.pop(name, None)

    def configure(self, default_settings=global_settings, **options):
        """
        Called to manually configure the settings. The 'default_settings'
        parameter sets where to retrieve any unspecified values from (its
        argument must support attribute access (__getattr__)).
        """
        if self._wrapped is not empty:
            raise RuntimeError('Settings already configured.')
        holder = UserSettingsHolder(default_settings)
        for name, value in options.items():
            setattr(holder, name, value)
        self._wrapped = holder

    @property
    def configured(self):
        """
        Returns True if the settings have already been configured.
        """
        return self._wrapped is not empty


class Settings(object):
    def __init__(self, settings_module):
        # 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(暴露给用户的配置文件)

        tuple_settings = (
            "INSTALLED_APPS",
            "TEMPLATE_DIRS",
            "LOCALE_PATHS",
        )
        self._explicit_settings = set()
        for setting in dir(mod):  # for 循环获取暴露给用户的配置文件中所有的变量名
            if setting.isupper():  # 判断变量名是否是大写,大写才执行下面的函数(非大写就过滤掉了)(这就是为什么 settings.py 文件里配置名必须全大写的原因)
                setting_value = getattr(mod, setting)  # 获取大写的变量名(配置)所对应的值

                if (setting in tuple_settings and
                        not isinstance(setting_value, (list, tuple))):
                    raise ImproperlyConfigured("The %s setting must be a list or a tuple. " % setting)
                setattr(self, setting, setting_value)  # 给 settings 对象设置键值对(我们写的 配置键值对)
                """
                用户如果配置了,就用用户的 ---> 字典的键存在的情况下把原来的替换掉
                用户如果没有配置,就用系统默认的 --> 如果没有第二次赋值替换,那么获取到的就是原来的值
                    本质就是利用字典的键如果存在就是替换的原理,实现了用户配置了就用用户的,用户没配置就用默认的
                d = {}
                d['user'] = 'jason'
                d['user'] = 'egon'  # 如果不写这个,那么下面打印出来的就会是 jason
                print(d.get('user'))
                # egon  # 把原来的顶掉了
                """
                self._explicit_settings.add(setting)

        if not self.SECRET_KEY:
            raise ImproperlyConfigured("The SECRET_KEY setting must not be empty.")

        if hasattr(time, 'tzset') and self.TIME_ZONE:
            # When we can, attempt to validate the timezone. If we can't find
            # this file, no check happens and it's harmless.
            zoneinfo_root = '/usr/share/zoneinfo'
            if (os.path.exists(zoneinfo_root) and not
                    os.path.exists(os.path.join(zoneinfo_root, *(self.TIME_ZONE.split('/'))))):
                raise ValueError("Incorrect timezone setting: %s" % self.TIME_ZONE)
            # Move the time zone info into os.environ. See ticket #2315 for why
            # we don't do this unconditionally (breaks Windows).
            os.environ['TZ'] = self.TIME_ZONE
            time.tzset()

    def is_overridden(self, setting):
        return setting in self._explicit_settings

    def __repr__(self):
        return '<%(cls)s "%(settings_module)s">' % {
            'cls': self.__class__.__name__,
            'settings_module': self.SETTINGS_MODULE,
        }


class UserSettingsHolder(object):
    """
    Holder for user configured settings.
    """
    # SETTINGS_MODULE doesn't make much sense in the manually configured
    # (standalone) case.
    SETTINGS_MODULE = None

    def __init__(self, default_settings):
        """
        Requests for configuration variables not in this class are satisfied
        from the module specified in default_settings (if possible).
        """
        self.__dict__['_deleted'] = set()
        self.default_settings = default_settings

    def __getattr__(self, name):
        if name in self._deleted:
            raise AttributeError
        return getattr(self.default_settings, name)

    def __setattr__(self, name, value):
        self._deleted.discard(name)
        super(UserSettingsHolder, self).__setattr__(name, value)

    def __delattr__(self, name):
        self._deleted.add(name)
        if hasattr(self, name):
            super(UserSettingsHolder, self).__delattr__(name)

    def __dir__(self):
        return sorted(
            s for s in list(self.__dict__) + dir(self.default_settings)
            if s not in self._deleted
        )

    def is_overridden(self, setting):
        deleted = (setting in self._deleted)
        set_locally = (setting in self.__dict__)
        set_on_default = getattr(self.default_settings, 'is_overridden', lambda s: False)(setting)
        return (deleted or set_locally or set_on_default)

    def __repr__(self):
        return '<%(cls)s>' % {
            'cls': self.__class__.__name__,
        }


settings = LazySettings()  # 基于模块的单例模式
网友评论