2đź‘Ť
Why do you need to handle both create and update by a single View? It’s much simpler to have two separate views, each inheriting from its respective generic view class. They can share the same form and template if you wish, and they’re most likely served from different URLs, so I don’t see what would you get by making it into a single view.
So: use two views, one inheriting from CreateView
and the other from UpdateView
. These handle pretty much everything you might need, while the second approach would require you to reinvent the wheel yourself. If cases when you have some common “housekeeping” code that is used both when creating or updating objects, the option of using a mixin, or you can perhaps create your own view that covers both use cases, inheriting from both CreateView
and UpdateView
.
60đź‘Ť
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.
- [Django]-How do I make an auto increment integer field in Django?
- [Django]-How to save pillow image object to Django ImageField?
- [Django]-Get javascript variable's value in Django url template tag
21đź‘Ť
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
- [Django]-How to do a HTTP DELETE request with Requests library
- [Django]-Django 1.9 deprecation warnings app_label
- [Django]-Django count RawQuerySet
7đź‘Ť
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
- [Django]-How to completely uninstall a Django app?
- [Django]-Django delete superuser
- [Django]-Django: using blocks in included templates
3đź‘Ť
In case you don’t need to raise 404 and want all fields to be blank if the object doesn’t exists, create object when first saving and updating when it exists you can use this.
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 = [...]
- [Django]-Sort order of Django Admin records
- [Django]-Using JSON in django template
- [Django]-Testing nginx without domain name
0đź‘Ť
You can also use Django Smartmin which is inspired from CBV of Django. Here is an example from the documentation : https://smartmin.readthedocs.org/en/latest/quickstart.html
- [Django]-How to delete project in django
- [Django]-Django model "doesn't declare an explicit app_label"
- [Django]-Django Test Client Method Override Header
0đź‘Ť
To share code between your UpdateView
and CreateView
, instead of creating a combined class, you can use a common superclass as mixin. That way, it might be easier to separate the different concerns. And – you can re-use a lot of existing Django code.
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
- [Django]-Readonly models in Django admin interface?
- [Django]-Django – how to unit test a post request using request.FILES
- [Django]-Complete django DB reset
0đź‘Ť
In my case it was how to adapt the provided answers for UpdateView with Singleton. That’s why I put hardcoded pk=1.
With simplified class:
class MyClass(UpdateView):
...
def get_object(self):
obj, _ = MyClass.objects.update_or_create(pk=1)
return obj
https://docs.djangoproject.com/en/4.2/ref/models/querysets/#update-or-create
- [Django]-How to combine multiple QuerySets in Django?
- [Django]-Django database query: How to get object by id?
- [Django]-Where should utility functions live in Django?