I am just learning Django and web development in general and I was wondering if what I want to do is possible. I would like to write a Django quiz that saves the answers a user inputs without needing a backend. Is this possible?If it is not possible what is the simplest and easiest way I can do this. My template:
{% extends "base.html" %}
{% block title %}Exam Questions{% endblock %}
{% block content %}
{% if all_questions %}
<form action="{% url 'save_answer' %}" method="post">
{% csrf_token %}
{% for question in all_questions %}
<h3>{{question.text }}</h3>
<input type="hidden" name="exam_id" value="{{ question.exam.id }}">
<input type="hidden" name="question_id" value="{{ question.id }}">
<input type="hidden" value="{{question.answer_set.all}}" name="answers">
{% for answer in question.answer_set.all %}
<p><input type="radio" name="answer" value="{{ answer.id }}">{{ answer.text }}</p>
{% endfor %}
{% endfor %}
<input type="submit" value="Send">
</form>
{% else %}
<h2>No questions available</h2>
{% endif %}
{% endblock %}
Now I would like to know how to save user answers without backends
Afaik, you can save it in your backend (django forms doc), or save it with javascript in a client browser cookie.
First, make a litle change with your template to make radio choices work
<p><input type="radio" name="answer_{{ question.id }}" value="{{ answer.id }}">{{ answer.option }}</p>
Then, here is the code to store answers to session:
def post(self, request, *args, **kwargs):
post = request.POST
question_ids = post.getlist('question_id')
results = dict()
for id in question_ids:
answer = post.get('answer_%s' % id)
results[id] = answer
# Save to session or do whatever you want.
request.session['results'] = results
# Response to review
return JsonResponse(results)
Related
I am having this very confusing issue and I could only find someone who had the same issue 9 year ago but it wasn't solved : Posting a hidden field with whitespace removes content after whitespace.
First of all I'm very new to programming so pardon me if this is a very obvious issue and if my bug is very ugly !
Basically I am trying to pass the content of a variable through the value parameter of an input tag inside of a form. This variable contains strings separated by whitespaces ("test space space")
My goal is to then manipulate this data in a view through request.POST
The issue is that the first word gets passed just fine but anything after the first whitespace isn't passed to the database.
Note that I am using the same variable elsewhere in the same template and the entire title is displayed without issue
Here is my view :
#login_required
def watchlist(request):
user = request.user
all_watched_auctions = WatchedAuction.objects.all()
user_watched_auctions=all_watched_auctions.filter(user_id=user.id)
if request.method == "POST" :
auction_info = request.POST
user_id = auction_info.get('user_id')
auction_id = auction_info.get('auction_id')
title = request.POST.get('title')
# #creating a watched auction
auction_to_watch = WatchedAuction(user_id=user_id, auction_id=auction_id, is_watched=True, title=title)
# #preventing duplicates
if auction_id not in user_watched_auctions:
auction_to_watch.save()
test = user_watched_auctions.values_list("auction_id", flat=True)
active_listings = Listing.objects.filter(status="ACTIVE")
return render(request,"auctions/watchlist.html",{
"watchlist" : test,
"listings" : active_listings,
"user" : user,
})
And here is the template. You can skip to the end. That's where the troublesome form is :
{% extends "auctions/index.html" %}
{% block title %} {{listing.title}}{% endblock %}
{% block h2 %}
{% if listing.status == "ACTIVE" %}
<h2>Listing is active</h2>
{% else %}
<h2>Listing is inactive</h2>
{% endif %}
{% endblock %}
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
<div>
<div>{% block test1 %}
{% if test.id == listing.id %}
{{block.super}}
</div>
<div>
</div>
{% if listing.author_id == user_id and listing.status == "ACTIVE" %}
</div>
<form action="" method="post">
{% csrf_token %}
<a href="">
<button> Close listing </button>
</a>
</form>
{% endif %}
{% if listing.author_id != user_id and listing.status == "ACTIVE" %}
<form action="{% url 'watchlist' %}" method="post" value="test.id">
{% csrf_token %}
<input type="hidden" name="user_id" value={{user_id}}>
<input type="hidden" name="auction_id" value={{listing.id}}>
<input type="hidden" name="is_valid" value=True>
<input type="hidden" name="title" value={{listing.title}}>
<button type="submit"> add to watchlist</button>
</form>
{% endif %}
{% endif %}
{% endblock %}
Thanks a lot to anyone taking the time to help me !
Most Probably this is due to lack of quotes between actual value value="actual_value". You can use single quotes or double quotes according to your html. Please try this way. It may work.
<input type="hidden" name="user_id" value="{{user_id}}">
<input type="hidden" name="auction_id" value="{{listing.id}}">
<input type="hidden" name="is_valid" value="True">
<input type="hidden" name="title" value="{{listing.title}}">
<button type="submit"> add to watchlist</button>
I am using the django all-auth login form. I wanted to customize the look of the form fields so I changed login.html within the account folder to look like this:
<form class="login" method="POST" action="{% url 'account_login' %}">
{% csrf_token %}
{% for field in form.visible_fields|slice:'2' %}
<div class="row form-group">
{% if field.name == 'login' %}
<input type="text" placeholder="Email"><i class="fas fa-at"></i>
{% else %}
<input type="password" placeholder="Password"><i class="la la-lock"></i>
{% endif %}
</div>
{% endfor %}
Forgot Password?
<button type="submit">Sign In</button>
</form>
The form renders exactly how I would like it to, however nothing happens when I click on submit. What is strange to me is that the form submits perfectly fine if in place of my for loop I simply type {{ form.as_p }}, it just doesn't look how I want. Can anyone see an error in my loop, or is there something else wrong here. I have been looking for a solution online but so far been unsuccessful
You need to specify the names of the fields in your input tags otherwise the POST dictionary will be empty. You are using {% if field.name == 'login' %} but you forgot to specify the name attribute. Same applies for the password input.
<form class="login" method="POST" action="{% url 'account_login' %}">
{% csrf_token %}
{% for field in form.visible_fields|slice:'2' %}
<div class="row form-group">
{% if field.name == 'login' %}
<input name='login' type="text" placeholder="Email"><i class="fas fa-at"></i>
{% else %}
<input name='password' type="password" placeholder="Password"><i class="la la-lock"></i>
{% endif %}
</div>
{% endfor %}
Forgot Password?
<button type="submit">Sign In</button>
</form>
def listing(request):
contact_list = Question.objects.all()
paginator = Paginator(contact_list, 1) # Show 25 contacts per page
page = request.GET.get('page')
contacts = paginator.get_page(page)
Let's say in each page I have a form, and when the form submitted I need to go to the next page.
I first tried to add a hidden field in the form, and manually calculate the next page, and put into HTTPResponseRedirect, but then I get an empty object error for the last page because of the following:
<input type="hidden" name="next" value="?page={{ contacts.next_page_number }}">
That page contains no results
{% for contact in contacts %}
{# Each "contact" is a Contact model object. #}
{{ contact.question_text|upper }}<br>
...
<form action="{% url 'listing' %}" method="post">
{% csrf_token %}
{% for choice in contact.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
<input type="hidden" name="next" value="?page={{ contacts.next_page_number }}">
{% endfor %}
<input type="submit" value="Vote">
</form>
{% endfor %}
<div class="pagination">
<span class="step-links">
{% if contacts.has_previous %}
« first
previous
{% endif %}
<span class="current">
Page {{ contacts.number }} of {{ contacts.paginator.num_pages }}.
</span>
{% if contacts.has_next %}
next
last »
{% endif %}
</span>
</div>
So what should be my strategy? Do I need to create a seperate view for form action, or should I use listing as form action and check wheter it is a GET/POST method, I am kind of lost here.
In my Django project I have an admin action which requires an confirmation page. I oriented it on the delete_selected action, but it does not work. Here is what I have.
part of my admin.py
def my_action(modeladmin, request, queryset):
if request.POST.get('post'):
print "Performing action"
# action code here
return None
else:
return TemplateResponse(request, "admin/my_action_confirmation.html")
admin/my_action_confirmation.html
<form action="" method="post">{% csrf_token %}
<div>
<input type="hidden" name="post" value="yes" />
<input type="hidden" name="action" value="my_action" />
<input type="submit" value="Confirm" />
</div>
</form>
This works almost. I get to the confirmation page but if I click "confirm" I just get back to the original page. The part with the action code is never reached. In fact the my_action function isn't called a second time. So how do I tell django, that the my_action function should be called a second time, once I clicked confirm?
Edit: I was more missing then I thought
The corrected my_action
def my_action(modeladmin, request, queryset):
if request.POST.get('post'):
print "Performing action"
# action code here
return None
else:
request.current_app = modeladmin.admin_site.name
return TemplateResponse(request, "admin/my_action_confirmation.html")
admin/my_action_confirmation.html
{% load l10n %}
<form action="" method="post">{% csrf_token %}
<div>
{% for obj in queryset %}
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
{% endfor %}
<input type="hidden" name="post" value="yes" />
<input type="hidden" name="action" value="my_action" />
<input type="submit" value="Confirm" />
</div>
</form>
admin.py
def require_confirmation(func):
def wrapper(modeladmin, request, queryset):
if request.POST.get("confirmation") is None:
request.current_app = modeladmin.admin_site.name
context = {"action": request.POST["action"], "queryset": queryset}
return TemplateResponse(request, "admin/action_confirmation.html", context)
return func(modeladmin, request, queryset)
wrapper.__name__ = func.__name__
return wrapper
#require_confirmation
def do_dangerous_action(modeladmin, request, queryset):
return 42/0
admin/action_confirmation.html
{% extends "admin/base_site.html" %}
{% load i18n l10n admin_urls %}
{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation
delete-selected-confirmation{% endblock %}
{% block content %}
<p>Are you sure you want to {{ action }}?</p>
<ul style="padding: 0">
{% for object in queryset.all %}
<li style="list-style: none; float: left; margin: 5px">
{{ object }}
</li>
{% endfor %}
</ul>
<hr>
<br>
<form action="" method="post">{% csrf_token %}
<fieldset class="module aligned">
{% for obj in queryset.all %}
<input type="hidden" name="_selected_action" value="{{ obj.pk|unlocalize }}"/>
{% endfor %}
</fieldset>
<div class="submit-row">
<input type="hidden" name="action" value="{{ action }}"/>
<input type="submit" name="confirmation" value="Confirm"/>
<a href="#" onclick="window.history.back(); return false;"
class="button cancel-link">{% trans "No, take me back" %}</a>
</div>
</form>
{% endblock %}
Just in case of anyone wanting to add a confirmation view to something more than an action.
I wanted to make the save of an admin creation view go to a confirmation view. My model was very complex and created a lot of implications for the system. Adding the confirmation view would make sure that the admin was aware of these implications.
The solution would be overriding some _changeform_view method which is called on the creation and the edition.
The full code is here: https://gist.github.com/rsarai/d475c766871f40e52b8b4d1b12dedea2
Here is what worked for me:
Add confirm action method (in admin.py)
from django.template.response import TemplateResponse
def confirm_my_action(modeladmin, request, queryset):
response = TemplateResponse(request,
'admin/confirm_my_action.html',
{'queryset': queryset})
return response
and point to it from your admin model (in admin.py)
class SomeModelAdmin(admin.ModelAdmin):
actions = [confirm_my_action]
Add template, which has a form whose action is pointing to my_action endpoint.
{% extends "admin/base_site.html" %}
{% block content %}
<div id="content" class="colM delete-confirmation">
<form method="post" action="/admin/my_action/">
{% csrf_token %}
<div>
{% for obj in queryset %}
<input type="hidden" name="obj_ids[]" value="{{ obj.pk }}" />
<ul><li>Obj: ">{{obj}}</a></li></ul>
{% endfor %}
</div>
<input type="submit" value="Yes, I'm sure">
No, take me back
</form>
<br class="clear">
<div id="footer"></div>
</div>
{% endblock %}
Add appropriate endpoint (e.g. in urls.py).
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^admin/my_action/', my_action_method),
]
I am trying to customize Django's comments form. Inside django.contrib.comments.forms I noticed that all the field forms are declared in the class CommentDetailForm, which is inherited from CommentSecurityForm. Then I think when I write the template tag {% get_comment_form for order as form %}, it's getting the class called CommentForm which inherits CommentDetailForm with a honeypot field.
I wanted to customize the comments form so that it only displays the comments field (and not the optional name, email, or URL fields). Those information will be given by the current logged in user. In fact, only logged in users with certain UserProfile.user_type (UserProfile has a foreign key to User) are allow to comment.
Any tips on how to achieve this? Looking at the source code of the Django's comments already scares me lol.
EDIT:
Here is how the comment template looks so far:
{% get_comment_form for order as form %}
<form action = "{% comment_form_target %}" method = "post">
{% csrf_token %}
{{ form }}
<input type = "submit" name = "submit" value = "Post">
</form>
And the site looks like this
I want to hide Name, Email address, and URL.
You should be able to do all of this in the template:
{% ifequal User.profile.user_type "comment_type" %}
{% get_comment_form for order as form %}
<form action="{% comment_form_target %}" method="post">
{% csrf_token %}
{% for field in form %}
{% ifequal field.name "name" %}
<input id="id_name" type="hidden" name="name" value="{{ user.username }}" />
{% else %}{% ifequal field.name "email" %}
<input type="hidden" name="email" value="{{ user.email }}" id="id_email" />
{% else %}{{ field }}{% endifequal %}{% endifequal %}
{% endfor %}
<input type="submit" name="submit" value="Post">
</form>
{% endifequal %}