Django Class Based View for both Create and Update
Simplest and basically the best solution of all 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
and that's it thanks @chriskief
Like suggested by @scubabuddha I ran into a similar situation and I used his answer modified as @mario-orlandi suggested in his comment:
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
I used this solution with Django 1.11 but I think it can work in Django 2.0.
Update
I confirm that this solution works with Django 2.0/2.1/2.2
I ran into a situation where I wanted something like this. Here's what I came up with (do note that if you're trying to use it as an update view and it can't find the requested object, it'll behave as a create view rather than throwing a 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)
It turns out that UpdateView
and CreateView
inherit from exactly the same classes and mixins. The only difference is in the get/post methods. Here's how they're defined in the Django source (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'
As you can see, the CreateView get and post methods set self.object = None
while the UpdateView
sets it to self.get_object()
. All I've done is combine those two in my CreateUpdateView.get_object
method which attempts to call the parent class' get_object
and returns None
rather than raising an exception if there is no object.
To serve a 404 page when used as an update view, you could probably override as_view
and pass it an update_only
boolean argument. If update_only
is True
and the view can't find the object, then raise the 404.