MultiValueDictKeyError at /boardeditor/ - django

I have a template that generates 2 forms. The forms are used to retrieve data via POST
The problem is when a user tamper with the data via POST for instance delete the input attribute name called process .
-----------------------------4827543632391\r\nContent-Disposition: form-data; name="csrfmiddlewaretoken"\r\n\r\nYWSIqoP9MjdKYlauwT5fSdxtFH2rcoaH\r\n-----------------------------4827543632391\r\nContent-Disposition: form-data; name="board"\r\n\r\n\r\n-----------------------------4827543632391\r\nContent-Disposition: form-data; name="process"\r\n\r\ndelete\r\n-----------------------------4827543632391--\r\n
I get this error If the user delete the value process from the name.
How can I combat this error and prevent users from tampering with the form.
MultiValueDictKeyError at /whiteboardeditor/
Key 'process' not found in <QueryDict: {u'': [u'delete'], u'csrfmiddlewaretoken': [u'YWSIqoP9MjdKYlauwT5fSdxtFH2rcoaH'], u'board': [u'2']}>
File "C:\o\17\mysite\pet\views.py" in WhiteBoardEditor
362. if request.POST['process'] == 'primary':
File "C:\Python26\lib\site-packages\django\utils\datastructures.py" in __getitem__
258. raise MultiValueDictKeyError("Key %r not found in %r" % (key, self))
Exception Type: MultiValueDictKeyError at /whiteboardeditor/
Exception Value: Key 'process' not found in <QueryDict: {u'': [u'delete'], u'csrfmiddlewaretoken': [u'YWSIqoP9MjdKYlauwT5fSdxtFH2rcoaH'], u'board': [u'2']}>
My html
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ boardpicture.as_p }}
<input type = "hidden" name="process" value= "primary" />
<input type = "submit" value= "save" />
</form>
{% for p in picture %}
<li>{{p.description}}
{% endfor %}
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ picturedelete.as_p }}
<input type = "hidden" name="process" value= "delete" />
<input type = "submit" value= "save" />
</form>
</form>
{% for pi in pict %}
<li>{{ pi.description }}
{% endfor %}
My views.py
def WhiteBoardEditor(request):
if not request.user.is_authenticated():
return HttpResponseRedirect(reverse('world:LoginRequest'))
picture = {}
pict = {}
if request.method == "POST":
forms = WhiteBoardPictureForm(request.user,request.POST,)
formss = PictureDeleteForm(request.user,request.POST,)
if request.POST['process'] == 'primary':
if forms.is_valid():
board = forms.cleaned_data['board']
if board:
boards = forms.cleaned_data['board']
picture = Picture.objects.filter(board=boards)
return render(request,'boardeditor.html',{
'picture':picture,
'boardpicture':WhiteBoardPictureForm(request.user),
'picturedelete':PictureDeleteForm(request.user)})
elif request.POST['process'] == 'delete':
if formss.is_valid():
pooh = formss.cleaned_data['board']
if pooh:
pooh = formss.cleaned_data['board']
pict = Picture.objects.filter(board=pooh)
return render(request,'boardeditor.html',{
'pict':pict,
'boardpicture':WhiteBoardPictureForm(request.user),
'picturedelete':PictureDeleteForm(request.user)})
return render(request,'boardeditor.html',{'board':WhiteBoardNameForm(request.user),'boardpicture':WhiteBoardPictureForm(request.user),'picturedelete':PictureDeleteForm(request.user)})
My views.py
class WhiteBoardPictureForm(forms.ModelForm):
def __init__(self, user, *args, **kwargs):
super(WhiteBoardPictureForm, self).__init__(*args, **kwargs)
self.fields['board'].queryset = Board.objects.filter(user=user)
class Meta:
model = Picture
fields = ('board',)
class PictureDeleteForm(forms.ModelForm):
def __init__(self, user, *args, **kwargs):
super(PictureDeleteForm, self).__init__(*args, **kwargs)
self.fields['board'].queryset = Board.objects.filter(user=user)
class Meta:
model = Picture
fields = ('board',)

