" name="sm-site-verification"/>
侧边栏壁纸
博主头像
PySuper博主等级

千里之行,始于足下

  • 累计撰写 203 篇文章
  • 累计创建 14 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录
Web

Django CreateView、UpdateView 源码解析

PySuper
2021-01-14 / 0 评论 / 0 点赞 / 10 阅读 / 13242 字
温馨提示:
所有牛逼的人都有一段苦逼的岁月。 但是你只要像SB一样去坚持,终将牛逼!!! ✊✊✊

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

类的继承

Django中类的多继承

ListView 处理的都是多个对象

CreateView 处理的都是单个对象

CreateView_1
CreateView_2

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

docker

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)
0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区