How to save checkbox value in django? - django

I am using checkboxes in Html and everytime I refresh my page, the checkboxes are unchecked again. How do I prevent this from happening ? Do I have to use JS ?
I tought about booleans fields but I don't really know how to implement them ...
I looked at other threads and it talked about javascript, but I do not understand anything at all about it, nor how to implement it.
Here is my code :
views.py :
'
#login_required(login_url='/login')
def home(request):
check=False
MyToDo = Todo.objects.filter(user=request.user)
formtoDo = forms.TodoForm()
if request.method == 'POST' and 'todosub' in request.POST:
formtoDo = forms.TodoForm(request.POST)
if formtoDo.is_valid():
todoit = formtoDo.save(commit=False)
todoit.user = request.user
todoit.save()
return HttpResponseRedirect('/home?')
[...]
data ={'form': form, 'formtoDo': formtoDo, 'MyToDo': MyToDo, 'check':check}
return render(request, "capygenda/entries.html", data)
'
html :
<form method="POST", class="Entry">
{% csrf_token %}
<p>{{ formtoDo|crispy}} <button type="submit" name="todosub" >Add</button></p>
</form>
{% csrf_token %}
{% for toto in MyToDo %}
<form method="POST">
{% csrf_token %}
<ul class="list">
<li class="list-item">
<input type="checkbox" class="hidden-box" id="{{ toto.id }}" autocomplete="off"/>
<label for="{{ toto.id }}" class="check--label">
<span class="check--label-box"></span>
<span class="check--label-text">{{ toto }}</span>
</label>
<button class="button-24" role="button">Delete</button>
</ul>
</form>