You should validate the forms before processing, like form.is_valid() and return appropriate
page with form and errors if its not valid.
If forms are valid use form.cleaned_data for posted data rather than request.POST.
Here is how to use forms Using forms in view
So either you can add process input field in the form.
Or for this specific instance, you can add a check in your view before processing form as
if request.POST.has_key('process'):
# return error

Related

Error in as_crispy_field got passed an invalid or inexistent field

The page returns the error as_crispy_field got passed an invalid or inexistent field after SUBMIT Button is clicked. I was trying to submit the form when the error is raised. The record is SUCCESSFULLY saved into the database but an error is raised. A form for the template was created to set up the fields. The Form was instantiated in the views so that it could easily map the elements from the FORM to the TEMPLATE.
What caused the error and how can I resolve it?
FORM: Here is my form code
class ModelCreateDescriptionForm(forms.ModelForm):
name = forms.CharField(max_length=100)
description = forms.Textarea()
model_type = forms.ChoiceField(
widget = forms.Select,
choices = MODEL_TYPE_CHOICES,
)
def __init__(self, project_id=1, *args, **kwargs):
super(ModelCreateDescriptionForm, self).__init__(*args, **kwargs)
# project = Project.objects.get(id=project_id)
self.fields['model_type'].choices = MODEL_TYPE_CHOICES
self.fields['model_type'].required = True
class Meta:
model = Model
fields = ['name', 'description', 'model_type']
VIEW: Here is my view
def modelCreationDescription(request, **kwargs):
pk = kwargs.get('pk')
project = Project.objects.get(id=pk)
context = {}
context['project'] = project
if request.method == 'POST':
form = ModelCreateDescriptionForm(pk, request.POST)
name = request.POST.get(u'name')
description = request.POST.get(u'description')
model_type = request.POST.get(u'model_type')
if not(name and model_type):
messages.warning(request, f'Please fill model name and model type!')
if form.is_valid():
formDescription = form.save(commit=False)
try:
formDescription.name = name
formDescription.description = description
formDescription.model_type = model_type
formDescription.project = project
except:
messages.warning(request, f'Something wrong!')
return redirect('all-model-listview')
# save the form value
formDescription.save()
messages.success(request, f'Model description successfully created')
return render(request, 'models/pred_steps/modelSetTargetFeatures.html', {'model_form': formDescription })
else:
form = ModelCreateDescriptionForm(project_id=pk)
context = {
'form': form,
'project': project,
'create_model_description': True,
}
return render(request, 'models/pred_steps/modelCreateDescriptions.html', context)
HTML: This is the template that returning an error
<div class="card border-left-info mb-1 shadow">
<div class="col-xl-10 col-md-8 mb-1">
{% if project %}
<p><h5 class="text-info">Project: {{ project }}</h5></p>
{% endif %}
<form method="POST">
{% csrf_token %}
<fieldset class='form-group'>
{{ form.name|as_crispy_field }}
</fieldset>
<fieldset class='form-group'>
{{ form.description|as_crispy_field }}
</fieldset>
<fieldset class='form-group'>
{{ form.model_type|as_crispy_field }}
</fieldset>
<div class="form-group">
{% if project.id %}
<a class="btn btn-outline-secondary float-right" href="{% url 'all-model-listview' %}">Cancel</a>
{% endif %}
<button class="btn btn-outline-success" type="submit">Submit and Continue</button>
</div>
</form>
</div>
</div>
In case the next template is causing the error here are the codes
FORM:
class ModelSetTargetFeaturesForm(forms.ModelForm):
target_column_classification = forms.ChoiceField(
widget = forms.Select,
)
target_column_regression = forms.ChoiceField(
widget = forms.Select,
)
def __init__(self, project_id=1, *args, **kwargs):
super(ModelSetTargetFeaturesForm, self).__init__(*args, **kwargs)
project = Project.objects.get(id=project_id)
df = pd.read_csv(project.base_file, encoding='ISO-8859-1')
cols = df.columns
a_cols = np.column_stack(([cols, cols]))
self.fields['target_column_classification'].choices = a_cols
self.fields['target_column_classification'].required=True
self.fields['target_column_regression'].choices = a_cols
self.fields['target_column_regression'].required=True
# project = Project.objects.get(id=project_id)
class Meta:
model = Model
fields = ['target_column_classification', 'target_column_regression', ]
VIEW:
def modelSetTargetFeatures(request, **kwargs):
pk = kwargs.get('pk')
model = Model.objects.get(id=pk)
project = Model.objects.get(project=model.project.pk)
context = {}
context['model'] = model
context['project'] = project
if request.method == 'POST':
form = ModelSetTargetFeaturesForm(pk,request.POST)
target_column_classification = request.POST.get(u'target_column_classification')
target_column_regression = request.POST.get(u'target_column_regression')
if not(target_column_regression and target_column_classification):
messages.warning(request, f'Please fill model name and model type!')
if form.is_valid():
formTargetFeatures = form.save(commit=False)
formTargetFeatures.target_column_classification = target_column_classification
formTargetFeatures.target_column_regression = target_column_regression
# save the form value
formTargetFeatures.save()
messages.success(request, f'Model description successfully created')
return render(request, 'models/pred_steps/modelFeaturesSelection.html', {'model_form': formTargetFeatures })
else:
form = ModelSetTargetFeaturesForm(model=pk)
context = {
'form': form,
'project': project,
'model': model,
'create_model_description': True,
}
return render(request, 'models/pred_steps/modelSetTargetFeatures.html', context)
TEMPLATE:
<div class="card border-left-info mb-1 shadow">
<div class="col-xl-10 col-md-8 mb-1">
{% if project %}
<p><h5 class="text-info">Project: {{ project }}</h5></p>
<p><h5 class="text-info">Model: {{ name }}</h5></p>
{% endif %}
<form method="POST">
{% csrf_token %}
<fieldset class='form-group'>
{{ form.target_column_classification|as_crispy_field }}
</fieldset>
<fieldset class='form-group'>
{{ form.target_column_regression|as_crispy_field }}
</fieldset>
<div class="form-group">
{% if project.id %}
<a class="btn btn-outline-secondary float-right" href="{% url 'all-model-listview' %}">Cancel</a>
{% endif %}
<button class="btn btn-outline-success" type="submit">Next: Feature Selection</button>
</div>
</form>
</div>
</div>
Your view is making use of two different html templates:
'models/pred_steps/modelCreateDescriptions.html'
and
'models/pred_steps/modelSetTargetFeatures.html'
The first one is used for presenting the form, upon a GET request, and allowing the participant to input their data: return render(request, 'models/pred_steps/modelCreateDescriptions.html', context)
However, once the participant's data are POST'ed, this happens:
form = ModelCreateDescriptionForm(pk, request.POST)
# ... inside of if_valid
formDescription = form.save(commit=False)
# ...
formDescription.save()
# ...
return render(request, 'models/pred_steps/modelSetTargetFeatures.html', {'form': formDescription })
It's this second rendition that is causing problems, so I assume you are using crispy fields in the modelSetTargetFeatures.html template also. When rendering this other template, you seem to try to include formDescription as a form. However, formDescription is not a form, because form.save(commit=False) returns a Model object, not a form. Hence, django/crispy forms doesn't understand what's going on and rightly says that what you are trying to use as a form is not a valid form (since it's actually a Model instance). If you really do want to pass the form itself on to the other template, you can simply use {'form': form}.
You might also want to use a better name for your model than simply Model. It's very confusing and might cause bugs, since the name is identical to that of django.db.models.Model, which we subclass from for creating specific Models. Additionally, you might want to use the pythonic naming convention of using snakecase (e. g. my_function_based_view) for functions, and camelcase with the first letter capitalized for classes (e. g. MyFormClass).
(note that the above response refers to the code as you originally posted it - you've done some edits since I started responding, but the basic issue seems to be the same even after the edits)

how to edit form which have single or multiple MultichoiceFields

I have a form with MultipleChoiceField. I am able to save the data correctly. Now, if a user wants to edit that form he/she should see already selected items in the dropdown along with remaining all the other option. I want this as a function-based view. eg. I have 5 products in the dropdown and at the time of form submission, I selected products 1 and 2. Now, when I click on edit I should be able to see products 1 and 2 selected along with the other 3 products as unselected.Just help me with the edit view of this form. I am using same template for create and update. the code is messy.
models.py
class Lead(models.Model):
state = models.CharField(_("State"), max_length=255, blank=True, null=True)
type = models.CharField(max_length=20,choices=TYPE,blank=True,null=True)
products = models.ManyToManyField(Product,related_name='company_products',limit_choices_to=5,blank=True,null=True)
forms.py my customized form just sharing products section as code is getting bigger.
class LeadForm(forms.ModelForm):
product_queryset = []
products = forms.MultipleChoiceField(choices=product_queryset)
def __init__(self, *args, **kwargs):
assigned_users = kwargs.pop('assigned_to', [])
super(LeadForm, self).__init__(*args, **kwargs)
self.fields['products'].required = False
self.fields['products'].choices = [(pro.get('id'),pro.get('name')) for pro in Product.objects.all().values('id','name')]
views.py i am sharing just products section as the code is bit lenghty.
def update_lead(request, pk):
lead_record = Lead.objects.filter(pk=pk).first()
template_name = "create_lead.html"
users = []
if request.user.role == 'ADMIN' or request.user.is_superuser:
users = User.objects.filter(is_active=True).order_by('email')
else:
users = User.objects.filter(role='ADMIN').order_by('email')
status = request.GET.get('status', None)
initial = {}
if status and status == "converted":
error = "This field is required."
lead_record.status = "converted"
initial.update({
"status": status, "lead": lead_record.id})
error = ""
form=LeadForm(instance=lead_record,initial=initial,assigned_to=users)
if request.POST:
form = LeadForm(request.POST, request.FILES,
instance=lead_record,
initial=initial, assigned_to=users)
if form.is_valid():
if request.POST.getlist('products', []):
lead_obj.products.clear()
lead_obj.products.add(*request.POST.getlist('products'))
else:
lead_obj.products.clear()
status = request.GET.get('status', None)
success_url = reverse('leads:list')
if status:
success_url = reverse('accounts:list')
return JsonResponse({'error': False, 'success_url': success_url})
return JsonResponse({'error': True, 'errors': form.errors})
context = {}
context["lead_obj"] = lead_record
context["lead_form"] = form
context["teams"] = Teams.objects.all()
context['products'] = Product.objects.all()
context["assignedto_list"] = [
int(i) for i in request.POST.getlist('assigned_to', []) if i]
return render(request, template_name, context)
create_lead.html i am using this html for create as well as update view. I am just sharing products div section
<div class="form-group" style="height:20px;">
<label for="exampleInputEmail1">Product{% if products.field %}<span
class="error">*</span>{% endif %}</label>
<select multiple="multiple">
{% for product in products %}
<option value="{{product.pk}}" {% if product in products.company_products.all %} selected="" {% endif %}>{{product.name}}</option>
{% endfor %}
</select>
</div>
On template, you should to avoid render form "by hand". You should to render form using django forms rendering system.:
<form action="/your-name/" method="post">
{% csrf_token %}
{{ lead_form }}
<input type="submit" value="Submit">
</form>
If you need to render just this field manually:
<div class="form-group" style="height:20px;">
<label for="exampleInputEmail1">Product{% if products.field %}<span
class="error">*</span>{% endif %}</label>
{{ lead_form.products }}
</div>

django REST framework: HTML form returns filename instead of file

I have this HTML template
<html><body>
<form method="POST">
{% render_form submitSerializer %}
<input type="submit" value="Save">
</form>
</body></html>
this serializer
class SubmitSerializer(serializers.ModelSerializer):
submittedFile = serializers.FileField()
class Meta:
model = Submit
fields = {'submittedFile', 'result'}
read_only_fields = {'result'}
and this view
class SubmitView(APIView):
renderer_classes = [renderers.TemplateHTMLRenderer]
def get(self, request, *args, **kwargs):
return Response({'submitSerializer':submitSerializer}, template_name='singlelesson.html')
def post(self, request, *args, **kwargs):
submit = Submit(lesson=lessonInstance, user=self.request.user, submittedFile = self.request.data['submittedFile'])
serializer = SubmitSerializer(submit)
#compare_files returns true if two files contain same text
result = compare_files(correct_solution, self.request.POST.get('submittedFile'))
serializer.save(result = result)
return Response({'submitSerializer':serializer}, template_name='singlelesson.html')
Problem is that the form uploads not the file but its name and I get type error when comparing the two files. How do I make the form upload the file instead?
You should extract the file from the FILES QueryDict in the request object, not POST. e.g.:
self.request.FILES['submittedFile']
Or the django-rest-framework way:
request.data['submittedFile']
For more explanation check out:
http://www.django-rest-framework.org/api-guide/parsers/#fileuploadparser
EDIT: Also change form to:
<html><body>
<form method="POST" enctype="multipart/form-data">
{% render_form submitSerializer %}
<input type="submit" value="Save">
</form>
</body></html>
And SubmitView to:
class SubmitView(APIView):
parser_classes = (MultiPartParser,)
...

form wizard initial data for edit not loading properly in Django?

I have a three page form-list coming out of a single model. I could save the model first time, but when I want to edit the model, only the first form shows the initial value, subsequent forms does not show the initial data. but when I print the initial_dict from views, I can see all the initial views correctly. I followed this blog on form wizard.
Here is my model.py:
class Item(models.Model):
user=models.ForeignKey(User)
price=models.DecimalField(max_digits=8,decimal_places=2)
image=models.ImageField(upload_to="assets/", blank=True)
description=models.TextField(blank=True)
def __unicode__(self):
return '%s-%s' %(self.user.username, self.price)
urls.py:
urlpatterns = patterns('',
url(r'^create/$', MyWizard.as_view([FirstForm, SecondForm, ThirdForm]), name='wizards'),
url(r'^edit/(?P<id>\d+)/$', 'formwizard.views.edit_wizard', name='edit_wizard'),
)
forms.py:
class FirstForm(forms.Form):
id = forms.IntegerField(widget=forms.HiddenInput, required=False)
price = forms.DecimalField(max_digits=8, decimal_places=2)
#add all the fields that you want to include in the form
class SecondForm(forms.Form):
image = forms.ImageField(required=False)
class ThirdForm(forms.Form):
description = forms.CharField(widget=forms.Textarea)
views.py:
class MyWizard(SessionWizardView):
template_name = "wizard_form.html"
file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT))
#if you are uploading files you need to set FileSystemStorage
def done(self, form_list, **kwargs):
for form in form_list:
print form.initial
if not self.request.user.is_authenticated():
raise Http404
id = form_list[0].cleaned_data['id']
try:
item = Item.objects.get(pk=id)
###################### SAVING ITEM #######################
item.save()
print item
instance = item
except:
item = None
instance = None
if item and item.user != self.request.user:
print "about to raise 404"
raise Http404
if not item:
instance = Item()
for form in form_list:
for field, value in form.cleaned_data.iteritems():
setattr(instance, field, value)
instance.user = self.request.user
instance.save()
return render_to_response('wizard-done.html', {
'form_data': [form.cleaned_data for form in form_list], })
def edit_wizard(request, id):
#get the object
item = get_object_or_404(Item, pk=id)
#make sure the item belongs to the user
if item.user != request.user:
raise HttpResponseForbidden()
else:
#get the initial data to include in the form
initial = {'0': {'id': item.id,
'price': item.price,
#make sure you list every field from your form definition here to include it later in the initial_dict
},
'1': {'image': item.image,
},
'2': {'description': item.description,
},
}
print initial
form = MyWizard.as_view([FirstForm, SecondForm, ThirdForm], initial_dict=initial)
return form(context=RequestContext(request), request=request)
template:
<html>
<body>
<h2>Contact Us</h2>
<p>Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
{% for field in form %}
{{field.error}}
{% endfor %}
<form action={% url 'wizards' %} method="post" enctype="multipart/form-data">{% csrf_token %}
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{{ wizard.form }}
{% endif %}
</table>
{% if wizard.steps.prev %}
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.first }}">"first step"</button>
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">"prev step"</button>
{% endif %}
<input type="submit" value="Submit" />
</form>
</body>
</html>
EDIT:
one this I noticed is the following:
On the edit mode, i.e, when I am at the following url : http://127.0.0.1:8000/wizard/edit/1/,
it displays the first form data correctly, and when I click submit, it is not taking me to step-2 of edit mode, i.e the URL changes to http://127.0.0.1:8000/wizard/create/.
If upon clicking submit on edit url (like /wizard/edit/1) in the first step, same url is maintained then the form would get its initial data in next step. but I cannot figure out how to avoid the url from changing to /wizard/create
The error looks trivial. In your template the form action has wizards url, which is url of create view. Hence when the form is submitted it goes to /wizard/create.
To able to use the template for both views, you can remove the action attribute from form tag. The form will be submitted to current url which can be create or edit.
So change your template to have form tag as
<form method="post" enctype="multipart/form-data">
EDIT: To save item
Update your view as:
def done(self, form_list, **kwargs):
for form in form_list:
print form.initial
if not self.request.user.is_authenticated():
raise Http404
id = form_list[0].cleaned_data['id']
try:
item = Item.objects.get(pk=id)
print item
instance = item
except:
item = None
instance = None
if item and item.user != self.request.user:
print "about to raise 404"
raise Http404
if not item:
instance = Item()
#moved for out of if
for form in form_list:
for field, value in form.cleaned_data.iteritems():
setattr(instance, field, value)
instance.user = self.request.user
instance.save()
return render_to_response('wizard-done.html', {
'form_data': [form.cleaned_data for form in form_list], })

