I'd like to show all grades from one user on Button Click. Now I wanted to integrate an expandable Button from Bootstrap. The GET Request works perfectly, but the JavaScript for the expandable Button gets triggered on Button Click and then the Page gets reloaded. That means the Button now is back at the original state. Do you guys maybe have a way to fix that?
noten.html
{% if subject_list %}
<form name="subject-list" method="GET">
<ul>
{% for subject in subject_list %}
<p>
<button class="btn btn-primary" type="submit" data-bs-toggle="collapse" data-bs-target="#{{ subject }}" aria-expanded="false" aria-controls="collapseExample" value="{{ subject }}" id="{{ subject }}" name="subject">
{{ subject }}
</button>
</p>
<div class="collapse" id="{{ subject }}">
<div class="card card-body">
{% if grades %}
{% for grade in grades %}
<li>{{ grade }}</li>
{% endfor %}
{% else %}
<p>Du hast keine Noten in diesem Fach!</p>
{% endif %}
</div>
</div>
{% endfor %}
</ul>
</form>
{% endif %}
views.py
def noten(request):
if request.user.is_authenticated: # if user is logged in
object_list = Subject.objects.order_by('name')
subject_list = []
for subject in object_list:
subject_list.append(subject.name)
if request.method == 'GET':
subject = request.GET.get('subject', '')
if subject != '':
print(f"GET {subject}")
grades = Test.get_grades(student=request.user.id, subject=subject, self=Test)
return render(request, 'noten.html', {'subject_list': subject_list, 'grades': grades})
else:
return render(request, 'noten.html', {'subject_list': subject_list})
else:
return render(request, 'noten.html', {'subject_list': subject_list})
else: # else redirect to login page
return redirect('loginForm:login')
Related
I'm currently building out a Django ModelForm with an adjoining inline_formset. I'm also using a button with javascript to allow the user to add in more instances of the formset in the front-end. However, the issue im running into is that the extra instances of the formset that are added by the user, don't get sent to the database when the form is submitted.
I'm unsure as to whether it is an issue with the javascript itself. Or something more fundamental to do with the Django inline formsets. Any help would be much appreciated. Everything else is working fine; heres my HTML template for reference
{% extends 'main.html' %}
{% load static %}
{% block content %}
<section id="form">
<form class="form" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
<div class="input">
<h1>{{field.label}}:</h1>
<p>{{field}}</p>
</div>
{% endfor %}
<div id="form-container">
{{formset.management_form}}
{% for field in formset %}
<div class="input">
<h1>Parts Required:</h1>
<p>{{field}}</p>
</div>
<div id="empty-form" class="input" style="display: none;">
<p>{{formset}}</p>
</div>
{% endfor %}
</div>
<button class="btn btn-primary" id="add-more" type="button">Add More</button>
<input class="submit btn btn-primary" type="submit"></input>
</form>
</section>
<script>
document.getElementById("add-more").onclick = function () {
var emptyForm = document.getElementById("empty-form");
var newForm = emptyForm.cloneNode(true);
newForm.style.display = "block";
document.getElementById("form-container").appendChild(newForm);
}
</script>
{% endblock content %}
Here's my view for the warranty form:
# Create Warranty Form:
#login_required(login_url='login')
def createWarranty(request):
warranty = WarrantyClaim.objects.all()
form = WarrantyClaimForm()
warrantyformset = inlineformset_factory(WarrantyClaim, PartsRequired, form=WarrantyClaimForm, extra=1)
if request.user.is_authenticated:
if request.method == 'POST':
form = WarrantyClaimForm(request.POST, request.FILES)
formset = warrantyformset(request.POST, request.FILES, instance=WarrantyClaim())
if form.is_valid() and formset.is_valid():
post = form.save(commit=False)
post.owner = request.user
post.save()
formset.instance = post
formset.save()
return redirect('spare-parts')
context = {'warranty': warranty, 'form': form, 'formset': warrantyformset}
return render(request, 'spareparts/parts_form.html', context)
I am using Flask, and have some basic elements on a login page. A route, a form, and a template. I also have a spinner that is triggered on the submission of the form.
Where I am running into issues is when someone does not fill out the info, and hits submit. Basically, a spinner is triggered, but also the infobulle warning the user to fill out the field is triggered as well causing the spinner to be stuck spinning.
Form:
class LoginForm(FlaskForm):
email = StringField("Email Address", validators=[DataRequired(), Email()])
password = PasswordField("Password", validators=[DataRequired()])
submit = SubmitField("Login")
Route:
#auth.route("/login", methods=["Get", "Post"])
def login():
form = LoginForm()
if form.validate_on_submit():
# Login to the site
return render_template("login.html", form=form, title="Sign In")
Template:
<form class="text-left col-lg-12" action="" method="POST">
{{ form.hidden_tag() }}
<div class="form-group">
{% if form.email.errors %}
{{ form.email(class_="form-control is-invalid", placeholder="Email") }}
<div class="invalid-feedback">
{% for error in form.email.errors %}
<span> {{ error }} </span>
{% endfor %}
</div>
{% else %}
{{ form.email(class_="form-control ", placeholder="Email") }}
{% endif %}
</div>
<div class="form-group">
{% if form.password.errors %}
{{ form.password(class_="form-control is-invalid", placeholder="Password") }}
<div class="invalid-feedback">
{% for error in form.password.errors %}
<span> {{ error }} </span>
{% endfor %}
</div>
{% else %}
{{ form.password(class_="form-control ", placeholder="Password") }}
<span class="p-viewer"><i class="icon-eye" id="togglePassword" style="margin-left: 100px; cursor: pointer;"></i></span>
{% endif %}
</div>
<div class="text-right mt-3">
<i class="icon-chevron-left"></i> Homepage
{{ form.submit(class="btn btn-primary", onclick='spinner()') }}
</div>
</form>
JavaScript for Spinner:
<script>
function spinner() {
$('#spinner').modal('show');
return true;
}
var FirstLoading = true;
function RestoreSubmitButton() {
if (FirstLoading) {
FirstLoading = false;
return;
}
$('#spinner').modal('hide');
}
document.onfocus = RestoreSubmitButton;
</script>
Because I am using DataRequired() in the form, it is inserting a "required" element in the input field. When I click submit without data, it fires the spinner and then raises the info box:
My question is, is there a way to handle this conflict properly? Or is there a way to trigger the spinner to run after the validation process is complete?
Thanks
I just needed to put an id on the form instead of on the button.
So, I changed:
<div class="text-right mt-3">
<i class="icon-chevron-left"></i> Homepage
{{ form.submit(class="btn btn-primary", onclick='spinner()') }}
</div>
to:
<div class="text-right mt-3">
<i class="icon-chevron-left"></i> Homepage
{{ form.submit(class="btn btn-primary") }}
</div>
and:
<form method="POST" acion="">
to:
<form method="POST" acion="" id="submit_id">
and added a bit of javascript:
$(document).ready(function() {
$("#submit_id").submit(function(e) {
spinner();
});
});
Now, the spinner is not kicked off until after the form itself has had a chance to see if required fields are needed
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)
I have a question about django and ajax, i want to show result of forloop with ajax. I have code below when i submit button, my html doesn't append data of ajax call. Anyone have solotion about this. Thanks for answer
This is my view
def test_ajax(request):
if request.method == 'POST':
for i in range(10):
helloworld(i)
return render(request, 'test_ajax.html')
def helloworld(request, i):
data = {
'status': i
}
return JsonResponse(data)
This is my url
path('testajax/', test_ajax),
path('helloworld/<int:i>/', helloworld, name='ajaxsend')
And this is my html file{% extends 'base.html' %}
{% extends 'base.html' %}
{% block content %}
<div class="container">
<h1>Hello World</h1>
<form id="test" method="POST">
{% csrf_token %}
<input type="text" id="id_username">
<button type="Submit" class="btn btn-secondary">Check Token Live</button>
</form>
<div id="people">
</div>
</div>
{% endblock content %}
{% block script %}
<script>
$("#test").on('submit', function (e) {
e.preventDefault(e);
$("#people").html('<h2>Hello World</h2>');
$.ajax({
url: "/helloworld/",
success: function(result){
alert('Hello world');
console.log(result.status)
$("#people").append('<h2>' + result.status + '</h2>');
}
});
});
</script>
{% endblock script %}
I an using django-crispy forms and using that in a jquery dialog box to show a form wizard. the problem that I am facing is that when used in the dialog box the wizard never moves to the next screen.
So, my wizard is defined as follows:
class ContactWizard(SessionWizardView):
def get_template_names(self):
return "reviewdialog.html"
def done(self, form_list, **kwargs):
return HttpResponseRedirect('index')
And the template is defined as:
{% load crispy_forms_tags %}
{% load i18n %}
{% block content %}
<form action="." method="post">
{% csrf_token %}
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{% crispy form %}
{% endfor %}
{% else %}
{% crispy wizard.form %}
{% endif %}
{% if wizard.steps.prev %}
<button name="wizard_goto_step" value="{{ wizard.steps.first }}">{% trans "first step" %}</button>
<button name="wizard_goto_step" value="{{ wizard.steps.prev }}">{% trans "prev step" %}</button>
{% endif %}
</table>
<input type="submit" class="btn btn-success" value = "NEXT">
</form>
{% endblock %}
Now, I show this in a jquery dialog where I have overridden the submit method to ensure that the dialog does not close on clicking the 'NEXT' button
<script>
function EditDialog(pk) {
$.ajax({
url: "{% url 'populatereviewform' %}",
method: 'GET',
data: {
pk: pk
},
success: function(formHtml){
//place the populated form HTML in the modal body
$('.modal-body').html(formHtml);
$( "#dialog" ).modal({width: 500, height: 500});
},
dataType: 'html'
});
$('#dialog').submit( function(e) {
return false;
});
return false;
}
</script>
The AJAX part just populates the form with some data and the dialog object is a standard jquery modal dialog. The form is shown at the first screen and is populated with the correct value but when I press NEXT nothing happens in the sense that the wizard does not transition.
The urls.py is configured as:
url(r'^review/(?P<pk>\d+)/$', views.ContactWizard.as_view([DummyForm, OtherForm]), name='review'),
Som, the form starts with the DummyForm (which is a ModalForm) but does not progress to the next wizard screen. I have a feeling it could be something to do with my javascript but could bit get to the bottom of this.
EDIT
So, based on #udi's answer, I tried the following:
$("#dialog").submit(function(e)
{
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
var postData = $(this).serializeArray();
var formURL = $(this).attr("action");
$.ajax(
{
url : formURL,
type: "POST",
data : postData,
success:function(data, textStatus, jqXHR)
{
$('.modal-body').html(data);
},
error: function(jqXHR, textStatus, errorThrown)
{
alert(errorThrown)
}
});
e.preventDefault(); //STOP default action
e.unbind(); //unbind. to stop multiple form submit.
});
return false;
}
However, the data that is returned here is not the next screen of the wizard but the underlying page on which the dialog is shown. So, perhaps it is the formURL or postdata variables that are not initialized properly?
The template for the wizard screen is:
{% load crispy_forms_tags %}
{% load i18n %}
{% block head %}
{{ wizard.form.media }}
{% endblock %}
{% block content %}
<form id="review-form" action="." method="post">
{% csrf_token %}
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{% crispy form %}
{% endfor %}
{% else %}
{% crispy wizard.form %}
{% endif %}
{% if wizard.steps.prev %}
<button name="wizard_goto_step" class="btn btn-success" value="{{ wizard.steps.first }}">{% trans "first step" %}</button>
<button name="wizard_goto_step" class="btn btn-success" value="{{ wizard.steps.prev }}">{% trans "prev step" %}</button>
{% endif %}
</table>
<input type="submit" class="btn btn-success" value = "Hello">
</form>
{% endblock %}
You will need to get the html of the next form via jQuery and update it in the dialog.