super() 和 self
CreateView里面出现了super()和self的使用,这里就先解释一下
- MRO顺序:A B E G H L
- 当我们使用 self 调用的时候,是从A开始,一直到L结束
- 但是使用 super 的时候,是从B开始,一直到最后
- Mixin ==> 利用多继承实现了一种组合模式
- 多继承是Python的特性,而Mixin是利用了这种特性实现了组合模式
class BMinx:
def log(self):
print("B log")
def display(self):
print("B display") # A --> B --> display()
# self指的是实例化的A() ==> A的self指向的display2() --> C.display2()
super(BMinx, self).display2()
self.log() # self --> A.log()
class C:
def display2(self):
print("C display")
class A(BMinx, C): # TODO:混入
def log(self):
print("A log")
# self.log()
# super(A, self)到A类的父类中查找log() --> B.log(), 如果B里面没有再到A里面去找
super(A, self).log()
def display2(self):
print("AA")
test = A()
test.display()
CreateView
类的继承
ListView 处理的都是多个对象
CreateView 处理的都是单个对象
ModelFormMixin
class ModelFormMixin(FormMixin, SingleObjectMixin):
"""提供一种在请求中显示和处理ModelForm的方法."""
fields = None # 定义用户需要填写哪些字段
def get_form_class(self):
"""返回要在此视图中使用的表单类"""
# 如果定义了字段,又定义了模型类==>冲突,报错
if self.fields is not None and self.form_class:
raise ImproperlyConfigured(
"Specifying both 'fields' and 'form_class' is not permitted."
)
if self.form_class:
# 如果只定义了form_class,直接返回模型类
return self.form_class
else:
if self.model is not None:
# 使用自定义的model
model = self.model
elif getattr(self, 'object', None) is not None:
# 如果未自定义model,就在当前实例self中,获取到object对象
model = self.object.__class__
else:
# 如果没有model,也没有object对象,就返回query_set()的模型类
model = self.get_queryset().model
if self.fields is None:
# fields为空就直接报错
raise ImproperlyConfigured(
"Using ModelFormMixin (base class of %s) without "
"the 'fields' attribute is prohibited." % self.__class__.__name__
)
return model_forms.modelform_factory(model, fields=self.fields)
def get_form_kwargs(self):
"""返回用于实例化表单的关键字参数"""
# get_form_kwargs() --> FormMixin().get_form_kwargs()
kwargs = super().get_form_kwargs()
# 如果实例中有object对象
if hasattr(self, 'object'):
# 更新到字典,并设置key为instance
kwargs.update({'instance': self.object})
return kwargs
def get_success_url(self):
"""成功后页面跳转"""
if self.success_url:
# 如果自定义了success_url,则使用自定义
url = self.success_url.format(**self.object.__dict__)
else:
# 未自定义success_url
try:
# 将实例对象的详情url,赋值给url
url = self.object.get_absolute_url()
except AttributeError:
raise ImproperlyConfigured(
"No URL to redirect to. Either provide a url or define"
" a get_absolute_url method on the Model.")
return url
def form_valid(self, form):
"""表单校验"""
# 用户保存之后,就是模型类中的一个对象
self.object = form.save()
return super().form_valid(form)
FormMixin
class FormMixin(ContextMixin):
"""提供一种显示和处理请求中表单的方法。"""
initial = {} # 表单中自定义添加默认值
form_class = None # 获取表单类
success_url = None # 用户填写表单,提交之后要跳转的URL
prefix = None # 自定义表单中的html的id
def get_initial(self):
"""获取到initial的浅拷贝"""
return self.initial.copy()
def get_prefix(self):
"""获取自定义的表单html_id"""
return self.prefix
def get_form_class(self):
"""直接返回表单类,如果在视图中还需要对表单内容进行处理,可以重写这个方法"""
return self.form_class
def get_form(self, form_class=None):
"""返回要在此视图中使用的表单的实例, 如果没有定义form_class, 则需要重写get_form_class方法"""
if form_class is None:
form_class = self.get_form_class()
return form_class(**self.get_form_kwargs())
def get_form_kwargs(self):
"""Return the keyword arguments for instantiating the form."""
kwargs = {
'initial': self.get_initial(), # 表单默认填充的内容
'prefix': self.get_prefix(), # 表单使用的html前缀(html_id)
}
if self.request.method in ('POST', 'PUT'):
# 如果提交的使用的是POST、PUT请求
kwargs.update({
'data': self.request.POST, # 更新数据
'files': self.request.FILES, # 更新files(body中上传的文件)
})
return kwargs
def get_success_url(self):
"""获取提交数据后跳转的success_url"""
if not self.success_url:
raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.")
return str(self.success_url) # success_url may be lazy
def form_valid(self, form):
"""表单校验:如果表单有效,重定向到提供的URL"""
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form):
"""get_context_data()查找顺序:FormMixin --> SingleObjectMixin --> ContextMixin"""
# 获取上下文,并使用render_to_response返回
# render_to_response --> TemplateResponseMixin!!!
# 实现效果 ==> 用户什么都不填写,点击提交还是停留在当前页面中
# 将当前表单中的内容,添加到上下文,并渲染到了模板中
return self.render_to_response(self.get_context_data(form=form))
def get_context_data(self, **kwargs):
"""Insert the form into the context dict."""
# 如果form不在字典中,则添加,并指向用户提交的表单数据对象
if 'form' not in kwargs:
kwargs['form'] = self.get_form()
# super().get_context_data() --> SingleObjectMixin().get_context_data()
return super().get_context_data(**kwargs)
SingleObjectMixin
class SingleObjectMixin(ContextMixin):
"""
提供检索单个对象以进行进一步操作的能力。
创建、删除、详情, 操作的都是单个对象 ==> Single
对于列表 ==> 多个对象 ==>
"""
model = None # 关联模型列
queryset = None # 查询集
slug_field = 'slug' # 单个对象的slug名
context_object_name = None # 上下文名称
slug_url_kwarg = 'slug' # url里面使用的参数
pk_url_kwarg = 'pk' # 主键
query_pk_and_slug = False # 是都定义查询的组件或者slug
def get_object(self, queryset=None):
"""获取当前操作的对象,根据主键或者是slug来进行查询"""
if queryset is None:
queryset = self.get_queryset()
pk = self.kwargs.get(self.pk_url_kwarg)
slug = self.kwargs.get(self.slug_url_kwarg)
if pk is not None:
queryset = queryset.filter(pk=pk)
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 pk is None and slug is None:
raise AttributeError(
"Generic detail view %s must be called with either an object "
"pk or a slug in the URLconf." % self.__class__.__name__
)
try:
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):
"""返回对象的查询集"""
if self.queryset is None:
if self.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()
def get_slug_field(self):
"""指定模型类里面哪个字段名是slug"""
return self.slug_field
def get_context_object_name(self, obj):
"""获取上下文的名字"""
# 使用用户自定义的上下文名
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):
"""在FormMixin中被super查询后使用"""
context = {}
if self.object:
context['object'] = self.object # 在上下文中添加object,指向object00
context_object_name = self.get_context_object_name(self.object)
# 如果有其他关键字参数,就更新字典
if context_object_name:
context[context_object_name] = self.object
context.update(kwargs)
# super().get_context_data() --> ContextMixin().get_context_data()
return super().get_context_data(**context)
ContextMixin
class ContextMixin:
extra_context = None
def get_context_data(self, **kwargs):
# 通过setdefault, 设置默认的字典key为view, 并把当前实例加载到字典中
kwargs.setdefault('view', self)
# 如果定义了额外的上下文,就使用update更新这个字典
if self.extra_context is not None:
kwargs.update(self.extra_context)
# 最后返回这个字典
return kwargs
ProcessFormView
class ProcessFormView(View):
"""Render a form on GET and processes it on POST."""
def get(self, request, *args, **kwargs):
"""用户填写表单之后,直接返回上下文渲染的页面"""
return self.render_to_response(self.get_context_data())
def post(self, request, *args, **kwargs):
"""获取到表单"""
form = self.get_form()
if form.is_valid():
# self.form_valid() --> ModelFormMixin().form_valid()
return self.form_valid(form)
else:
# self.form_valid() --> FormMixin().form_invalid()
return self.form_invalid(form)
def put(self, *args, **kwargs):
"""有些浏览器不支持put,只支持post,这里就还是调用了post方法"""
return self.post(*args, **kwargs)
View
- head:类似于get,但是只显示请求的头信息
- options:获取服务器支持哪些HTTP方法
UpdateView
CreateView和UpdateView
观察上面二者的继承示例图,可以发现二者的继承关系是一样的
!
- 其实这里很容易理解的:
- 创建文章的时候肯定是
没有文章对象的
- 但是我们更新文章,这个时候的
文章是已经存在的
- 创建文章的时候肯定是
CreateView
class BaseCreateView(ModelFormMixin, ProcessFormView):
def get(self, request, *args, **kwargs):
self.object = None
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = None
return super().post(request, *args, **kwargs)
UpdateView
class BaseUpdateView(ModelFormMixin, ProcessFormView):
def get(self, request, *args, **kwargs):
self.object = self.get_object()
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
return super().post(request, *args, **kwargs)
评论区