Django form fails to display

I have a simple Django form being passed through a view to a template where it should display, but, for a reason that I -- after 5 hours -- have failed to deduce, it does not. Any and all ideas welcome, I'm dying to solve this irksome problem.
I have the following Django form:
class BandAddToCartForm(forms.Form):
LENGTH_CHOICES = ( ('XS', 'XS'),
('S', 'S'),
('M', 'M') )
length = forms.Select(choices=LENGTH_CHOICES)
quantity = forms.IntegerField(widget=forms.HiddenInput())
band_sku = forms.CharField(widget=forms.HiddenInput())
# override the default __init__ so we can set the request
def __init__(self, request=None, *args, **kwargs):
self.request = request
super(BandAddToCartForm, self).__init__(*args, **kwargs)
# custom validation to check for cookies
def clean(self):
if self.request:
if not self.request.session.test_cookie_worked():
raise forms.ValidationError("Cookies must be enabled.")
return self.cleaned_data
It is passed to the template through the following view:
def show_custom_watches(request,
template_name="catalog/custom_watches.html"):
bands = Band.objects.all()
page_title = "Custom Watches"
meta_keywords = "custom, watches, beaded"
meta_description = "Custom beaded watches for every occassion."
return render_to_response(template_name,
locals(),
context_instance=RequestContext(request))
# need to evaluate the HTTP method
if request.method == 'POST':
#add to cart, create bound form
postdata = request.POST.copy()
form = BandAddToCartForm(request, postdata)
#check if posted data is valid
if form.is_valid():
#add to cart and redirect to cart page
cart.add_band_to_cart(request)
# if test cookie worked, get rid of it
if request.session.test_cookie_worked():
request.session.delete_test_cookie()
url = urlresolvers.reverse('show_cart')
return HttpResponseRedirect(url)
else:
# it's a GET, create the unbound form. Note request as a kwarg
band_form = BandAddToCartForm(request=request, label_suffix=':')
# set the test cookie on our first GET request
request.session.set_test_cookie()
return render_to_response("catalog/custom_watches.html",
locals(),
context_instance=RequestContext(request))
Lastly, here is the relevant bit of template where the form is failing to display:
{% for b in bands %}
<div class="watch_list_item">
<img class="" src="{{ MEDIA_URL }}images/bands/thumbnails/{{ b.image }}" alt="{{ b.name }}" />
<div class="watch_form_area">
<p>{{ b.name }}</p>
<form method="post" action="." class="cart">{% csrf_token %}
{{ band_form.as_p }}
<input type="submit" value="Add To Cart" name="add_product" alt="Add To Cart" class="add_to_cart_button" id="add_only_product" />
</form>
</div>
</div>
{% endfor %}
The Add to cart button appears as it should, but the length selector completely fails to display. Any ideas?
The first
return render_to_response(template_name,
locals(),
context_instance=RequestContext(request))
always happens before you initialise the form, remove it and it should work.