用于创建和更新的基于 Django 类的视图
Posted
技术标签:
【中文标题】用于创建和更新的基于 Django 类的视图【英文标题】:Django Class Based View for both Create and Update 【发布时间】:2013-06-16 01:49:27 【问题描述】:假设我想创建一个基于类的视图,它既更新又创建一个对象。从previous question 我发现我可以做以下事情之一:
1) 使用 2 个通用视图 CreateView
和 UpdateView
,我认为这意味着有两个 URL 指向两个不同的类。
2) 使用一个基于类的视图,它继承了基 View
,我认为这意味着有两个 URL 指向一个类(我创建的继承了 View
)。
我有两个问题:
a) 哪个更好?
b) ccbv.co.uk 显示了一个基础 View
,但我没有看到任何记录的 get、post 等方法,这是正确的吗?
【问题讨论】:
View
只能处理 get
、post
等,如果您自己定义此名称的方法......
那我应该使用多重继承吗?
这看起来很干净: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)
事实证明UpdateView
和CreateView
继承自完全相同的类和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_only
是True
并且视图找不到对象,则引发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】:要在UpdateView
和CreateView
之间共享代码,而不是创建组合类,您可以使用公共超类作为 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 中创建基于更新类的视图时,为我的文件上传使用表单集不起作用