Use javascript variable with django template - django

I have a brain model with different resolutions and I want to let the user to change the resolution of the the model by just choosing his desired resolution from a select tag.
I mean I have the complete models loaded and without sending a POST to the server I want to change the view of the brain model.
The problem is that I can't pass the read value from the select to my django tag to choose the suitable moedl to render.
This is my code:
<body>
<select id="resolution_options" onchange="change_brain_resolution()" name="resolution_options">
{% for brain in brains %}
<option value="{{ brain.resolution }}">
{{ brain.resolution_title }}
</option>
{% endfor %}
</select>
<script>
function change_brain_resolution() {
var selcted_resolution = getElementById('resolution_options').value;
{% for brain in brains %}
// Here is my problem !!
{% if brain.resolution == selcted_resolution %}
// Do drawing
{% endfor %}
}
</script>
</body>
The reson of why I don't want to do a POST is because there are many other things the user have been drawing and doing and he may want to change the resolution of the brain model in order to have a better view or performance but he wants to keep the other objects in the scene
Thank you very much.

Try this:
function change_brain_resolution() {
var selcted_resolution = getElementById('resolution_options').value;
{% for brain in brains %}
if (selcted_resolution == {{ brain.resolution }}) {
// Do drawing
}
{% endfor %}
}
This ends up putting logic for every brain resolution on your client side. If you want to be more efficient, you have to pass the selected resolution to server with AJAX, and render the response.
Remember that templates are rendered before the page, hence they don't have access to value of javascript variables. If you want to pass javascript variables to server, you should do so after page load with an AJAX request.

Related

How do I render all the objects of a child model belonging to a particular object of the parent model on click?

Suppose there are two models Lists and Tasks where 'Lists' have one-to-many relationship with 'Tasks'.
All the objects of the Lists model are rendered on the page like this:
HTML
<div class="grid-container">
{% for list in lists %} <!--lists is context for Lists.objects.all() -->
<div class="grid-item" id="{{ list.id }}" onclick="showTasks( {{ list.id }} )">
{{ list.name }}
</div>
{% endfor %}
</div>
<dialog id="tasks">
</dialog>
JavaScript
<script>
function showTasks(listid){
document.getElementById("tasks").show();
}
</script>
Now I want to render all the tasks_set (all the objects of 'Tasks') related to a particular object of 'Lists' in that dialog with id="tasks".
As it can be seen in the above snippet, I thought of doing it by passing list.id as a parameter to the JavaScript function but couldn't figure out beyond it. How can I achieve it?
Solution 1:
Building on Iain's comment some code. I think it is easiest if you use jQuery so load it in the section of your html template, e.g.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
Then you need a view that returns the required tasks data
def tasks_view(request):
list_id = request.GET.get('listid') # fetch the id of the list
tasks = Lists.objects.get(pk=list_id).tasks.all() # get your tasks
data = {'tasks': render_to_string('tasks.html', {'tasks': tasks})} # pre render the data
return JsonResponse(data)
A remark about the view: You can of course also return the raw Json data. However, in your case I think it is easier to create a small sub-template (tasks.html in the example) and use render_to_string to get the html code you can simply add to your base html page. Don't forget to add the view to your urls.
An example task.html just for the completeness:
<ul>
{% for task in tasks %}
<li>{{ task }}</li>
{% endfor %}
</ul>
Then send an Ajax request to the view (tasks_view) when clicked.
<script>
function showTasks(listid){
$.ajax({
url: '/tasks/', // url of the view created above
data: {
'listid': listid // Your list id
},
data_type: 'html', // as we are receiving a html template
success: function(data){
$('#tasks').append(data.tasks); // append the html code to the dialog
$('#tasks').show();
}
});
}
</script>
Solution 2
In case you do not want to use Ajax and do not mind rendering all your tasks on loading the template you can also create a for the tasks of each list and show them on demand. For this iterate through your lists:
{% for list in lists %}
<dialog id="list-{{ list.id }}">
<ul>
{% for task in list.tasks.all %}
<li>{{ task }}</li>
{% endfor %}
</ul>
</dialog>
{% endfor %}
As you can see you can access the m2m field 'tasks' of each Lists object with list.tasks.all (no ()!). And each has got an individual id.
And then just show and hide the dialogs (just as an example w/o jQuery in case you are reluctant to use it):
<script>
function showTasks(listid){
// Close all dialogs
var all_dialogs = document.getElementsByTagName('dialog');
for (i = 0; i < all_dialogs.length; i++){
all_dialogs[i].removeAttribute('open');
}
// Open the required dialog
var dialog = document.getElementById("list-" + listid);
dialog.setAttribute('open','open');
};
</script>

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.

