I am working on a form builder similar to Google Forms but I came across an error when building the 'Edit response' function.
The error stated that the "Answer matching query does not exist."
This is my HTML code for the edit response page:
28 {% endif %}
29 {% for question in form.questions.all %}
30 <div class="margin-top-bottom box question-box">
31 {% if form.is_quiz %}
32 <h1 class="question-title txtClr">{{question.question}} {% if question.required %}<span class="require-star">*</span>{% endif %}</h1>
33 {% else %}
34 <h1 class="question-title txtClr" oncopy = "return false">{{question.question}} {% if question.required %}<span class="require-star">*</span>{% endif %}</h1>
35 {% endif %}
36 {% if question.question_type == "short" %}
37 <input type="text" name="{{question.id}}" class="short-answer" placeholder="Your answer" {% if question.required %} required {% endif %}
38 value="{{response|get_response:question.pk}}">
39 {% elif question.question_type == "paragraph" %}
40 <textarea name="{{question.id}}" placeholder="Your answer" class="long-answer textarea-adjust"
41 {% if question.required %} required {% endif %}>{{response|get_response:question.pk}}</textarea>
42 {% elif question.question_type == "multiple choice" %}
43 {% for choice in question.choices.all %}
44 <div class="multiple-choice">
45 {% if response|get_response:question.pk|to_int == choice.pk|to_int %}
46 <input type="radio" name="{{question.id}}" id="{{choice.id}}" {% if question.required %} required {% endif %} value="{{choice.id}}" checked>
47 {% else %}
48 <input type="radio" name="{{question.id}}" id="{{choice.id}}" {% if question.required %} required {% endif %} value="{{choice.id}}">
The error seems to be coming from line 38, and this string "{{response|get_response:question.pk}}" was in red.
views.py:
def edit_response(request, code, response_code):
formInfo = Form.objects.filter(code = code)
#Checking if form exists
if formInfo.count() == 0:
return HttpResponseRedirect(reverse('404'))
else: formInfo = formInfo[0]
response = Responses.objects.filter(response_code = response_code, response_to = formInfo)
if response.count() == 0:
return HttpResponseRedirect(reverse('404'))
else: response = response[0]
if formInfo.authenticated_responder:
if not request.user.is_authenticated:
return HttpResponseRedirect(reverse("login"))
if response.responder != request.user:
return HttpResponseRedirect(reverse('403'))
if request.method == "POST":
if formInfo.authenticated_responder and not response.responder:
response.responder = request.user
response.save()
if formInfo.collect_email:
response.responder_email = request.POST["email-address"]
response.save()
#Deleting all existing answers
for i in response.response.all():
i.delete()
for i in request.POST:
#Excluding csrf token and email address
if i == "csrfmiddlewaretoken" or i == "email-address":
continue
question = formInfo.questions.get(id = i)
for j in request.POST.getlist(i):
answer = Answer(answer=j, answer_to = question)
answer.save()
response.response.add(answer)
response.save()
if formInfo.is_quiz:
return HttpResponseRedirect(reverse("response", args = [formInfo.code, response.response_code]))
else:
return render(request, "index/form_response.html", {
"form": formInfo,
"code": response.response_code
})
return render(request, "index/edit_response.html", {
"form": formInfo,
"response": response
})
urls.py
path('form/<str:code>/response/<str:response_code>', views.response, name="response"),
path('form/<str:code>/response/<str:response_code>/edit', views.edit_response, name="edit_response"),
Does anyone have a solution to this? Thanks
Related
In an auction web. I'm trying to terminate adding any input (bids, comments and add to watchlist) by any user to the database if the owner of the product press on 'close ' button so I tried to implement this pseudo code:
in views.py
function listing:
t = 1
if t == 1:
if owner clicks on the close button:
...
t = 2
if the user adds a comment:
...
if the user adds a bid:
...
if user clicks on add to watchlist button:
...
else:
return message ('Auction has ended')
I have implemented this code and the listing page accepted entries and inputs after the owner of the listing clicked on the 'close' button. I also tried to change the value of t to a boolean value ( t = True, if t == True, if user clicked: return t = False) but it didn't work. so what is the problem.
def entry(request, name):
enter = listing.objects.get(title = name)
f= bids.objects.filter(product_id = enter)
x = 0
z = ""
last = None
ended = None
like = "k"
t = 1
if t == 1:
if request.method == 'POST' and request.POST.get("bid"):
biddd = int(request.POST['bid'])
user = request.user
if biddd > x:
bids.objects.create(bid= biddd , product_id= listing.objects.get(title = name).id, user = user)
for object in f:
if object.bid > x:
if object.bid > x:
x = object.bid
if request.method == 'POST' and request.POST.get('close_bid'):
final = listing.objects.get(title = name)
last = bids.objects.get(bid = x, product = final).user
t = 2
elif request.method == 'POST' and request.POST.get("bid"):
bidd = int(request.POST['bid'])
if bidd >= x:
messages.info(request, 'bid accepted')
z = "#0a720e"
else:
messages.info(request, 'bid already present')
z = "#f44336"
elif request.method == 'POST' and request.POST.get("watchlist"):
if request.user.is_authenticated:
productt = listing.objects.get(title = name)
user = request.user
exist = watchlist.objects.filter(product = productt, user = user)
if exist.exists():
messages.info(request, 'Already added to your watchlist')
else:
watchlist.objects.create(product = productt, user = user)
else:
return render(request, "auctions/login.html")
elif request.method == 'POST' and request.POST.get("comments"):
if request.user.is_authenticated:
comment = request.POST['comments']
product = listing.objects.get(title = name)
user = request.user
comments.objects.create(comment = comment, user = user, product = product)
else:
return render(request, "auctions/login.html")
commentproduct = listing.objects.get(title = name)
commentss= comments.objects.filter( product = commentproduct)
else:
messages.info(request, 'Auction has ended')
return render(request, "auctions/entry.html" , {"enter":enter, "bid": f, "x": x, "z":z, "comments":commentss, "winner": last, "ended": ended, "l": l})
in entry.html:
{% extends "auctions/layout.html" %}
{% load static %}
{% block body %}
<h1>Listing: {{enter}}</h1>
<img src="{{enter.image}}">
<p>{{enter.description}}</p>
<h3>Current Bid: {{x}}</h3>
<form action = "#" method="POST">
{% csrf_token %}
<label>Add bid:</label>
<input type="number" name="bid">
<input type="submit">
</form>
{% for message in messages %}
<div class="alert" style="background-color:{{z}}">
<span class="closebtn" onclick="this.parentElement.style.display='none';">×</span>
{{message}}
</div>
{% endfor %}
<form method='POST' action='#'>
{% csrf_token %}
<button type="submit" name="watchlist" value="Add to watchlist"> Add to watchlist</button>
</form>
{% for message in messages %}
<h5>{{message}}</h5>
{% endfor %}
<h2>Details:</h2>
<p><strong>Owner:</strong> {{enter.author}}</p>
<p><strong>Category:</strong> {{enter.category}}</p>
<br>
<h2>Add a comment:</h2>
<form method="POST" action="#">
{% csrf_token %}
<textarea name="comments" placeholder="Add a comment" style="width:600px"></textarea>
<input type="submit" value="Add comment">
</form>
{% if user.is_authenticated and user == enter.author %}
<form method='POST' action='#'>
{% csrf_token %}
<button type="submit" name="close_bid" value="Close bid"> Close Bid</button>
</form>
{% endif %}
<h2>Comments:</h2>
{% for comment in comments %}
<h3>{{comment.user}}:</h3>
<p>{{comment.comment}}</p>
{% endfor %}
{% if user.is_authenticated and user == winner %}
<div class="alert" style="background-color:green">
<span class="closebtn" onclick="this.parentElement.style.display='none';">×</span>
You won!
</div>
{% elif not user.is_authenticated %}
<p></p>
{% else %}
<div class="alert" style="background-color:red">
<span class="closebtn" onclick="this.parentElement.style.display='none';">×</span>
You lost!
</div>
{% endif %}
{% if user.is_authenticated and user == winner %}
<p>
You won!
</p>
{% elif not user.is_authenticated %}
<p>.</p>
{% else %}
<p>you lost</p>
{% endif %}
{% endblock %}
So I am trying to create an interactive quiz application using Flask. However, the issue I am running into is that I can't seem to save and output what the user selected. I tried to fix the quiz_answers function in the app.py and the quiz_solutions.html jinja2 syntax, but got an output that I did not want.
Example Question: What is 2+2?
Multiple choice: Option 1: 4, Option 2: 5, Option 3: 6, etc.
What I want the html page to show:
User selected: Option 2: 5
Correct Answer: Option 1: 4
Here is a what I currently have for app.py:
from flask import Flask, render_template, request
import random, copy
import questions
app = Flask(__name__)
copy_questions = copy.deepcopy(questions.original_questions)
def shuffle(q):
"""
This function is for shuffling
the dictionary elements.
"""
selected_keys = []
i = 0
while i < len(q):
current_selection = random.choice(list(q.keys()))
#current_selection = list(q.keys())
if current_selection not in selected_keys:
selected_keys.append(current_selection)
i = i+1
return selected_keys
questions_shuffled = shuffle(copy_questions)
#app.route('/')
def quiz():
#questions_shuffled = shuffle(copy_questions)
for i in copy_questions.keys():
random.shuffle(copy_questions[i])
return render_template('main.html', q = questions_shuffled, o = copy_questions)
#app.route('/solution', methods=['POST'])
def quiz_answers():
for i in copy_questions.keys():
answered = request.form[i]
if questions.original_questions[i][0] == answered:
return render_template('quiz_solutions.html',q = questions_shuffled, o = copy_questions)
break
if __name__ == '__main__':
app.run(debug=True)
questions.py:
original_questions = {
'What is 2+2': ['Option 1: 4', 'Option 2: 5', 'Option 3: 6',' etc.']
}
main.html:
<h1>Math Quiz</h1>
<form action='/solution' method='POST'>
<!--<form action='/' method='POST'>-->
<ol>
{% for i in q %}
<li>{{i}}</li>
{% for j in o[i] %}
<input type='radio' value='{{j}}' name='{{i}}' />{{j}}
<br>
{% endfor %}
<br>
{% endfor %}
<br>
<input type="submit" value="Submit" />
</ol>
</form>
quiz_solutions.html:
<h1>Math Quiz</h1>
<form method='POST'>
<ol>
{% for i in q %}
<li>{{i}}</li>
{% for j in o[i] %}
<input type='radio' value='{{j}}' name='{{i}}' />{{j}}
<br>
{% endfor %}
{% for j in o[i] %}
{% if j== o[i][0] %}
Correct answer: {{j}}
{% else %}
{% endif %}
{% endfor %}
<br>
{% endfor %}
</ol>
</form>
I have been researching how can I avoid using snippet of code over and over. The answer probably will involve using (generic) Class-based functions. However, I am a beginner in Django and this seems confusing. Here is my view in views.py:
#login_required(login_url='/login')
def view_list(request, listing_id):
bid = Bid.objects.all().filter(listing=listing_id).order_by('-id')
b_u = bid[0].user
listing = Listing.objects.get(pk=listing_id)
if request.method == "GET":
return render(request, "auctions/view_list.html", {
"form": BidForm(),
"total_bids": bid.count(),
"bid": None if bid == 0 else bid[0].bid,
"listing": listing,
"bid_user": "Your bid is the current bid." if request.user == b_u else None
})
else:
form = BidForm(request.POST)
if form.is_valid():
value = form.cleaned_data
if value['bid'] <= bid[0].bid:
error_check = True
return render(request, "auctions/view_list.html", {
"error_check": error_check,
"alert": f"Your bid is lower than the current bid $({bid[0].bid})! Try placing a higher one.",
"form": BidForm(),
"total_bids": bid.count(),
"bid": None if bid == 0 else bid[0].bid,
"listing": listing,
"bid_user": "Your bid is the current bid." if request.user == b_u else None
})
else:
error_check = False
new_bid = form.save(commit=False)
new_bid.user_id = request.user.id
new_bid.listing_id = listing.id
new_bid.save()
return render(request, "auctions/view_list.html", {
"error_check": error_check,
"alert": "Your bid was successfully placed!",
"form": BidForm(),
"total_bids": bid.count(),
"bid": None if bid == 0 else bid[0].bid,
"listing": listing,
"bid_user": "Your bid is the current bid." if request.user == b_u else None
})
And here is my template code:
{% extends "auctions/layout.html" %}
{% load humanize %}
{% load crispy_forms_tags %}
{% block body %}
{% if error_check == True %}
<div class="alert alert-warning" role="alert">
{{ alert }}
</div>
{% elif error_check == False %}
<div class="alert alert-success" role="alert">
{{ alert }}
</div>
{% endif %}
<div>
<h3>Listing: {{ listing.title }}</h3>
<img src="{{ listing.image }}" alt="Listings' Images">
<p>{{ listing.description }}</p>
{% if not bid %}
<strong>${{ listing.price|stringformat:"1.2f" }}</strong>
{% else %}
<strong>${{ bid|stringformat:"1.2f" }}</strong>
{% endif %}
<p> {{ total_bids }} bid(s) so far. {% if bid_user %} {{ bid_user }} {% endif %}</p>
<form method="POST" name="bidding" action="{% url 'view_list' listing.id %}">
{% csrf_token %}
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">$</span>
</div>
<div style="margin: 0; padding: 0 2px; height: 6px;">
{% crispy form %}
</div>
<div class="input-group-append" >
<span class="input-group-text">.00</span>
</div>
<input type="submit" class="btn btn-primary" value="Place Bid">
</div>
</form>
<h4>Details</h4>
<li>Listed by: {{ listing.user }} </li>
<li>Category: {{ listing.category }} </li>
<li>Listing created at: {{ listing.created_at }} </li>
</div>
{% endblock %}
So how can I avoid all this repetition and make the code more succinct. Also, this way when the user places a successful bid, the rendered template does not contain the new information from the form.
The pattern is very simple
def some_view(request):
form = SomeForm(request.POST or None)
if request.method == 'POST' and form.is_valid():
# Form processing
return render(request, "auctions/view_list.html", {
"form": form
})
views.py
def create(request):
choicenumber = 3
submitbutton = request.POST.get('submit', False)
choicebutton = request.POST.get('add_choice', False)
if choicebutton:
choicenumber += 1
render(request, 'polls/create.html', {
'questionfields': Question.__dict__,
'choicenumber': choicenumber,
})
if submitbutton:
new_question = Question.objects.create(question_text=request.POST.get('question_text', ''),
pub_date=timezone.now())
if new_question.question_text == '':
context = {
'questionfields': Question.__dict__,
'error_message': "No poll question entered.",
'choicenumber': choicenumber,
}
del new_question
return render(request, 'polls/create.html', context)
else:
for i in range(choicenumber + 1):
choice = request.POST.get(('choice' + str(i)), '')
if choice:
choice_obj = Choice.objects.create(question=new_question, choice_text=choice)
new_question.choice_set.add(choice_obj)
new_question.save()
return HttpResponseRedirect(reverse('polls:detail', args=(new_question.id,)))
else:
return render(request, 'polls/create.html', context)
create.html
{% extends "polls/base.html" %}
{% block title %}Create a Poll{% endblock title %}
{% block header %}Create:{% endblock header %}
{% load custom_tags %}
{% block content %}
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="{% url 'polls:create' %}" method="post"> {% csrf_token %}
{% for field in questionfields %}
{% if field == 'question_text' %}
<label for="{{ field }}">{{ field|capfirst|replace }}:</label>
<input type="text" name="{{ field }}" id="{{ field }}">
<br>
{% endif %}
{% endfor %}
<br>
{% for choice in choicenumber|rangeof %}
<br>
<label for="choice{{ forloop.counter }}">Choice {{ forloop.counter }}:</label>
<input type="text" name="choice{{ forloop.counter }}" id="choice{{ forloop.counter }}">
<br>
{% endfor %}
<br>
<input type="button" name="add_choice" value="Add choice">
<br>
<br>
<input type="submit" value="Create" name="submit">
</form>
{% endblock content %}
I recently added the add_choice button to my template:
<input type="button" name="add_choice" value="Add choice">
Using this, I tried using a request.POST.get() to check if it has been clicked, and if so for it to increment 'choicenumber' and re-render the view with the increased value of choicenumber:
if choicebutton:
choicenumber += 1
return render(request, 'polls/create.html', {
'questionfields': Question.__dict__,
'choicenumber': choicenumber,
})
Since the increment causes choicenumber to increase, within the create.html the for loop for producing the input boxes for each choice --
{% for choice in choicenumber|rangeof %}
<br>
<label for="choice{{ forloop.counter }}">Choice {{ forloop.counter }}:</label>
<input type="text" name="choice{{ forloop.counter }}" id="choice{{ forloop.counter }}">
<br>
{% endfor %}
-- should loop once more than usual on the re-render. This does not happen however, and the website renders as originally.
Does anyone know how to make this work properly or improve my code? I am very new to the Django framework, so any advice would be great. Thanks!
EDIT::
updated views.py
def create(request):
choicenumber = 3
context = {
'questionfields': Question.__dict__,
'choicenumber': choicenumber,
}
submitbutton = request.POST.get('submit', False)
choicebutton = request.POST.get('add_choice', False)
if request.method == 'GET':
request.session['choicenumber'] = 0
if request.method == 'POST':
if choicebutton:
request.session['choicenumber'] += 1
if submitbutton:
new_question = Question.objects.create(question_text=request.POST.get('question_text', ''),
pub_date=timezone.now())
if new_question.question_text == '':
context = {
'questionfields': Question.__dict__,
'error_message': "No poll question entered.",
'choicenumber': choicenumber,
}
del new_question
return render(request, 'polls/create.html', context)
else:
for i in range(choicenumber + 1):
choice = request.POST.get(('choice' + str(i)), '')
if choice:
choice_obj = Choice.objects.create(question=new_question, choice_text=choice)
new_question.choice_set.add(choice_obj)
new_question.save()
return HttpResponseRedirect(reverse('polls:detail', args=(new_question.id,)))
else:
return render(request, 'polls/create.html', context)
EDIT 2::
updated create.html
{% extends "polls/base.html" %}
{% block title %}Create a Poll{% endblock title %}
{% block header %}Create:{% endblock header %}
{% load custom_tags %}
{% block content %}
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="{% url 'polls:create' %}" method="post"> {% csrf_token %}
{% for field in questionfields %}
{% if field == 'question_text' %}
<label for="{{ field }}">{{ field|capfirst|replace }}:</label>
<input type="text" name="{{ field }}" id="{{ field }}">
<br>
{% endif %}
{% endfor %}
<br>
{% for choice in {{ request.session.choicenumber }}|rangeof %}
# the update is here: choicenumber -> {{ request.session.choicenumber }}
# this defaults as 0 though, even if it is defined as 3.
<br>
<label for="choice{{ forloop.counter }}">Choice {{ forloop.counter }}:</label>
<input type="text" name="choice{{ forloop.counter }}" id="choice{{ forloop.counter }}">
<br>
{% endfor %}
<br>
<input type="button" name="add_choice" value="Add choice">
<br>
<br>
<input type="submit" value="Create" name="submit">
</form>
{% endblock content %}
Here is my route function :
#app.route('/home/shelter/<int:shelter_id>/', methods=['GET','POST'])
def showShelterDetails(shelter_id):
shelter = session.query(Shelter).filter_by(id = shelter_id).one()
if request.method == 'POST':
if request.form['shelterName']:
shelter.name = request.form['shelterName']
session.add(shelter)
session.commit()
flash("Shelter("+shelter.name+") edited successfully!")
return render_template('shelterdetails.html', shelter_id=shelter_id, shelter=shelter)
else:
flash(u'Error Occured!','error')
return render_template('shelterdetails.html', shelter_id=shelter_id, shelter=shelter)
if request.form['shelterAddress']:
shelter.name = request.form['shelterAddress']
session.add(shelter)
session.commit()
flash("Shelter("+shelter.name+") address edited successfully!")
return render_template('shelterdetails.html', shelter_id=shelter_id, shelter=shelter)
else:
flash(u'Error Occured!','error')
return render_template('shelterdetails.html', shelter_id=shelter_id, shelter=shelter)
else:
return render_template('shelterdetails.html', shelter_id = shelter_id, shelter = shelter)
Here is my HTML template :
{% extends "master.html" %}
{% block title %}Home{% endblock %}
{% block body %}
{% with messages = get_flashed_messages(category_filter=["message"]) %}
{% if messages %}
<div class="alert alert-success">
{% for message in messages %}
<strong>{{message}}!</strong>
{% endfor %}
</div>
{% endif %}
{% endwith %}
{% with errors = get_flashed_messages(category_filter=["error"]) %}
{% if errors %}
<div class="alert alert-danger">
{% for message in errors %}
<strong>{{message}}!</strong>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<form action="{{ url_for('showShelterDetails',shelter_id=shelter_id )}}" method = 'post'>
<fieldset class="form-group">
<label for="shelterName">Name :</label>
<input type="text" class="form-control" style="width:500px" name="shelterName" value='{{ shelter.name }}' >
</fieldset>
<fieldset class="form-group">
<label for="shelterName">Address :</label>
<input type="text" class="form-control" style="width:500px" name="shelterAddress" value='{{ shelter.address }}'>
</fieldset>
My problem is : The POST method doesn't check whether the shelterAddress field is empty or blank or not. Also while returning the success or failure messages. Only the top 1 message gets printed.
Why do the other messages not get printed ? Also why doesn't it check for empty fieds ?
EDIT 1:
Changed my view function to :
#app.route('/home/shelter/<int:shelter_id>/', methods=['GET','POST'])
def showShelterDetails(shelter_id):
shelter = session.query(Shelter).filter_by(id = shelter_id).one()
if request.method == 'POST':
if request.form['shelterName']!="":
shelter.name = request.form['shelterName']
session.add(shelter)
session.commit()
flash("Shelter("+shelter.name+") edited successfully!")
if request.form['shelterAddress']!= "":
shelter.name = request.form['shelterAddress']
session.add(shelter)
session.commit()
flash("Shelter("+shelter.name+") address edited successfully!")
else:
return render_template('shelterdetails.html', shelter_id = shelter_id, shelter = shelter)
ValueError: View function did not return a response
That's because you're returning early whether request.form['shelterName'] is truthy or not. You probably want one return statement at the very end of your view function instead.