• Django 源码阅读笔记(详细视图)


    SingleObjectMixin

    class SingleObjectMixin(ContextMixin):
        """
        提供检索单个对象,并对该对象操作的一些功能
        """
        model = None   # 模型类 eg:User
        queryset = None  # 查询集 eg: User.object.filter(active=True)
    
        # model 和 queryset 指定一个就行 不允许同时指定
        # queryset是具有可变值的类属性,因此在直接使用它时必须小心。在使用它之前,要么调用它的all()
        # 方法,要么使用它的方法进行检索 get_queryset(),以处理后台传回的拷贝。
    
        slug_field = 'slug'  # 模型中包含该字段的名称
        context_object_name = None  # 指定在模版的上下文中使用的变量的名称,所有的字段信息都会被包含
        # 在名为 context_object_name 的对象中,
        # 例如 context_object_name = forms
        # 假设 forms 类似这样 {'name': 'monkey'}
        # 在模版中  {{ forms.name }} 将会渲染出 name 的 值
    
        slug_url_kwarg = 'slug'  # 也是用来检索唯一的对象,但是它是为了安全而存在的,默认为slug
        # 用来和pk 一起获取唯一对象
        pk_url_kwarg = 'pk'  # 用来检索唯一的对象的关键信息,它默认的是pk 视作模型类的主键<id>字段 需要在URL中传入
        query_pk_and_slug = False  # 如果为 True 则确定唯一的对象时 会同时使用pk 和 字段 来确定 默认是False
    
        def get_object(self, queryset=None):
            """
            返回视图要显示的对象的信息
            默认情况下会从URL中获取pk或slug 参数来确定唯一的对象
            并将这个对象返回 只要返回的是一个具体的对象就可以 无论是谁的对象
            并不会被 model或query_set属性约束,在子类中可以覆盖这个方法返
            回任何的对象都可以
            """
    
            if queryset is None:
                # 如果没有定义 query_set 属性  执行 get_queryset 方法 该方法使用 model 属性返回
                # 一个指定 model 所有实例的查询集 如果 get_queryset 方法没有在子类中被重写
                queryset = self.get_queryset()
    
            pk = self.kwargs.get(self.pk_url_kwarg)   # 获取主键id值
            slug = self.kwargs.get(self.slug_url_kwarg)   # 获取slug 值
    
            # 如果pk 不为空,通过pk获取查询集 保存在 queryset中
            # 如果slug 不为空且 pk 也不为空 使用slug 过滤queryset的结果保存在queryset中
            # 如果都为空 爆抛出错误 无法找到唯一的对象
            if pk is not None:
                queryset = queryset.filter(pk=pk)
    
            # Next, try looking up by slug.
            if slug is not None and (pk is None or self.query_pk_and_slug):
                slug_field = self.get_slug_field()
                queryset = queryset.filter(**{slug_field: slug})
    
            # If none of those are defined, it's an error.
            if pk is None and slug is None:
                raise AttributeError("Generic detail view %s must be called with "
                                     "either an object pk or a slug."
                                     % self.__class__.__name__)
    
            try:
                # 从过滤后的查询集中获取唯一的对象 成功则返回这个对象 失败 报 404 错误 页面不存在
                obj = queryset.get()
            except queryset.model.DoesNotExist:
                raise Http404(_("No %(verbose_name)s found matching the query") %
                              {'verbose_name': queryset.model._meta.verbose_name})
            return obj
    
        def get_queryset(self):
            """
            通过 model 或 queryset 属性确定查询集 成功返回查询集 失败主动抛出错误
            """
            if self.queryset is None:
                if self.model:  # 如果 queryset 为None 且 model 属性存在 返回model的所有实例
                    return self.model._default_manager.all()
                else:
                    raise ImproperlyConfigured(
                        "%(cls)s is missing a QuerySet. Define "
                        "%(cls)s.model, %(cls)s.queryset, or override "
                        "%(cls)s.get_queryset()." % {
                            'cls': self.__class__.__name__
                        }
                    )
            return self.queryset.all()  # 如果 queryset 被子类重写了 则直接返回.all() 所有的对象集合
    
        def get_slug_field(self):
            """
            获取将由slug用于查找的slug字段的名称。
            """
            return self.slug_field
    
        def get_context_object_name(self, obj):
            """
            获取在上下文模版中使用的 用于对象的名称。
            用户指定了context_object_name 属性 则使用其值
            没有则使用 model 的名字 全部小写
            源码< self.model_name = self.object_name.lower() >
            """
            if self.context_object_name:
                return self.context_object_name
            elif isinstance(obj, models.Model):
                return obj._meta.model_name
            else:
                return None
    
        def get_context_data(self, **kwargs):
            """
            将单个对象 插入上下文字典中,以便于在模版中使用.
            子类如果覆盖此方法,一定要返回上下文字典 否则将无法在Template中组织上下文
            也就是没法渲染模版了
            """
            context = {}
            if self.object:
                context['object'] = self.object
                context_object_name = self.get_context_object_name(self.object)
                if context_object_name:
                    context[context_object_name] = self.object
            # 将原有的kwargs 传入字典中
            context.update(kwargs)
            return super(SingleObjectMixin, self).get_context_data(**context)
    

    BaseDetailView

    class BaseDetailView(SingleObjectMixin, View):
        """
        用于显示单个对象的基本视图
        因为继承View 因此 它必须实现View 约束的方法中的某个 一般来说是 get
        """
        def get(self, request, *args, **kwargs):
            self.object = self.get_object()
            context = self.get_context_data(object=self.object)  # 覆盖object <SignalObjectMixin 中 context.update(kwargs)>
            # 这样 context 原有的object 被更新为 传入的 self.object 事实上他们是一致的
            return self.render_to_response(context)  # 将模版和上下文字典渲染成响应对象 并返回
    
    

    SingleObjectTemplateResponseMixin

    class SingleObjectTemplateResponseMixin(TemplateResponseMixin):
        """
        绝大多数的功能都在父类中实现的,参看父类的源码解析
    
        该类的作用我认为有一下几点
        1 解耦合 将模版和上下文字典 结合生成响应对象的方法继承自父类的 render_to_response() 而该方法调用
        了确定 模版名称的方法 用来确定使用的 模版列表。该类可不依赖 父类 来获取模版
        2 允许不显示的给 template_name 值 来自己推断模版 
        		# 这样的设计 希望约束使用者 编写通用风格的模版名而减少代码量 提升代码的可读性和可维护行
        		# 但是 往往不利于让使用者知道他在干嘛~ 
        """
        template_name_field = None  # 默认的参数
        template_name_suffix = '_detail'  # django 主动的推断模版名时需要的后缀
    
        def get_template_names(self):
            """
            重写了 父类的方法
            作用 推断模版名、解耦
            返回用于请求的模板名称列表。 如果render_to_response被覆盖,则可能不会被调用。 返回以下列表:
    
             *视图上``template_name''的值(如果提供)
             *模板上的template_name_field字段的内容
             视图正在操作的对象实例(如果有)
             *``<app_label> / <model_name> <template_name_suffix> .html``
            """
            try:
                # 尝试获取 模版的 文件名列表 get_template_names() 被父类的render_to_response方法调用
                names = super(SingleObjectTemplateResponseMixin, self).get_template_names()
            except ImproperlyConfigured:
                # 如果没有指定 template_name 就实现自己的获取 方法 以解耦对父类的依赖
                # 初始化一个 列表
                names = []
    
                # 如果设置了self.template_name_field,则获取该字段的值 用作模版的名字
                if self.object and self.template_name_field:
                    name = getattr(self.object, self.template_name_field, None)
                    if name:
                        names.insert(0, name)
    
                # 最不明确的选项是默认的 < app >/< model >_detail.html;
                # _detail 是 template_name_suffix 的值
                # 仅在有关对象是模型时才使用此功能。
                if isinstance(self.object, models.Model):
                    object_meta = self.object._meta
                    names.append("%s/%s%s.html" % (
                        object_meta.app_label,
                        object_meta.model_name,
                        self.template_name_suffix
                    ))
                elif hasattr(self, 'model') and self.model is not None and issubclass(self.model, models.Model):
                    # 不指定 模版时 django 试图拼接出一个模版名,我不觉得这是一个很好的设计
                    # 虽然它使得框架更为的聪明,最重要的是 希望使用者使用 统一风格的模版名称
                    # 但是这不可避免的加重了 负担 同时 使用者 可能会不清楚他们做了什么
                    names.append("%s/%s%s.html" % (
                        self.model._meta.app_label,
                        self.model._meta.model_name,
                        self.template_name_suffix
                    ))
    
                # 如果 我们最终还是没有得到期望的 一个可用的模版名称的话 就只能抛出异常
                if not names:
                    raise
    
            return names
    

    DetailView

    class DetailView(SingleObjectTemplateResponseMixin, BaseDetailView):
        """
        渲染对象的“详细”视图。默认情况下,这是一个从 self.queryset 中查找的模型实例,但是
        视图将通过覆盖 self.get_object() 来渲染任意的对象 
        
        方法的流程
        
        dispatch()  请求分发
        http_method_not_allowed() 方法过滤
        get_template_names() 获取模版名
        get_slug_field() 获取用于确定对象的字段
        get_queryset() 获取查询集
        get_object() 使用 pk slug 等获取唯一的对象
        get_context_object_name() 获取模版中使用的 上下文字典的名称
        get_context_data() # 获取上下文字典数据
    
        render_to_response() 返回响应体
        
        """
        
    # 所有的事情都在父类中完成 尽可能的理解 MRO 以及每一个类 实现的方法,深刻的体会Mixin 拆分的精髓 我觉得这是django 中的精华。
    
  • 相关阅读:
    PHP——文件操作
    PHP——注册页面,审核页面,登录页面:加Session和Cookie
    ajax——优化0126(增删改查:添加查看详情,返回结果类型为JSON型,在窗口显示)
    ajax——三级联动下拉列表框的优化(简化页面,用jquery插件代替原来页面代码,返回处理数据类型为"TEXT")
    ajax——实现三级联动下拉列表
    通过view实现字段的只读、隐藏操作【转】
    OpenERP how to set the tree view limit
    OpenERP 疑问之一
    Django 安装
    OpenERP 中国财务模块 调整
  • 原文地址:https://www.cnblogs.com/monkey-code/p/13130053.html
Copyright © 2020-2023  润新知