Add button against each record on django admin change list page?

I want to add a button against each row on django admin change list page. When that button is clicked, i want to make entries in some database table(all this happens on the backend) and after that the button should be disabled on the same page against that row.
How can i achieve this? I have searched a lot button there is no solution available.
You are going to have to edit the changelist_result_list template at the point where the result items are generated (near the bottom) to add an element that provokes a request. For example:
<tr class="{% cycle 'row1' 'row2' %}">
{% for item in result %}
{{ item }}
<!-- NEW STUFF --->
{% if forloop.last %}
<td>{% if pk %}CLICK ME{% endif %}</td>
{% endif%}
{% endfor %}
</tr>
(Don't forget to also update the table headers further up)
You are going to have to provide a template context variable for {{pk}} by messing with the templatetag result_list in django.contrib.admin.template_tags.admin_list -- meaning, you are creating your own templatetag for this.
Then you need to set up a view and an url conf to handle the request the links send.
Let's say you're at foo/bar and click on the link for the row with pk=2. A GET request will be send for foo/bar/do_things_with_object_2.
Your url conf needs to capture the pk (and the model, here: bar) and your view needs to do whatever it is you want on the object.

Display Form Field based on other Fields within the Django Form

I am trying to create a way to only display certain fields within a django form based on the bound data of another field within that same form. I'm familiar with the idea of form.field.bound_type but I'm not sure how to continually check for state change on a field in a form and update the other field accordingly. Something like if you were filling out an application and it asked if you've committed a crime, if you click yes then a details text area pops up.
I'm using:
Django 1.8.4
Bootstrap3 6.6.2
As it pertains to this question. Here is what I've currently got with the field values edited for work protection. It does SORT of work. Meaning the form is fine, the if statement works initially but it doesn't reevaluate the if once the specified field has changed.
<form action= "/send_email/" method="post" class='col-sm-5'>
{% csrf_token %}
{% bootstrap_field form.field_one%}
{% bootstrap_field form.field_two%}
{% bootstrap_field form.field_three%}
{% if form.field_three.bound_data == "A Value" %}
{% bootstrap_field form.field_four%}
{% endif %}
{% buttons %}
<button type="submit" class="btn btn-primary">
{% bootstrap_icon "glyphicon glyphicon-ok-circle" %} Submit
</button>
{% endbuttons %}
</form>
Solution:
With Birdie's help I was able to figure out the solution. For anyone who has hit this same Django related problem here is how you add or remove fields based on another field in the same form.
<script>
// function that hides/shows field_four based upon field_three value
function check_field_value(new_val) {
if(new_val != 'A value') {
// #id_field_four should be actually the id of the HTML element
// that surrounds everything you want to hide. Since you did
// not post your HTML I can't finish this part for you.
$('#field_four').removeClass('hidden');
} else {
$('#field_four').addClass('hidden');
}
}
// this is executed once when the page loads
$(document).ready(function() {
// set things up so my function will be called when field_three changes
$('#field_three').change( function() {
check_field_value(this.value);
});
});
</script>
<form action= "/send_email/" method="post" class='col-sm-5'>
{% csrf_token %}
{% bootstrap_field form.field_one%}
{% bootstrap_field form.field_two%}
<div id="field_three">{% bootstrap_field form.field_three%}</div>
<div id="field_four">{% bootstrap_field form.additional_notes %}</div>
{% buttons %}
<button type="submit" class="btn btn-primary">
{% bootstrap_icon "glyphicon glyphicon-ok-circle" %} Submit
</button>
{% endbuttons %}
</form>
You cannot do this within the template because the template is executed on the server side, but the user interaction occurs on the client side.. in the browser. This must be done in javascript and run in the browser.
Here is an example of jQuery code which does this. I did not test it so it may need tweaking, but this should get you in the right direction.
You will need to look at your HTML to determine the id of the element you actually want to hide() and show(). Normally you would have some kind of HTML element (eg. a DIV) surrounding both the field or fields you want to hide as well as the label(s).. and you would hide everything at once by hiding the element which contains all the fields, rather than each individual field itself.
If you add the HTML surrounding field_four to your question, I will update the answer to work with what you've got...
<script>
// Ideally this script (javascript code) would be in the HEAD of your page
// but if you put it at the bottom of the body (bottom of your template) that should be ok too.
// Also you need jQuery loaded but since you are using bootstrap that should
// be taken care of. If not, you will have to deal with that.
// function that hides/shows field_four based upon field_three value
function check_field_value() {
if($(this).val() == 'A Value') {
// #id_field_four should be actually the id of the HTML element
// that surrounds everything you want to hide. Since you did
// not post your HTML I can't finish this part for you.
$('#id_field_four').hide();
} else {
$('#id_field_four').show();
}
}
// this is executed once when the page loads
$(document).ready(function() {
// set things up so my function will be called when field_three changes
$('#id_field_three').change(check_field_value);
// set the state based on the initial values
check_field_value.call($('#id_field_three').get(0));
});
</script>
Thanks all for the contribution, but I could not get the code above work. This is my solution:
<script>
function hideField() {
check = document.getElementsByName("put your field name here")[0].value;
response = document.getElementById("put your id here");
if (check === "Open") {
response.style.display = "none";
}else{
response.style.display = "block";
}
}
</script>
The key is to figure out Id for the field you want to hide, and name for the field you check. I did NOT use DIV container to assign the id and the name, but inspected rendered HTML page with developer tools in the browser. Let me know if you have questions or want more detailed explanation.
I found this question very interesting. I have updated it with bootstrap 4.0v, with the following script, I hope it can help someone:
<script>
// function that hides/shows field_four based upon field_three value
function check_field_value(new_val) {
if(new_val != 'A value') {
// #id_field_four should be actually the id of the HTML element
// that surrounds everything you want to hide. Since you did
// not post your HTML I can't finish this part for you.
$('#field_four').removeClass('d-none');
} else {
$('#field_four').addClass('d-none');
}
}
// this is executed once when the page loads
$(document).ready(function() {
// set things up so my function will be called when field_three changes
$('#field_three').change( function() {
check_field_value(this.value);
});
});
</script>
<form action= "/send_email/" method="post" class='col-sm-5'>
{% csrf_token %}
{% bootstrap_field form.field_one%}
{% bootstrap_field form.field_two%}
<div id="field_three">{% bootstrap_field form.field_three%}</div>
<div id="field_four">{% bootstrap_field form.additional_notes %}</div>
{% buttons %}
<button type="submit" class="btn btn-primary">
{% bootstrap_icon "glyphicon glyphicon-ok-circle" %} Submit
</button>
{% endbuttons %}
</form>

Rendering Foreign Keys in templates grouped by origin

I have an Area model, and I have Service model. Area is a Foreignkey of Service.
I want a template which shows each Service grouped under its respective Area, i.e.
Area 1
- service a
- service b
Area 2
- etc.
I've passed in an object list of all services to service_list.html. I have a custom tag get_areas which returns the areas, on which I can create the Area divisions, and from which I can potentially pass an area name to a service filter. But since I can't filter (can I?) in {% for service in object_list %}, how can I filter the service list in each Area's section of the HTML?
Thanks so much in advance.
If you post your models I can give you the exact code, but in general something like this should work:
# Pass in 'areas' variable from view with all required areas
{% for area in areas %}
{{ area.name }}
{% for service in area.service_set.all %} #Gets all the services associated with an area
{{ service.name }}
{% endfor %}
{% endofor %}
Not sure you even need a custom tag, but maybe I just don't understand that part.
Take a look at the regroup template tag. It was built for exactly the same purpose
https://docs.djangoproject.com/en/1.3/ref/templates/builtins/#regroup
One thing that I have realized is that once you get the point of displaying complex interrelated data in your templates, that it makes sense to transform the data into an appropriate object (generally a list of dictionaries) before passing them onto the template.
That way, you can test your information easier, and have a much easier time displaying it. (You'll have much powerful tools at your disposition in Python based Views than in Django Templating Language).
#Maz - thank you for that. I'm learning at the moment and need to look at service_set.
#arustgi - that worked perfectly. For the benefit of fellow novices stumbling over this, I pass in 'queryset': Service.objects.all() and use:
{% regroup object_list by area as area_list %}
{% for area in area_list %}
<h2 class="separate">{{ area.grouper }}</h2>
{% for service in area.list %}
<div class="column">
<h3>{{ service.title }}</h3>
{{ service.body }}
</div>
{% endfor %}
{% endfor %}
Concise, descriptive code. Many thanks, both of you