用于创建和更新的基于 Django 类的视图

Posted

技术标签:

【中文标题】用于创建和更新的基于 Django 类的视图【英文标题】:Django Class Based View for both Create and Update 【发布时间】:2013-06-16 01:49:27 【问题描述】:

假设我想创建一个基于类的视图,它既更新创建一个对象。从previous question 我发现我可以做以下事情之一:

1) 使用 2 个通用视图 CreateViewUpdateView,我认为这意味着有两个 URL 指向两个不同的类。

2) 使用一个基于类的视图,它继承了基 View,我认为这意味着有两个 URL 指向一个类(我创建的继承了 View)。

我有两个问题:

a) 哪个更好?

b) ccbv.co.uk 显示了一个基础 View,但我没有看到任何记录的 get、post 等方法,这是正确的吗?

【问题讨论】:

View 只能处理 getpost 等,如果您自己定义此名称的方法...... 那我应该使用多重继承吗? 这看起来很干净:chriskief.com/2015/01/19/… 【参考方案1】:

为什么需要通过单个视图来处理创建和更新?拥有两个独立的视图要简单得多,每个视图都继承自其各自的通用视图类。如果您愿意,它们可以共享相同的表单和模板,而且它们很可能是从不同的 URL 提供的,所以我看不出将它变成一个视图会得到什么。

所以:使用两个视图,一个继承自 CreateView,另一个继承自 UpdateView。这些几乎可以处理您可能需要的所有内容,而第二种方法则需要您自己重新发明***。如果您有一些在创建或更新对象时都使用的通用“管家”代码,可以选择使用 mixin,或者您可以创建自己的视图来涵盖这两个用例,继承自 CreateView 和 @ 987654324@.

【讨论】:

如果您不知道 POST 会导致创建还是更新怎么办?那么如何路由到好的视图(CreateView 或 UpdateView)? 没有绝对的规则...您还可以对其中没有 id 且 POST 数据中也没有 id 的 url 执行一些自定义 POST,只是期望,基于整个 POST 数据,服务器像成年人一样自己判断是创建还是更新:) 我觉得“它们很可能来自不同的 URL”的假设是完全错误的。一个常见的 URL 设计是,objects 的 URL 上的 GET 返回这些对象的列表,而同一 URL 的 POST 添加该类型的新对象。 “拥有两个独立的视图要简单得多” - 我不同意。我有通用的模型内务代码,需要在创建和更新时发生。我不明白为什么不应该有 CreateOrUpdateView。 url 可能包含 slug 而不是 id。 令人遗憾的是,SO 有这么多像这样的答案。基本上“不,你不应该那样做,OP,因为这不是我通常做的事情,我不想以你的方式思考。”【参考方案2】:

我遇到了我想要这样的东西的情况。这是我想出的(请注意,如果您尝试将其用作更新视图并且找不到请求的对象,它将表现为创建视图而不是抛出 404):

from django.views.generic.detail import SingleObjectTemplateResponseMixin
from django.views.generic.edit import ModelFormMixin, ProcessFormView

