通用类模型ListView的MVT配置
《Django高级实战-开发企业及问答网站》学习笔记3:通用类视图ListView常用属性和方法、ListView源码解析、Python中的多继承-MRO、C3线性化算法原理。
312阅读 · 2020-7-21 23:49发布
通用类模型ListView的MVT配置
models设置
- UUID字段
import uuid # 使用UUID模型,主键、默认值为uuid.uuid4,不允许编辑 uuid_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
关联外键字段:可以使用settings.AUTH_USER_MODEL或者类对象。
# 字段user关联USER应用,可以为空,外键被删除时当前字段设置为NULL,方便反查的名称为publisher。 user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.SET_NULL,related_name="publisher",verbose_name="用户") # 也可以用如下写法 # from .models import User # user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL,related_name="publisher",verbose_name="用户")
- 自关联字段:例如文章和评论放在一张表中,该字段相互关联,另外会再使用一个字段区分文章和评论。
# 关联自己,当关联的外键被删除时,该字段也被删除。 parent = models.ForeignKey('self', blank=True, null=True, on_delete=models.CASCADE, related_name='thread', verbose_name='自关联')
- 多对多字段:
liked = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, null=True, related_name='liked_news', verbose_name='点赞用户')
- 布尔字段:
reply = models.BooleanField(default=False, verbose_name='是否为评论')
- 时间字段:
created_at = models.DateTimeField(auto_now_add=True, verbose_name'创建时间')
- Meta配置:
class Meta: verbose_name = '首页' verbose_name_plural = verbose_name ordering = ("-created_at",) # 以时间倒序排列,值为元组
models相关函数:
# 可以根据自己的需要,在models中添加与models相关的方法。 def __str__(self): return self.content # 判断是否存在parent,并根据结果返回不同内容。** def get_parent(self): if self.parent: return self.parent else: return self
通用类视图ListView
常用方法和属性
- model:指定关联的模型类(既返回数据表的中的数据)。
- queryset:用于定义要返回的对象(可以进行简单的过滤)。
- paginate_by:每页显示的数量。# url中的?page参数
- page_kwarg:可以指定page参数改为自定义的名称。
- template_name:关联的前端模板文件,若不指定该字段,则会使用“模型类的小写加_list”作为关联的模板文件。
- context_object_name:定义QuerySet在模板文件中的名字。默认是“模型类名_list”或“object_list”。
- ordering:指定排序的字段,可以传入元组类型多字段排序。
- def get_ordering(self):用于定义较复杂的排序。
- def get_paginate_by(self,queryset):用于定义较复杂的分页。
- def get_queryset(self):获取视图的对象列表,返回queryset,重写该方法可以实现较复杂的过滤。默认返回models的所有对象。
- def get_context_data(self,*,object_list=None,**kwargs):用于添加额外的上下文。
def get_context_data(self,*,object_list=None,**kwargs): context = super.get_context_data() context['views'] = 500 return context
template设置
- 友好标签模板:setting.py中引入'django.contrib.humanize',template中添加{% load static humanize %}。
- naturaltime:humanize中时间友好显示的标签,使用{{ news.created_at|naturaltime }}可以将2020年7月21日21:00转换显示为“1小时前”。
通用类视图ListView源码解析
- ListView是列表视图,ListView主要是实现了对对象分页、排序重命名等通用方法。
- ListView的继承关系如下图(图片中的顺序是从右往左,和实际相反):
- BaseListView:该类中只定义了get方法,用于响应前端请求。
- View:django最基本的类视图,该类中定义了8种常用的http方法。
MultipleObjectMixin:该类中定义了queryset、paginate_by等属性(可以被重写),以及get_queryset、get_ordering、get_context_data等方法。(以下列出部分方法源码,这里不做细致的记录,如果有需要请自行查阅源码逻辑):
class MultipleObjectMixin(ContextMixin): ...省略... def get_queryset(self): """ Return the list of items for this view. The return value must be an iterable and may be an instance of `QuerySet` in which case `QuerySet` specific behavior will be enabled. """ if self.queryset is not None: queryset = self.queryset if isinstance(queryset, QuerySet): queryset = queryset.all() elif self.model is not None: queryset = 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__ } ) ordering = self.get_ordering() if ordering: if isinstance(ordering, str): ordering = (ordering,) queryset = queryset.order_by(*ordering) return queryset def get_ordering(self): """Return the field or fields to use for ordering the queryset.""" return self.ordering def get_context_data(self, *, object_list=None, **kwargs): """Get the context for this view.""" queryset = object_list if object_list is not None else self.object_list page_size = self.get_paginate_by(queryset) context_object_name = self.get_context_object_name(queryset) if page_size: paginator, page, queryset, is_paginated = self.paginate_queryset(queryset, page_size) context = { 'paginator': paginator, 'page_obj': page, 'is_paginated': is_paginated, 'object_list': queryset } else: context = { 'paginator': None, 'page_obj': None, 'is_paginated': False, 'object_list': queryset } if context_object_name is not None: context[context_object_name] = queryset context.update(kwargs) return super().get_context_data(**context)
- ContextMixin:该类中只定义了get_context_data方法。
- MutipleObjectTemplateResponseMixin:该类只定义了get_template_names方法,用于获取模板名字。
- TemplateResponseMixin:该类定义了模板名字等字段,使用render_to_response返回上下文给前端。
扩展知识:Python中的多继承-MRO
ListView通用类中出现了多个继承的情况,如果多个类中都编写了A方法,那么实际调用A方法时哪个类下的方法会被执行呢?
- MRO全称是方法解析顺序(Method Resolution Order)。它定义了Python中多继承存在的情况下,解释器查找函数解析的具体顺序。
- Python2.1:只有经典类(Old-style Class),MRO是根据DFS算法计算(深度优先搜索)。
- Python2.2:引入新式类(New-style Class),新式类使用BFS算法(广度优先搜索)。
- Python2.3-2.7:经典类和新式类共存,但新式类改为使用C3算法(C3线性化算法)。
- Python3之后:只有新式类,使用C3算法。
经典类的MRO——DFS算法
- 使用从左到右的顺序,深度优先遍历类的继承视图。
- 缺陷:如果遇到菱形继承(既父类B和C都继承了D),靠右继承的类重写了父类方法,但重写方法不会被执行。
新式类的MRO——BFS算法
- 使用广度优先的顺序,先查找所有父类的函数,再查找父类的父类,以此类推。
- 缺陷:如果左侧继承的祖父类和右侧的类存在相同方法,调用时会使用右侧类中的方法,违背了单调性(单调性指先从B以及B的父类开始找)。
新式类的MRO——C3算法
- 从左到右扫描得到的搜索路径,对于每一个节点解释器都会判断该节点是不是好的节点。
- 好的节点是指,N节点(好的节点)之后的搜索节点中,节点都不继承自N。
扩展知识:C3线性化算法的原理
- 参考资料:http://kaiyuan.me/2016/04/27/C3_linearization/ ,C3 线性化算法与 MRO
- C3线性化的方法解析列表公式(本文仅做笔记,不过于细致的讲解,如需理解具体含义请参考上一行的文章):
L[C(B1⋯BN)]=C+merge(L[B1],⋯,L[BN],B1⋯BN)
C3 线性化merge操作步骤
- 选取 merge 中的第一个列表记为当前列表 K。
- 令 h=head(K),如果 h 没有出现在其他任何列表的 tail 当中,那么将其加入到类 C 的线性化列表中,并将其从 merge 中所有列表中移除,之后重复步骤 2。
- 否则,设置 K 为 merge 中的下一个列表,并重复 2 中的操作。
- 如果 merge 中所有的类都被移除,则输出类创建成功;如果不能找到下一个 h,则输出拒绝创建类 C 并抛出异常。
C3线性化算法计算例子
演算过程
L(A) = A + merge(L(O), [O]) = [A,O] L(B) = [B,O] L(C) = [C,O] L(D) = [D,O] L(E) = [E,O] L(F) = [F,O] L(K1) = K1 + merge(L(C),L(A),L(B),(C,A,B)) = K1 + merge([C,O],[A,O],[B,O],(C,A,B)) = [K1,C] = merge([O],[A,O],[B,O],(A,B)) = [K1,C,A] = merge([O],[O],[B,O],(B)) = [K1,C,A,B] = merge([O],[O],[O]) = [K1,C,A,B,O] L(K2) = [K2,B,D,E,O] L(K3) = [K3,A,D,O] L(Z) = Z + merge(L(K1),L(K3),L(K2),(K1,K3,K2)) = Z + merge(L([K1,C,A,B,O]),L([K3,A,D,O]),L([K2,B,D,E,O]),(K1,K3,K2)) = [Z,K1] + merge(L([C,A,B,O]),L([K3,A,D,O]),L([K2,B,D,E,O]),(K3,K2)) = [Z,K1,C] + merge(L([A,B,O]),L([K3,A,D,O]),L([K2,B,D,E,O]),(K3,K2)) = [Z,K1,C,K3] + merge(L([A,B,O]),L([A,D,O]),L([K2,B,D,E,O]),(K2)) = [Z,K1,C,K3,A] + merge(L([B,O]),L([D,O]),L([K2,B,D,E,O]),(K2)) = [Z,K1,C,K3,A,K2] + merge(L([B,O]),L([D,O]),L([B,D,E,O])) = [Z,K1,C,K3,A,K2,B] + merge(L([O]),L([D,O]),L([D,E,O])) = [Z,K1,C,K3,A,K2,B,D] + merge(L([O]),L([O]),L([E,O])) = [Z,K1,C,K3,A,K2,B,D,E] + merge(L([O]),L([O]),L([O])) = [Z,K1,C,K3,A,K2,B,D,E,O]