Django form with __init__ doesn't clean fields - django

I have a django form I'm attempting to add CAPTCHA support to. However, this requires me to pass request.session to the form. To accomplish that, I added this constructor to the form:
def __init__(self, request=None, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
self.request = request
I then instantiate the RegistrationForm by passing the request object to it. However, when submitting the form, it fails to clean any of the fields. It just fails validation and passes a blank field back to the template. Here is the registration code that fails:
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
...do registration...
else:
return render(request, 'template.htm', {'form': form})
No matter what I put in the fields, it never validates. In fact, it doesn't even look like it cleans the fields. I just get back a new blank form after hitting the register button, no errors, nothing.
Any ideas?

Based on the code you posted, it looks as though you are passing request.POST into the request parameter for your RegistrationForm. i.e. you are doing the equivalent of:
form = RegistrationForm(request=request.POST)
What you really want to do is this:
form = RegistrationForm(request=request, data=request.POST)
Try this and see if it works for you.

Related

Django - Previously page with data entered when form is invalid

Can I make my page return with the information previously entered when the form is invalid?
Views.py
def MyView(request):
[...code...]
if request.method == 'POST':
form = MyForm(request.POST or None)
if form.is_valid()
//do something and redirect to success page
else:
//back to the page with the information filled in by the user (HERE IS MY PROBLEM)
the line form = MyForm(request.POST or None) creates a MyForm object called form with request data assigned to it - its kinda like filled out form. The is_valid() method checks for errors in your form and adds particular errors to your form so this is now a filled out form with errors assigned. If you want to return this form to user you should add it to context so considering its a standard django function based view it should look like this:
def MyView(request):
[...code...]
if request.method == 'POST':
form = MyForm(request.POST or None)
if form.is_valid()
form.save()
return render(request, 'succes_page_template.html')
else:
return render(request, 'current_template.html', context = {'form': form})
if the form is invalid the next thing the user sees is same page where he filled out the form ('current_template.html') but with all the fields filled with data he/she already put it also form will have erros assigned to particular fields so you can print them. If you are new to Django I suggest getting into class based views from the start - they do the heavy lifting for you and refactoring + debugging becomes much easier. here is the link cheers!

How to Exclude Fields From Form Validation but Save the Cleaned Data

Edit: I'm not asking how to avoid the model fields being validated; I understand why it's required. I'm asking how I could skip over only the checks I wrote when I override the form's clean() method. I want the cleaned_data without it going through the other validation I wrote.
Let's say I have a ModelForm with the options to "save" and "Archive" (i.e. save for later) which only appear in the post_data if they are clicked. If the user clicks "save", the form would go through all the validation (including some custom logic in form.clean()) like normal. If the user clicks "archive" none of the custom validation in the form.clean() should run but the data should be saved.
In forms.py, I tried defining my form.clean() method as something like this:
def clean(self, full_clean=True):
cleaned_data = super().clean()
if full_clean:
# All my validation
...
...
return cleaned_data
Then in my views.py:
...
...
if request.method == "POST":
post_data = request.POST
if post_data.get('save') != None: # Do all validation
if form.is_valid():
form.save()
cleaned_form = form.cleaned_data
...
...
else: # If not valid
# Do something with the errors
...
elif post_data.get('archive') != None: # Don't do any validation
form.is_valid()
cleaned_form = form.clean(full_clean=False)
...
...
form.save()
...
The problem is, the form.clean() method is called when form.is_valid() is called so all the custom validation is still executed. I've also tried making full_clean default to False instead but then it won't validate properly when I want it to. If I remove the second form.is_valid() call, the cleaned_data doesn't get created and throws an error. Any suggestions would be much appreciated.
Override the init() and set a new field based on the post_data.
def __init__(self, *args, **kwargs):
archived = kwargs.pop('archived', None)
super(MyForm, self).__init__(*args, **kwargs)
self.fields['archived'] = forms.BooleanField(initial=archived, required=False)
and then actually add to your form
form = MyForm(request.post, archived=post_data.get('archived'))
In your custom clean() method, do a check for your self.fields.get('archived').
def clean(self):
if not self.fields.get('archived']:
# Do your validation
else it just skips over the custom validation.

Re-display table after form submit

Good afternoon,
I have a simple Django 2.2 application for users to check in equipment they have checked out. A table of users and items they have checked out dominates the top of the page. On the very bottom row, a single text/submit form. I would like this to happen:
user enters equipment id and submits
page re-displays with: name removed from table (if success), form cleared, success/fail message next to cleared form.
I am close. All of my logic and queries work, my item gets checked back in. However, the page re-renders with no table of users, just the form with the old data still in it.
views.py
class EquipmentReturn(View):
def get(self, request, *args, **kwargs):
# get checked out items for display table -this works
form = ExpressCheckInForm(request.POST)
return render(request, 'eq_return.html',
context={'master_table': master_table,
'form': form}
def post(self, request):
if request.method == 'POST'
form = ExpressCheckInForm(request.POST)
if form.is_valid():
# this checks the item back in (or not) and creates messages-works
else:
form - ExpressCheckInForm()
return render(request, 'eq_return.html', context={'form': form}
I know there is a better way to do this. For instance, my form would not appear until I declared it in the get function. How can I make all of this happen on one page? Thanks!
I think something like this might work. I assume that there is missing code here, for example where you get the master_table.
class EquipmentReturn(View):
def get(self, request, *args, **kwargs):
# get checked out items for display table -this works
form = ExpressCheckInForm()
return render(
request, 'eq_return.html',
context={'master_table': master_table, 'form': form},
)
def post(self, request):
form = ExpressCheckInForm(request.POST)
if form.is_valid():
# this checks the item back in (or not) and creates messages-works
# after saving the form or whatever you want, you just need to redirect back
# to your url. It will call get again and start over
return HttpResonseRedirect(reverse('your-url-name'))
return render(request, 'eq_return.html', context={'form': form})
It looks like you are still in the function based view mindset. Search differences and how to understand and use class based views.

passing empty queryset to form results in form_invalid

I have a model form that contains among other things a dropdown list of pupils (ModelMultipleChoiceField), which is initially empty but when a user selects a teacher, I get the list of pupils registered for this teacher via ajax request.
self.fields['pupil'] = forms.ModelMultipleChoiceField(queryset=Pupil.objects.none(),)
Everything works till the validation. Since initially queryset points to none, when a form is submitted it is returned with an error "It is invalid choice".
I can pass the entire set of pupils to the pupil field and then make it empty via jQuery but that looks ugly. What is the right way of doing it?
You can do it as follows. In this case first we render the form with empty queryset after you loading the choices with ajax. you will submit data with a post request. when we receive the POST request we are sending full queryset to the form.
In above case you explained the queryset is empty so you have got validation error as invalid choice. But, in this case we have full queryset so, it will work perfectly.
views.py
def sample_view(request):
if request.method == 'POST':
queryset = Pupil.objects.all()
form = SampleForm(request.POST, queryset=queryset)
# your code goes here
else:
queryset = Pupil.objects.none()
form = SampleForm(queryset=queryset)
# your code goes here
forms.py
class SampleForm(forms.ModelForm):
def __init__(self, *args, *kwargs):
queryset = kwargs.pop('queryset', None)
super(SampleForm, self).__init__(*args, **kwargs)
self.fields['pupil'] = forms.ModelMultipleChoiceField(queryset=queryset)
# your code

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.