class CreateUpdateView(
    SingleObjectTemplateResponseMixin, ModelFormMixin, ProcessFormView
):

    def get_object(self, queryset=None):
        try:
            return super(CreateUpdateView,self).get_object(queryset)
        except AttributeError:
            return None

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        return super(CreateUpdateView, self).get(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        return super(CreateUpdateView, self).post(request, *args, **kwargs)

事实证明UpdateViewCreateView 继承自完全相同的类和mixin。唯一的区别在于 get/post 方法。以下是它们在 Django 源代码 (1.8.2) 中的定义方式:

class BaseCreateView(ModelFormMixin, ProcessFormView):
    """
    Base view for creating an new object instance.

    Using this base class requires subclassing to provide a response mixin.
    """
    def get(self, request, *args, **kwargs):
        self.object = None
        return super(BaseCreateView, self).get(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        self.object = None
        return super(BaseCreateView, self).post(request, *args, **kwargs)


class CreateView(SingleObjectTemplateResponseMixin, BaseCreateView):
    """
    View for creating a new object instance,
    with a response rendered by template.
    """
    template_name_suffix = '_form'


class BaseUpdateView(ModelFormMixin, ProcessFormView):
    """
    Base view for updating an existing object.

    Using this base class requires subclassing to provide a response mixin.
    """
    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        return super(BaseUpdateView, self).get(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        return super(BaseUpdateView, self).post(request, *args, **kwargs)


class UpdateView(SingleObjectTemplateResponseMixin, BaseUpdateView):
    """
    View for updating an object,
    with a response rendered by template.
    """
    template_name_suffix = '_form'

如您所见,CreateView 的 get 和 post 方法设置为 self.object = None,而 UpdateView 将其设置为 self.get_object()。我所做的只是将这两者结合到我的 CreateUpdateView.get_object 方法中,该方法尝试调用父类的 get_object 并返回 None,而不是在没有对象时引发异常。

要在用作更新视图时提供 404 页面,您可以覆盖 as_view 并传递一个 update_only 布尔参数。如果update_onlyTrue 并且视图找不到对象,则引发404。

【讨论】:

应该的。我正在尝试建立一个 Wiki 站点,而这正是我正在寻找的。​​span> 它比这更简单; get_object 将在未提供 pk 或 slug 时引发 AttributeError,但在指定 pk/slug 但无法找到时引发 DoesNotExist。这意味着您不需要 update_only 标志,因为您的新 get_object 只会在未指定 pk 时返回 None,即它会知道何时创建和何时更新! :-) 谢谢!只是想知道.. 为什么不像你那样简单地从 UpdateView 继承并覆盖 get_object()、get() 和 post()? 我接受了@MarioOrlandi 的建议,只是继承自UpdateView。不过,这是一个很好的课程答案! 这应该是批准的解决方案。干净整洁的解决方案。我试图通过添加更新 Formmixin(用于回答问题)来实现 Detailview(Questions)。这真的很有帮助。【参考方案3】:

您还可以使用受 Django 的 CBV 启发的 Django Smartmin。这是文档中的一个示例:https://smartmin.readthedocs.org/en/latest/quickstart.html

【讨论】:

【参考方案4】:

要在UpdateViewCreateView 之间共享代码,而不是创建组合类,您可以使用公共超类作为 mixin。这样,分离不同的关注点可能会更容易。并且 - 您可以重用大量现有的 Django 代码。

class BookFormView(PJAXContextMixin):
    template_name = 'library/book_form.html'
    form_class = BookForm

    def form_valid(self, form):
        form.instance.owner = self.request.user
        return super().form_valid(form)

    class Meta:
        abstract = True


class BookCreateView(BookFormView, CreateView):
    pass


class FormatUpdateView(BookFormView, UpdateView):
    queryset = Book.objects

【讨论】:

【参考方案5】:

就像@scubabuddha 建议的那样,我遇到了类似的情况,我使用他的答案修改为@mario-orlandi 在他的评论中建议:

from django.views.generic import UpdateView


class CreateUpdateView(UpdateView):

    def get_object(self, queryset=None):
        try:
            return super().get_object(queryset)
        except AttributeError:
            return None

我在 Django 1.11 中使用了这个解决方案,但我认为它可以在 Django 2.0 中使用。

更新

我确认此解决方案适用于 Django 2.0/2.1/2.2

【讨论】:

我必须捕获 Http404 异常而不是 AttributeError @Bobort,如果您收到 404 表明未找到在 URL 中传递的 pk。您可能仍想提高 404。【参考方案6】:

所有link中最简单也是最好的解决方案

class WorkerUpdate(UpdateView):
    form_class = WorkerForm

    def get_object(self, queryset=None):

        # get the existing object or created a new one
        obj, created = Worker.objects.get_or_create(mac=self.kwargs['mac'])

        return obj

就是这样 谢谢@chriskief

【讨论】:

我认为这可能是最优雅的答案,但我想知道是否有合适的方法来使用update_or_create 方法?在get_object 方法中使用它似乎不正确,但是......如果它有效?我想我就试试吧 如果要使其通用,可以从请求中设置用户为对象创建者:obj, created = Setting.objects.get_or_create(user=self.request.user)【参考方案7】:

如果您不需要引发 404 并且如果对象不存在并且希望所有字段为空白,则在第一次保存时创建对象并在存在时更新您可以使用它。

views.py

from django.views.generic import UpdateView


class CreateUpdateView(UpdateView):
    model = MyModel
    form_class = MyModelForm

    def get_object(self, queryset=None):
        return self.model.objects.filter(...).first()

forms.py

class MyModelForm(forms.ModelForm):

    class Meta:
        model = MyModel
        fields = [...]

【讨论】:

是的,....first() 如果没有匹配则返回 None。

以上是关于用于创建和更新的基于 Django 类的视图的主要内容,如果未能解决你的问题,请参考以下文章

基于 Django 类的视图 - 具有两个模型表单的 UpdateView - 一个提交

基于 Django 类的视图和通用视图详细信息使用

仅在 django 中创建基于更新类的视图时,为我的文件上传使用表单集不起作用

Django:在基于类的视图中包含媒体(css/js)

django.urls.exceptions.NoReverseMatch 基于类的列表视图

在基于 Django 类的视图中使用 modelformset_factory