I can't figure out how to use a ModelForm
in a FormView
so that it updates an already existing instance??
The form POSTs on this URL: r'/object/(?P<pk>)/'
I use a ModelForm
(and not directly an UpdateView
) because one of the fields is required and I perform a clean on it.
I'd basically like to provide the kwarg instance=...
when initializing the form in the FormView
(at POST) so that it's bound to the object whose pk is given in the url. But I can't figure out where to do that...
class SaveForm(ModelForm):
somedata = forms.CharField(required=False)
class Meta:
model = SomeModel # with attr somedata
fields = ('somedata', 'someotherdata')
def clean_somedata(self):
return sometransformation(self.cleaned_data['somedata'])
class SaveView(FormView):
form_class = SaveForm
def form_valid(self, form):
# form.instance here would be == SomeModel.objects.get(pk=pk_from_kwargs)
form.instance.save()
return ...
After some discussion with you, I still don't see why you can't use an UpdateView
. It seems like a very simple use case if I understand correctly. You have a model that you want to update. And you have a custom form to do cleaning before saving it to that model. Seems like an UpdateView
would work just fine. Like this:
class SaveForm(ModelForm):
somedata = forms.CharField(required=False)
class Meta:
model = SomeModel # with attr somedata
fields = ('somedata', 'someotherdata')
def clean_somedata(self):
return sometransformation(self.cleaned_data['somedata'])
class SaveView(UpdateView):
template_name = 'sometemplate.html'
form_class = SaveForm
model = SomeModel
# That should be all you need. If you need to do any more custom stuff
# before saving the form, override the `form_valid` method, like this:
def form_valid(self, form):
self.object = form.save(commit=False)
# Do any custom stuff here
self.object.save()
return render_to_response(self.template_name, self.get_context_data())
Of course, if I am misunderstanding you, please let me know. You should be able to get this to work though.
For any further visitors of this thread, yes, you can make a FormView
that acts like both a CreateView
and an UpdateView
. This, despite some opinions of other users, can make a lot of sense if you want to have a single form/URL/page for a web form to save some user data which can be optional but needs to be saved once and only once. You don't want to have 2 URLs/views for this, but just only one page/URL which shows a form, filled with previous data to be updated if a model was already saved by the user.
Think in a kind of "contact" model like this one:
from django.conf import settings
from django.db import models
class Contact(models.Model):
"""
Contact details for a customer user.
"""
user = models.OneToOneField(settings.AUTH_USER_MODEL)
street = models.CharField(max_length=100, blank=True)
number = models.CharField(max_length=5, blank=True)
postal_code = models.CharField(max_length=7, blank=True)
city = models.CharField(max_length=50, blank=True)
phone = models.CharField(max_length=15)
alternative_email = models.CharField(max_length=254)
So, you write a ModelForm
for it, like this:
from django import forms
from .models import Contact
class ContactForm(forms.ModelForm):
class Meta:
model = Contact
exclude = ('user',) # We'll set the user later.
And your FormView
with both "create" and "update" capabilities will look like this:
from django.core.urlresolvers import reverse
from django.views.generic.edit import FormView
from .forms import ContactForm
from .models import Contact
class ContactView(FormView):
template_name = 'contact.html'
form_class = ContactForm
success_url = reverse('MY_URL_TO_REDIRECT')
def get_form(self, form_class):
"""
Check if the user already saved contact details. If so, then show
the form populated with those details, to let user change them.
"""
try:
contact = Contact.objects.get(user=self.request.user)
return form_class(instance=contact, **self.get_form_kwargs())
except Contact.DoesNotExist:
return form_class(**self.get_form_kwargs())
def form_valid(self, form):
form.instance.user = self.request.user
form.save()
return super(ContactView, self).form_valid(form)
You don't even need to use a pk
in the URL of this example, because the object is retrieved from the DB via the user
one-to-one field. If you have a case similar than this, in which the model to be created/updated has a unique relationship with the user, it is very easy.
Hope this helps somebody...
Cheers.
You can use the post method of FormView to get the posted data and save to model using form.save(). Hope this will help.
Try this
class SaveForm(ModelForm):
somedata = forms.CharField(required=False)
class Meta:
model = SomeModel # with attr somedata
fields = ('somedata', 'someotherdata')
def __init__(self, *args, **kwargs):
super(SaveForm, self).__init__(*args, **kwargs)
def save(self, id):
print id #this id will be sent from the view
instance = super(SaveForm, self).save(commit=False)
instance.save()
return instance
class SaveView(FormView):
template_name = 'sometemplate.html'
form_class = SaveForm
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
form.save(kwargs.get('pk'))
else:
return self.form_invalid(form)
来源:https://stackoverflow.com/questions/21606739/django-update-model-with-formview-and-modelform