Django Form field initial value on failed validation - django

how do I set the value of a field element after a form has been submitted but has failed validation? e.g.
if form.is_valid():
form.save()
else:
form.data['my_field'] = 'some different data'
I don't really want to put it in the view though and would rather have it as part of the form class.
Thanks

The documentation says:
If you have a bound Form instance and want to change the data somehow, or if you want to bind an unbound Form instance to some data, create another Form instance. There is no way to change data in a Form instance. Once a Form instance has been created, you should consider its data immutable, whether it has data or not.
I cannot really believe that your code works. But ok. Based on the documentation I would do it this way:
if request.method == 'POST':
data = request.POST.copy()
form = MyForm(data)
if form.is_valid():
form.save()
else:
data['myField'] = 'some different data'
form = MyForm(initial=data)

I ended up doing
if request.method == 'POST':
new_data = request.POST.copy()
form = MyForm(data=new_data)
if form.is_valid():
form.save()
else:
new_data['myField'] = 'some different data'
Hope this helps someone

You can put it in the form class like this:
class MyForm(forms.Form):
MY_VALUE = 'SOMETHING'
myfield = forms.CharField(
initial=MY_VALUE,
widget=forms.TextInput(attrs={'disabled': 'disabled'})
def __init__(self, *args, **kwargs):
# If the form has been submitted, populate the disabled field
if 'data' in kwargs:
data = kwargs['data'].copy()
self.prefix = kwargs.get('prefix')
data[self.add_prefix('myfield')] = MY_VALUE
kwargs['data'] = data
super(MyForm, self).__init__(*args, **kwargs)
The way it works, is it tests to see if any data has been passed in to the form constructor. If it has, it copies it (the uncopied data is immutable) and then puts the initial value in before continuing to instantiate the form.

Related

How to use form.cleaned_data django

After form.is_valid(), we get form.cleaned_data. How can i use this cleaned data on the next page.
For example, after the form page is processed we redirect the customer to next page, where I want to use the cleaned_data's info like name, contact, address..etc fields to be shown in next page.
def ind(request):
if request.method == 'POST':
form = form_name(request.POST)
if form.is_valid():
print(form.cleaned_data)
return render(request, 'app_one/abc.html', {'data': form.cleaned_data})
# form.save(commit=True)
# return render(request,'app_one/index.html')
else:
form=form_name()
return render(request,'app_one/index.html',{'form':form)
We will have a validated data after calling the form.is_valid() method. Once we have a validated data then we can use as we like.
For your case
customer details which are filled on the first page need show those details on the second page as receipt.
You can create a model named Reciept and save the details in the model for future reference. If you want these details in the other page views then simply pass the model object in context to render the details.
You can use the cleaned data like below
def ind(request):
if request.method == 'POST':
form = form_name(request.POST)
if form.is_valid():
context = {}.update(form.cleaned_data)
return render(request, 'app_one/abc.html', context)
# form.save(commit=True)
# return render(request,'app_one/index.html')
else:
form=form_name()
return render(request,'app_one/index.html',{'form':form)
Example Form:
class MyForm(forms.Form):
reciept_num = forms.CharField()
consider above form as an example
You can access the reciept_num data in template using the name reciept_num.
You can assign the cleaned_data to variables as usual for forms
e.g. your_data=form.cleaned_data['your_data']
After that, pass those variables to context.
e.g. context = {
'your_data':your_data
}
Lastly return the template.
e.g. return(request,'template.html',context=context)
At the 'template.html', use the variables as {{your_data}}.

Django request.POST as an argument of a form

I am having a hard time wrapping my head around what request.POST is doing as a argument in the following example:
def addauthorView(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
first_name = form.cleaned_data['firstname']
last_name = form.cleaned_data['lastname']
user_email = form.cleaned_data['email']
c = AuthorModel(firstname=first_name, lastname=last_name, email=user_email)
c.save()
return HttpResponseRedirect('thanks/')
else:
form = ContactForm(request.POST)
return render(request, 'addauthor.html', {'form': form})
So I know that this works, but for some reason I cannot understand the magic that is happening with form = ContactForm(request.POST). Why does the ContactForm need the request.POST argument? What is happening behind the scenes?
Extra question, why is form = ContactForm(request.POST) then repeated in the else: block. Why is that helpful and when is that useful? Examples?
In a nutshell, request.POST is simply the data that was sent when the form was submitted. It's a dictionary of what the user submitted for firstname, lastname and email in your code sample. For those that come from a PHP background, it's what is provided in $_POST.
form = ContactForm(request.POST) binds the data to the form class so Django can do fun stuff like validate inputs with is_valid().
Why then, would you add request.POST to the else: block? Well, have you ever submitted a form to a website and when there was an error you had to completely fill out the form again? That's a crappy user experience, right? By sending the form back to the user with the data from request.POST, you can re-render what the user inputed - along with helpful extras such as error messages - so they can fix them and resubmit.
EDIT: To expand, here is the init method from the BaseForm class in Django:
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
initial=None, error_class=ErrorList, label_suffix=None,
empty_permitted=False):
self.is_bound = data is not None or files is not None
self.data = data or {}
self.files = files or {}
self.auto_id = auto_id
self.prefix = prefix
self.initial = initial or {}
self.error_class = error_class
# Translators: This is the default suffix added to form field labels
self.label_suffix = label_suffix if label_suffix is not None else _(':')
self.empty_permitted = empty_permitted
self._errors = None # Stores the errors after clean() has been called.
self._changed_data = None
# The base_fields class attribute is the *class-wide* definition of
# fields. Because a particular *instance* of the class might want to
# alter self.fields, we create self.fields here by copying base_fields.
# Instances should always modify self.fields; they should not modify
# self.base_fields.
self.fields = copy.deepcopy(self.base_fields)
When you pass request.POST to your form class, you're really doing data=request.POST. That in turn triggers the self.is_bound = True

Django Overwrite existing instance in table

I'm creating a form in one page, then in another page I'm trying to pull out the form (populated with the data saved in it already) and would like to make changes to it so that when I save it it overwrites the instance instead of creating another one.
def edit(request):
a = request.session.get('a', None)
if a is None:
raise Http404('a was not found')
if request.method == 'POST':
form = Name_Form(request.POST, instance=a)
if form.is_valid():
j = form.save( commit=False )
j.save()
else:
form = Name_Form( instance = a )
This is the code I have for the "editting form" view.. When I open this page the form is successfully prepopulated with all the data. However, when I make changes and save, it does not overwrite the existing instance, instead it creates a new one.
Any ideas?
Have a look here: https://docs.djangoproject.com/en/dev/ref/models/instances/#how-django-knows-to-update-vs-insert
I think this may help you.
Update:
What about trying a more "explicit" way.
Assume, id_of_Name stores only the id or pk of your model which you want to edit (I assume the model is called "Name"). Then just retrieve the id/pk from session to query your db for the model instance. Also try to directly call the save method on the form.
def edit(request):
id_of_Name = request.session.get('a', None)
if id_of_Name is None:
raise Http404('id_of_Name was not found')
instance = Name.objects.get(pk=id_of_Name)
if request.method == 'POST':
form = Name_Form(request.POST, instance=instance)
if form.is_valid():
form.save()
else:
form = Name_Form( instance = instance )

Django form, request.post and initial

I have a django form where I need to set a value for validation purposes which is not passed in as part of the standard Post request.
In my code I currently have something like this:
if request.method == 'POST':
postform = CreatePostForm(request.POST, request.FILES, initial={'post_type':post.post_type})
if postform.is_valid():
.....
The value post_type is a selection with a value of something like 'QUE'
The issue I am having is that this does not appear to be valid. Does anyone have any suggestions on how to add the post_type value into the CreatePostForm class before the validation takes place.
Please note I do not want to expose this value on the form so including it as part of the post is not an option.
One approach is to make it a property on the form class that you hand in as an argument when you instantiate it:
class CreatePostForm(forms.Form/ModelForm):
def __init__(self, post_type, *args, **kwargs):
super(CreatePostForm, self).__init__(*args, **kwargs)
self.post_type = post_type
postform = CreatePostForm(post_type=post.post_type, request.POST, request.FILES)
Hope that helps you out.

form instance of a model gives id=None Django

I'm misunderstanding something! If my model is not saved, it does not have an id associated with it. So if I have something like this:
views.py (inserting or editing existing info uses the same modelform)
def insert_or_modify(request, id=None):
if id is not None:
book = BookModel.objects.get(pk=id)
else:
book = BookModel()
if request.method == 'POST':
form = BookInfoForm(request.POST, request.FILES, instance=book)
if form.is_valid():
form.save()
....
return render_to_response(...)
I also have an image and use upload_to for the imagefield. There are two problems: id is None and I'm not sure how to manipulate/save the instance=book so that I would actually get an id. The second problem is the location I save my data to is way off. Even though the template tag book.img.url has the desired location of the book at http:127.0.0.1:8000/folder1/media/id/, the actual location is somewhere else:
Where I want to save my image to:
/project/folder1/media/id/
where id is the book id.
What I actually get:
/project/id/
(But 'id' becomes 'None' since it doesn't exist!)
My previous code worked. It would save to the correct location, but with this current code, it doesn't work. So the saving issue doesn't seem like it's due to settings.py since it worked previously.
EDIT: removed non-code from code formatting area
EDIT: I found out why I wasn't saving to the correct location. As it turned out, I forgot to uncomment something when I last modified settings.py. Saving to the location works now! Sorry guys!
EDIT: I think the id=None problem is caused by form.save(). If I avoid doing that and just save to the model directly, I don't have this problem.
Id assigns only on saving objects when you use autoincrement id field (default).
You can save item before handling image, and then save image.
May be you can not worry about image name - becouse django file storages dont have troubles with same image names. So if you just save file "image.png", and then save another file with name "image.png" - then it will be saved as "image_1.png"
def add_or_create(request, item_id=None):
item = get_object_or_404(BookModel, id=item_id) if item_id else None
form = BookInfoForm(request.POST or None, request.FILES or None, instance=book) # assume it is ModelForm
if form.is_valid():
book = form.save()
For the first part:
def insert_or_modify(request, id=None):
if id:
book = BookModel.objects.get(pk=id)
if request.method == 'POST':
form = BookInfoForm(request.POST, request.FILES, instance=book)
if form.is_valid():
save_book = form.save()
# use save_book as your instance of BookModel
....
else:
if request.method == 'POST':
form = BookInfoForm(request.POST, request.FILES)
if form.is_valid():
save_book = form.save()
# use save_book as your instance of BookModel
....
save_book = form.save() allows you to then use save_book as your saved instance of BookModel, and save_book.id is its id.
def create_id(instance,some_id=None):
if some_id is None:
obj=Post.objects.first()
new_id=obj.id
new_id+=1
return new_id
else:
return some_id
def pre_save_post_receiver(sender, instance, *args, **kwargs):
if not instance.id:
instance.id = create_id(instance)
pre_save.connect(pre_save_post_receiver, sender=Post)