Two steps:
In your views, if the form is submitted i.e., when request.method == 'POST' is True, pass another parameter {'checked: 'checked'}:
if request.method == 'POST' and 'todosub' in request.POST:
formtoDo = forms.TodoForm(request.POST)
if formtoDo.is_valid():
todoit = formtoDo.save(commit=False)
todoit.user = request.user
todoit.save()
return HttpResponseRedirect('/home?')
[...]
# ---- here look at the last item ----
data ={'form': form, 'formtoDo': formtoDo, 'MyToDo': MyToDo, 'check':check, 'checked':'checked'}
return render(request, "capygenda/entries.html", data
In your template, place that variable as an attribute in the input HTML element:
<input type="checkbox" class="hidden-box" id="{{ toto.id }}" autocomplete="off" {{ checked }}/>
This way, the checked attribute of the input will be dynamically placed. If it was passed via POST method, you have it. Otherwise, it's just empty.

add checked in the input field if you want it to keep it pre-checked or dependent on View values.
<input {% if checked %} checked{% endif %}"/>

Related

How to call a django function on click that doesn't involve changing page [duplicate]

My application currently flows through 3 pages:
User selects question in index page
User submits answer in answer page
User is presented with result in results page.
I want to compress that down to a single page where the user submits an answer to the question and result is shown on the same page.
The following django-template code separates questions with Bootstrap accordion. How do I post the form without refreshing the whole page? I want to be able to display the result on the page, update CSS styling with Javascript etc.
<h2>{{ category.title }}</h2>
<div class="accordion" id="accordion{{category.title}}">
{% for challenge in category.challenge_set.all %}
<div class="card">
<div class="card-header" id="heading{{challenge.id}}">
<h2 class="mb-0">
<button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#collapse{{challenge.id}}" aria-expanded="true" aria-controls="collapse{{challenge.id}}">
{{ challenge.question_text }} - {{ challenge.point_value }} points
</button>
</h2>
</div>
<div id="collapse{{challenge.id}}" class="collapse in" aria-labelledby="heading{{challenge.id}}" data-parent="#accordion{{category.title}}">
<div class="card-body">
<p>{{ challenge.description }}</p>
<form action="{% url 'challenges:answer' challenge.id %}" method="post">
{% if challenge|is_answered:request %}
<label for="answered">Answer</label>
<input type="text" name="answered" id="answered" value="{{ challenge.answer_text }}" readonly>
{% else %}
{% csrf_token %}
<label for="answer">Answer</label>
<input type="text" name="answer" id="answer">
<input type="submit" value="Submit">
{% endif %}
</form>
</div>
</div>
{% endfor %}
</div>
Here is the view:
def index(request):
context = {'challenges_by_category_list': Category.objects.all()}
return render(request, 'challenges/index.html', context)
def detail(request, challenge_id):
challenge = get_object_or_404(Challenge, pk=challenge_id)
return render(request, 'challenges/detail.html', {'challenge': challenge})
def results(request, challenge_id, result):
challenge = get_object_or_404(Challenge, pk=challenge_id)
return render(request, 'challenges/results.html', {'challenge':challenge, 'result':result})
def answer(request, challenge_id):
challenge = get_object_or_404(Challenge, pk=challenge_id)
result = "Incorrect, try again!"
if challenge.answer_text.lower() == request.POST['answer'].lower():
current_user = request.user
session = User_Challenge(user=current_user, challenge=challenge, answered=True)
session.save()
points = Profile(user=current_user, points=challenge.point_value)
points.save()
result = "Correct!"
return HttpResponseRedirect(reverse('challenges:results', args=(challenge.id, result)))
You can try this:
Add the below script in your template:
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
write a script and a function inside it to submit the form data.
<script type="text/javascript">
function submitData( challenge_id ){
// Get answer from the input element
var answer = document.getElementById("answer").value;
// add the url over here where you want to submit form & challenge_id is also taken as a parameter.
var url = "<your_url>";
$.ajax({
url: url,
data: {
'answer': answer,
},
dataType: 'JSON',
success: function(data){
// show an alert message when form is submitted and it gets a response from the view where result is provided and if url is provided then redirect the user to that url.
alert(data.result);
if (data.url){
window.open(data.url, '_self');
}
}
});
}
</script>
Change type of the submit button and add an onclick event to call the submitData() function and pass the challenge id to it. And remove the action attr from the form.
see below:
<form method="post">
{% csrf_token %}
{% if challenge|is_answered:request %}
<label for="answered">Answer</label>
<input type="text" name="answered" id="answered" value="{{ challenge.answer_text }}" readonly>
{% else %}
<label for="answer">Answer</label>
<input type="text" name="answer" id="answer">
// over here
<button type="button" onclick="submitData({{ challenge.id }})">
Submit
</button>
{% endif %}
</form>
Return a JsonReponse to the ajax call from the views.
views.py
def answer(request, challenge_id):
answer = request.GET.get('answer', False)
url = False
if challenge.objects.filter(id=challenge_id).exists() and answer:
challenge = Challenge.objects.get(id=challenge_id)
if challenge.answer_text.lower() == answer.lower():
current_user = request.user
session = User_Challenge(user=current_user, challenge=challenge, answered=True)
session.save()
points = Profile(user=current_user, points=challenge.point_value)
points.save()
result = "Correct!"
# specify the url where you want to redirect the user after correct answer
url = ""
else:
result = "Incorrect, try again!"
data = {
'result': result,
'url': url
}
return JsonResponse(data)

In django, how to use another model data from function based views

So I have a form that updates a key_instance object with a borrower. Currently my app needs the user to enter the name of the borrower, but I want it to display a dropdown list of data from another model the user model to select from, is there anyway to do this in a class based view? Here are my views.py and my template. What I was thinking is that I would like to use a get_list_or_404 on the user model and display it as a drop down list in the template and use that selection to populate the form field.
I manged to get the dropdown list to display in my template but I'm not sure as to how to save that value in my views.
Does anyone know if this is the right way or if this is doable? Thank you!!
views.py
def submit_key_request(request, pk):
"""
View function for renewing a specific keyInstance by admin
"""
key_inst=get_object_or_404(KeyInstance, pk=pk)
names = get_list_or_404(Users)
# If this is a POST request then process the Form data
if request.method == 'POST':
# Create a form instance and populate it with data from the request (binding):
form = UpdateKeyForm(request.POST)
# Check if the form is valid:
if form.is_valid():
# process the data in form.cleaned_data as required (here we just write it to the model due_back field)
key_inst.is_requested = True
key_inst.status = 'r'
key_inst.date_requested = datetime.date.today()
key_inst.borrower = form.cleaned_data['borrower']
key_inst.save()
# redirect to a new URL:
return HttpResponseRedirect(reverse('all-available-keys') )
# If this is a GET (or any other method) create the default form.
else:
form = UpdateKeyForm(initial={'borrower': 'N/A'})
return render(request, 'catalog/keyinstance_request_update.html', {'form': form, 'keyinst':key_inst})
template
{% extends "base_generic.html" %}
{% block content %}
<div class="wrapper">
<div class="centered"> <h1>Request Keys For Room: {{keyinst.roomkey}}</h1></div>
<div class="square-box">
<div class="square-content">
<form action="" method="post" >
{% csrf_token %}
<table style="display: inline-flex">
{{ form}}
</table>
<select name = 'name'>
{% for name in names %}
<option value="{{ name }}">{{ name }}</option>
{% endfor %}
</select>
<p>
(Please use their login name i.e. <b>{{ user.get_username }}</b>)
</p>
<p><input required id="checkBox" type="checkbox" onclick="validate()"> I accept the terms and conditions</p>
<p id="text" style="display:none">You Have Agreed To the Terms and Conditions</p>
<input type="submit" value="Submit" />
</form>
</div>
</div>
</div>
{% endblock %}
Here is how I manged to do it, Not sure if this is the best 'pythonic' or best practice. Please let me know if it's not.
my views.py
def submit_key_request(request, pk):
"""
View function for renewing a specific keyInstance by admin
"""
key_inst=get_object_or_404(KeyInstance, pk=pk)
names = get_list_or_404(User)
# If this is a POST request then process the Form data
if request.method == 'POST':
name = request.POST['name']
key_inst.is_requested = True
key_inst.status = 'r'
key_inst.date_requested = datetime.date.today()
key_inst.borrower = name
key_inst.save()
return HttpResponseRedirect(reverse('all-available-keys') )
# If this is a GET (or any other method) create the default form.
else:
pass
return render(request, 'catalog/keyinstance_request_update.html', {'keyinst':key_inst, 'names':names})
template
{% extends "base_generic.html" %}
{% block content %}
<div class="wrapper">
<div class="centered"> <h1>Request Keys For Room: {{keyinst.roomkey}}</h1></div>
<div class="square-box">
<div class="square-content">
<form action="" method="post" >
{% csrf_token %}
</br>
<select name = 'name' required>
{% for key in names %}
<option value="{{ key }}">{{ key }}</option>
{% endfor %}
</select>
<p>
(Please use their login name i.e. <b>{{ user.get_username }}</b>)
</p>
<p><input required id="checkBox" type="checkbox" onclick="validate()"> I accept the terms and conditions</p>
<p id="text" style="display:none">You Have Agreed To the Terms and Conditions</p>
<input type="submit" value="Submit" />
</form>
</div>
</div>
</div>
{% endblock %}

Jump to page in Django

Alright, here is what I want to do:
A user sees a number of assignments in a table, with each assignment paginated using paginator. A user can input a number in a field and click "go", he is then taken to the page.
Here's my code so far:
View:
def home(request):
if request.user.is_authenticated():
assignments = show_assignments(request)
form=GoToForm(request.POST or None)
if request.POST and form.is_valid():
form = form.cleaned_data
assignment = form["assignment"]
page = form["page"]
return HttpResponseRedirect("/coding/assignment/%i/?page=%i") % (assignment, page)
return render(request, 'account.html', {'assignments':assignments, 'form':form})
Template:
{% for assignment in assignments %}
<tr>
<td><a href="{% url 'coding:assignment' assignment.id %}">
{{ assignment.country }}: {{ assignment.start_date }} to {{ assignment.end_date }}</a></td>
<td>{{ assignment.finished_articles }} of {{ assignment.article_num}} articles finished </td>
<td>Start where I left off</td>
<td>
<form id="form" action="" method="post" accept-charset="utf-8">
{% csrf_token %}
Jump to page {{ form.page }}
<input type="hidden" name="" value="{{ assignment.id }}" id="id_assignment">
<input type="submit" value="Go">
</form>
</td>
</tr>
{% endfor %}
This isn't working.
Is this because I need to use formsets (because I have multiple GoToForms on one page), or is the HttpResponseRedirect not working?
If the GoToForm form class looks simple (e.g. it only has page field), then there's no need to use this form class at all. Just add form fields manually in template and validate them inside post view if needed.
Formset for this case would be overkill, unless you have a lot of input fields to work with.
Also remember to change id="form" to make it unique for each form or use class="form" instead.

Django identifying non field validation error is associated with which form

Is there a way to identifying the validation error is associated with which form for a template contaning multiple forms?
Let me explain the scenario. I have login window popup script within my base.html
<div id="dialog1" class="window">
<form method="post" action="/accounts/login/?next=/IW/home" id='login-form' name=login-form>{% csrf_token %}
<div class="d-header">
{{ form.non_field_errors }}
<input type="text" name="username" id="id_username" value="" onclick="this.value=''"/><br/>
<input type="password" name="password" id="id_password" value="" onclick="this.value=''"/><br/>
<input type="hidden" name="login_form" value="1" />
<input type="submit" value="login" />
</div>
{% endif %}
</div>
</form>
</div>
<div id="mask"></div>
{% if form.non_field_errors %}
<script>
var maskHeight = $(document).height();
var maskWidth = $(window).width();
//Set heigth and width to mask to fill up the whole screen
$('#mask').css({'width':maskWidth,'height':maskHeight});
$('#mask').show();$('.window').show();
</script>
{% endif %}
As all other templates extends base,html whenever there is an error non_field error then login window pops up . I would like to show the login error only when login form is submit not on submit of someother form with a non field error.
For this I need to identify the name of the form.something like this {% ifequal form.form_name login_form %} - Display login error .Is this possible??
They isn't anything special about the name 'form' in the template. It's just a default context name, you can choose to name your forms anything you like.
Just name the forms differently in your context:
from django.http import Http404
def detail(request, poll_id):
# NOTE: This view code isn't really form processing code, just using it to show
# how you can pass different names into your template context.
login_form = MyLoginForm()
detail_form = MyDetailForm()
try:
p = Poll.objects.get(pk=poll_id)
except Poll.DoesNotExist:
raise Http404
return render_to_response('polls/detail.html', {'poll': p, 'login_form': login_form, 'detail_form': detail_form})
And then:
<div id="dialog1" class="window">
<form method="post" action="/accounts/login/?next=/IW/home" id='login-form' name=login-form>
{% csrf_token %}
<div class="d-header">
{{ login_form.non_field_errors }}
.
.
.
Also, if you want to do multiple instances of the same form type, have a look at formsets

problem handling upload file django

I'm trying to do a feature that would allow the user to upload a file. That file would have information to insert into the database, that's why in the function I'm saving data. Not sure if it's the best option.
What I've done so far:
forms.py:
class UploadFileForm(forms.Form):
file = forms.FileField()
views:
def last_step(request, word):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
msg = handle_uploaded_file(word, request.FILES['file'])
return render_to_response('final_insert.html', {'msg': msg})
else:
form = UploadFileForm()
return render_to_response('upload.html',
{
'word': word,
'form': form })
template:
<form enctype="multipart/form-data" action="{% url upload_done 'clinical' %}" method="post">
<div>
{% for field in form %}
{{ field }}
{% endfor %}
<input type="submit" value="Save" />
</div>
</form>
function:
def handle_uploaded_file(word, f):
msg = "first stop"
data = []
for chunk in f.chunks():
data.append(chunk)
msg = "after chunk"
if word == 'clinical':
pat = Patient.objects.values_list('patient', flat=True)
for i in data:
if i[0] not in pat:
b2 = Patient(patient=i[0])
b2.save()
msg = "number was inserted"
else:
msg = "something"
return msg
The problem is when I hit "save" in the template, it redirects well to another template, but I don't see any message, like I suppose to see (final_insert.html shows {{ msg }})
Can someone help me understand what I'm doing wrong?
Any help is welcome!
Thanks for your help!
I was able to understand my mistake.
sorry guys for my silly mistake
so this is the form:
<form enctype="multipart/form-data" action="{% url upload_done 'clinical' %}" method="post">
<div>
{% for field in form %}
{{ field }}
{% endfor %}
<input type="submit" value="Save" />
</div>
</form>
urls:
url(r'^insert/file/(?P<word>clinical)/upload/$', last_step, name="upload"),
url(r'^insert/file/(?P<word>clinical)/upload/result/$', final, name='upload_done'),
so the view last_step corresponds to the url "upload" and not "upload_done"
I wrote into the form action={% url upload_done 'clinical' %}, so when I hit save it will redirect me automatically to the other template. Without running the code!!
So I changed the form to:
<form enctype="multipart/form-data" action="{% url upload 'clinical' %}" method="post">
<div>
{% for field in form %}
{{ field.label_tag }}
{{ field }}
{% endfor %}
<input type="submit" value="Guardar" />
</div>
</form>
and now it works..
sorry guys, I thought I needed to redirect to the other page but when he redirects he doesn't run the code..