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.
Related
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)
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], })
This is rather weird. I've been using Django forms for a long time and can't figure this out.
I have a small form with 1 field for "Quantity". Whenever I submit the form nothing happens and it NEVER get's into my condition to check if the request method is a POST. I have put a pdb in the code as well and it never reaches. I am not sure why. Here is the code.
views.py
def show_product(request, product_slug, template_name='catalog/product.html'):
product_cache_key = request.path
product = cache.get(product_cache_key)
if not product:
product = get_object_or_404(Product, slug=product_slug)
cache.set(product_cache_key, product, settings.CACHE_TIMEOUT)
categories = product.categories.filter(is_active=True)
if request.method == 'POST':
import pdb; pdb.set_trace() # it NEVER hit's this
postdata = request.POST.copy()
form = ProductAddToCartForm(request, postdata)
if form.is_valid():
cart.add_to_cart(request)
if request.session.test_cookie_worked():
request.session.delete_test_cookie()
url = urlresolvers.reverse('show_cart')
return redirect(url)
else:
form = ProductAddToCartForm(request=request)
form.fields['product_slug'].widget.attrs['value'] = product_slug
request.session.set_test_cookie()
context = RequestContext(request, locals())
return render_to_response(template_name, context)
forms.py
class ProductAddToCartForm(forms.Form):
quantity = forms.IntegerField(widget=forms.TextInput(attrs={'class': 'input-quantity', 'placeholder': 'Qty'}), error_messages={'invalid': 'Please enter a valid quantity.'}, min_value=1)
product_slug = forms.CharField(widget=forms.HiddenInput())
def __init__(self, request=None, *args, **kwargs):
self.request = request
super(ProductAddToCartForm, self).__init__(*args, **kwargs)
def clean(self):
if self.request:
if not self.request.session.test_cookie_worked():
raise forms.ValidationError("Sorry, please enable your cookies.")
return self.cleaned_data
template
<form method="post" action=".">
{% csrf_token %}
{{ form.quantity.errors }}
{{ form.quantity }}
<input type="submit" name="submit" value="Add to Cart" class="btn btn-danger" />
{{ form.product_slug }}
</form>
When I click "Add to Cart" the URL goes from http://localhost:8000/product/arm-glove/ to this one http://localhost:8000/product/arm-glove/?csrfmiddlewaretoken=RFG0F1Lg0Eu3GcDhtYwPPCpy9Oct5zCX&quantity=2&submit=Add+to+Cart&product_slug=arm-glove
What am I missing here?
Turns out there was an unclosed tag used for the search which is a GET request so the form's POST was never being seen.
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
This is almost certainly a newbie error but I can't work out what's wrong. I've read through the other similar questions on here but non seem to be quite the same.
I have a simple form which posts data but I always get a completely blank response.
Here's the code:
url.py
urlpatterns = patterns('shotgun.views',
# shotgun App URL patterns
url(r'^$', ControlPanelView.as_view(), name='index'),
)
form.py
class ProjectAssignmentForm(forms.Form):
projects = MultipleChoiceField(widget=forms.CheckboxSelectMultiple)
def __init__(self, user_projs, *args, **kwargs):
super(ProjectAssignmentForm, self).__init__(*args, **kwargs)
self.fields["projects"].choices = tuple([(item.name, item.name) for item in SGProject.objects.all() if item.name != "Template Project"])
self.fields["projects"].initial = user_projs
view.py
class ControlPanelView(SLBaseView):
template_name = "shotgun/index.html"
def get_context_data(self, **kwargs):
context = super(ControlPanelView, self).get_context_data(**kwargs)
sg_user = SGCustomNonProjectEntity01.objects.filter(code=self.request.user.get_full_name())[0]
projs = SGProject.objects.filter(Q(entities__entity_id=sg_user.id), Q(entities__type="CustomNonProjectEntity01"))
context["assigned_projs"] = projs
if self.request.method == "POST":
form = ProjectAssignmentForm(projs, self.request.POST)
if form.is_valid():
# update user projects logic
else:
form = ProjectAssignmentForm(projs)
context["pas_form"] = form
return context
template.html
<div class="full_width" >
<h4 class="no_pad" >{{ user.first_name|upper }}'S CONTROL PANEL</h4>
</div>
<div class="full_width top_tab">
<div class="border border_pad">
<h6>PROJECTS YOU ARE ASSIGNED TO</h6>
{% if assigned_projs %}
<form action="/intranet/shotgun/" method="post">{% csrf_token %}
<div class="border_pad">
{{ pas_form.projects.errors }}
{{ pas_form.projects }}
<input type="submit" value="Update" />
</div>
</form>
{% else %}
<p>You are not assigned to any projects.</p>
{% endif %}
</div>
</div>
I have the csrf middleware enabled in my settings.py
The behaviour I would expect from all this would be:
Unbound form - display the page with pre-populated data
Bound invalid - display the page with selected data
Bound valid - display the page with updated data
The unbound data page displays as expected but as soon as the post request is created by clicking the Update button on the form the a blank page is displayed. I tried throwing an exception after the if self.request.method == "POST" statement but the code doesn't seem to get this far. I'm really not sure what's going on.
There is a syntax error in your views.py file.
if self.request.method == "POST":
form = ProjectAssignmentForm(projs, self.request.POST)
should be
if self.request.method == "POST":
form = ProjectAssignmentForm(projs, self.request.POST)
Do you have debugging turned off? That would explain why you're not getting any output. Because there is a fatal error django would print an error report, but if debugging is off it will email the error report to the admin listed in settings.py instead.
Check that in settings.py:
DEBUG = True