Django template loop missing <form> element on first iteration - django

I've got a really weird error, and I don't know whether it's the django template error or something weird happening in the DOM.
I have a list of dictionaries called data_table, which I pass as part of the context to the template engine. I'm trying to create rows in a table, and one of the cells is this:
{% for row in data_table %}
<tr>
<!-- a few cells ... followed by the final cell which contains a form -->
<td>
<form action="/remove-survey-question/" method="POST">
<a href="#" onclick="removeSurveyQuestion(this)">
<image class="trash-svg" src="{% static "trash.svg" %}">
</a>
<input type="hidden" name="question_type" value="{{ row.question_type }}" />
<!-- some more hidden inputs... -->
</form>
</td>
</tr>
{% endfor %}
This works perfectly except for the first row. The javascript function throws an error because, when I use the browser's DOM inspector, the parent <form> node is missing! All the child nodes of the missing <form> are present though.
How could this happen? I'm using django 1.9.2.

I just compared the source vs the DOM, and the <form> tag is present in the source, but not the DOM. The reason was that I had inadvertently created a nested form (which is not allowed).

Related

How to pass a <select> in a View using Django

I was trying to make a cart system in Django and wanted to pass Size and the Quantity of product as <Select>
input in View.
My Template have :
<ul class="list-unstyled">
Select Size:
<select name="sizes">
{% for size in product.sizes.all %}
<li class="list-item list-inline-item"><option value="{{size.nameSize}}">{{size.nameSize}}</option> </li>
{% endfor %}
</select>
</ul>
This is how it looks :
But when i Submit it using the Add to Cart Button i get error:
This is the code in the view:
def add_item(request,pk):
product = get_object_or_404(Product,pk=pk)
size = request.POST['sizes']
selectsize = Size.objects.get(nameSize=size)
user = request.user
usercart = Cart.objects.get(owner=user)
newitem = CartItems.objects.create(cart = usercart,product=product,size=selectsize)
items = usercart.cartitems
return render(request,'cart.html',{'cartitems':items})
I am trying to use the name of the size from the Template and compare the size name i have in the database for that product Using:
selectsize = Size.objects.get(nameSize=size)
I was able to get size with name 36 so i wanted to pass the value 36 from the template to the variable size using post.
But i get the error mentioned which i believe is because name for the <select> is common in all the <option>.
If i can either get an alternate way to do that or solve this error both type of solutions are welcomed.
*I am not using Django Forms because i don't know how to have django form display like i am displaying my products in cart and on the product page.
ANSWER
I was missing a submit button and was rather using a <a href="{% url 'add_item' product.pk %}>Add To Cart</a>" to submit the form which was not working.
Now i replaced it with <button class="btn btn-success" style="margin-top: 10px;" type="submit">Add To Cart New</button>
And the form Action is given the link i was trying to go to.
<form method="post" enctype="multipart/form-data" action="{% url 'add_item' product.pk %}">
A silly mistake on my side.
Thanks for the answers.
Expanding on what #Yevhenii M. said, and talking particularly about the MultiValueDictKeyError:
This error happens when the given key (sizes in this case) is not found in the POST dict. This might be happening (i'm only guessing, since you didn't post the full html code), because you didn't put the corresponding <form> tag surrounding the select.
So, the final code would look something like:
<form action="url-to-send-form-data" method="POST">
{% csrf_token %}
<select name="sizes">
{% for size in product.sizes.all %}
<option value="{{size.nameSize}}">{{size.nameSize}}</option>
{% endfor %}
</select>
</form>
The {% csrf_token %} is needed in order to protect you against Cross Site Request Forgery attacks (more info: https://docs.djangoproject.com/en/2.2/ref/csrf/)
EDIT: Now that I take a closer look, the error message shows that the url is being called with a GET request (maybe because of trying to access to /item_added/1 straight from the browser's url). That is why django can't find the sizes key.
One common way to call the url via post, is as shown in the code snipet above, and adding a submit button to the html:
...
<button type="submit">Submit</button>
</form>
You don't need to use <ul> tag here.
You can write:
Select Size:
<select name="sizes">
{% for size in product.sizes.all %}
<option value="{{size.nameSize}}">{{size.nameSize}}</option>
{% endfor %}
</select>
and result will be the same.
Since you didn't specify that add_item(request, pk) works only by POST, then you can't expect that request.POST always will be presented.
Better write your code as this:
if request.POST:
# do something
And if you don't specify default value for your select in template, then sizeswill not be in your request.POST.
You can write like this just to be sure that you got some value:
request.POST.get('sizes', 'some_default_value')
Just because you get MultiValueDictKeyError you need to see what you get in request. Maybe you get QueryDict, then you need to extract first value. For example, see this SO question. For example, print your request.POST or check type.

customise django widget in template

I have standard model form in django with Imagefield and standard widget. It made me such output on the page:
Currently: qwe/Tulips.jpg <input id="image-clear_id" name="image-clear" type="checkbox" /> <label for="image-clear_id">Clear</label><br />
Change: <input id="id_image" name="image" type="file" />
I want to place outputs of this widget in different parts of page. How can I cut it in templates.
If there is a way to use part of the output in template like {{form.name_of_field.label}} or {{form.name_of_field.errors}}
I've tried different names but no use
There must be a way to use them apart.
And yet another one who needs form styling.
I would recommend to use Widget Tweaks
<form method='POST' action="/" enctype='multipart/form-data'>
{% load widget_tweaks %}
{% csrf_token %}
{{ form.first_name |add_class:"customCSS1 customCSS2" }}
{{ form.second_name |add_class:"customCSS3 customCSS4" }}
</form>
{{ form.media.js }}
with this plugin you can style the form as you wish. All Css classes work. You can put each form field wherever you want on the Page. Is that what you are looking for? Your question is a bit misleading.
Hope that helps if not leave a comment :)

Producing the right HTML semantics with Django

I usually keep it simple and use the following form syntax in my templates:
<div>
<div>{{form.title.label}}:</div>
<div>{{form.title}}</div>
</div>
The problem with this approach is the bad semantic in the html output.
<div>
<div>Title:</div>
<div><input id="id_form-title" type="text" maxlength="30" name="form-title"></div>
</div>
Correct should be:
<div>
<label for="id_form-title">Title</label>
<input id="id_form-title" type="text" maxlength="30" name="form-title">
</div>
Is there a django build-in tag to do this automatically for me, or do I have to do it manually myself like this?
<div>
<label for="id_form-title">{{form.title.label}}</label>
{{form.title}}
</div>
It is indeed annoying that outputting fields one by one doesn't give you automatic access to a properly-constructed label element - doing form.as_p will correctly produce the fields plus labels, but you give up all control over the form layout.
You can build up the label tag using the field information fairly easily though:
<label for="{{ field.auto_id }}">{{ field.label }}</label>
{{ field }}
You can put this in a template tag for easier reuse.
Don't forget to also add {{ field.errors }} to display the errors associated with each field.
using label_tag should give you properly constructed label tag. So instead of just {{form.title.label}} you should use {{form.title.label_tag}} and it will result in the desired html

How to properly use the django built-in login view

I'm just getting started with Django, and I'm trying to use built-in features as much as possible. As such, for user login, I'm using the built-in login view, and assigning it to the base url of my site:
urlpatterns=patterns('django.contrib.auth.views',
url(r'^/$','login',{'template':'mytemplate.html'}),
mytemplate.html looks something like this:
<!DOCTYPE html>
<html>
<body>
{%if form.errors %}
<p> Invalid username/password combination, please try again </p>
{% endif %}
<h1>Welcome to My Site!</h1>
<form action="{% url django.contrib.auth.views.login %}" method="post">
{% csrf_token %}
{{form.username.label_tag}}{{form.username}}
{{form.password.label_tag}}{{form.password}}
<input type="submit" id="submit" name="submit" value="Sign in" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
forgot username/password<br />
new user
</body>
</html>
my problem is, the template doesn't appear to be getting passed any of the context it's supposed to. In the rendered HTML, all of my variable tags simply disappear (i.e. rather than being replaced by the appropriate values, thay are replaced with nothing).
I imagine I'm skipping some critical step, but I can't figure out what it is. Any ideas?
You need to change from 'template' to 'template_name'
urlpatterns=patterns('django.contrib.auth.views',
url(r'^/$','login',{'template_name':'mytemplate.html'}),
https://docs.djangoproject.com/en/1.4/topics/auth/#django.contrib.auth.views.login
Try removing the template name from your url configuration. Django will then fall back to a standard template, that way you can see if you screwed up the template somehow or if something else is wrong.
My next guess would be to check your settings for the TEMPLATE_CONTEXT_PROCESSORS. If you have defined any of them, be sure to include
"django.contrib.auth.context_processors.auth",
If you haven't defined any, django will use a standard tuple, which allready includes the auth processor.

Getting the value of an item in a django template for loop

Using django, I have a form in which a user enters his 'position'. The user may add multiple positions. The user may also delete positions. There are two things worth noting with this:
1) in the form, there are two buttons, 'Add' and 'Delete'.
2) I am using a for loop in the template to populate the list of positions and delete buttons.
This is what I currently have:
# in template
<tr>
<td>Position</td>
<td>{{ form.position }}
<input type="submit" value="Add" , name='action'/>
</td>
</tr>
<tr>
<td> </td>
<td>
{% for position in positions %}
{{ position}}
<input type="submit" value="Delete {{position }}", name='action'/>
{% endfor %}
</td>
</tr>
# in views.py
...
if action == 'Add':
positions.append(request.POST['position'])
return render_to_response(...)
if 'Delete' in action:
positions.remove(request.POST['action'][7:])
return render_to_response('...)
This seems like a very inelegant way to do the "Deletion" part.
Is there a better way to get the value of the position, without having to cram in additional information in the 'Delete' submit button, and then slicing it off to get its value?
I see three options here:
Use checkbox field for each position and one "Delete" button. In that case a user can choose multiple positions to be deleted and you can get their IDs from request easily.
Use a hidden field position and a little bit of Javascript to fill it. If you use jquery it could be:
<input type="hidden" name="position" value="" />
{% for position in positions %}
<input type="submit" value="Delete" name="action" data-position="{{ position }}" />;
{% endfor %}
<script type="text/javascript">
var $position_input = $("input[name='position']");
$("input[name='action'][value='Delete'].click(function(e) {
var $this = $(this);
var position = $this.data("position");
$position_input.val(position);
});
</script>
Insert position ID into name attribute, like this:
<input type="submit" value="Delete" name="delete-position.{{ position }} />
In view function you'll have to look through all data in request.POST and find all items which start with delete-position and then use slicing.