Flask: Use multiple instances of one form - python

My website lets a teacher create multiple questions for an assignment. Once the assignment is created, a student can come and write an answer for each question.
My problem is that only one answer is saved for each entry. For example, there are two questions in an assignment. The answer for question 1 is "asdf", the answer for question 2 is "fdsa". The content for both answers will be saved as "asdf", when they should be unique.
I have tried printing request.form and it looks like this (excluding the csrf_tokens):
('code_content', 'asdf'), ('code_content', 'fdsa'), ('submit', 'Submit Assignment')]
So I know that fdsa is still in there somewhere, but I'm not able to access it. If this is important, there were two csrf_tokens that were the exact same when printing request.form.
To get that data, I created a 'GetQuestionContent()' form for as many questions in the assignment. Like this:
questions = []
question_content_forms = []
for question in Question.query.all():
if int(question.owner) == int(assignment_id):
questions.append(question)
question_content_forms = [GetQuestionContent() for item in range(0, len(questions))]
Then, in the HTML, I write the form like this:
<form method="POST">
{% for question in questions %}
<div class="accordion-item">
<h2 class="accordion-header" id="heading{{ question.id }}">
<button class="accordion-button collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapse{{ question.id }}"
aria-expanded="false"
aria-controls="collapse{{ question.id }}">
{{ question.title }}
</button>
</h2>
<div id="collapse{{ question.id }}"
class="accordion-collapse collapse"
aria-labelledby="heading{{ question.id }}"
data-bs-parent="#questionsAccordion">
<p>
<small>
<strong>Question Description</strong>
<br>
{% if question.description != "" %}
{{ question.description|safe }}
{% else %}
<em>The assignment creator did not provide a description for this question.</em>
{% endif %}
</small>
</p>
{{ question_content_forms[loop.index - 1].hidden_tag() }}
{% if question.type == "code" %}
<div class="code-box">
{{ question_content_forms[loop.index - 1].code_content(id = "editor") }}
</div>
{% endif %}
{% if question.type == "text" %}
{{ question_content_forms[loop.index - 1].text_content|safe }}
{% endif %}
</div>
</div>
{% endfor %}
{% if current_user.account_type == "Student" %}
{{ submit_button.submit(class="btn btn-outline-success mt-3", value="Submit Assignment")}}
{% endif %}
</form>
When the user presses submit, I want to get every answer and put it into their own entry for the StudentQuestionSubmission table in my database. This is what that code looks like:
if request.method == "POST" and submit_button.data:
print(request.form)
index = 0
for question in questions:
question_to_submit = StudentQuestionSubmission(question_id = int(question.id),
student_id = int(current_user.id))
if question.type == "code":
question_to_submit.question_content = question_content_forms[index].code_content.data
elif question.type == "text":
question_to_submit.question_content = question_content_forms[index].text_content.data
print(f"\n\n{ question_content_forms[index].code_content.data } \n \
{ question_content_forms[index].text_content.data } \n\n")
index += 1
db.session.add(question_to_submit)
assignment_to_submit = StudentAssignmentSubmission(assignment_id = int(assignment_id),
student_id = int(current_user.id),
has_submitted = True,
submission_date = date.today())
db.session.add(assignment_to_submit)
db.session.commit()
flash(f"'{assignment.name}' has been succesfully submitted.")
return redirect(url_for('classroom_assignments_list', class_id = class_id, paper_id = paper_id))
You can see that I print the data of the textboxes. It will output 'asdf' on both iterations even if I wrote something entirely different for question 2.
I appreciate any help. Thank you.
EDIT: 'hackily' getting the content from multiple instances of the same form using request.form.to_dict(flat=False)['your_form_field']
Here's the new code:
if request.method == "POST" and submit_button.data:
code_content = request.form.to_dict(flat=False)['code_content']
text_content = request.form.to_dict(flat=False)['text_content']
code_content_index = 0
text_content_index = 0
for question in questions:
question_to_submit = StudentQuestionSubmission(question_id = int(question.id),
student_id = int(current_user.id))
if question.type == "code":
question_to_submit.question_content = code_content[code_content_index]
code_content_index += 1
elif question.type == "text":
question_to_submit.question_content = text_content[text_content_index]
text_content_index += 1
print(f"\n\n{ question_to_submit.question_content } \n\n")
db.session.add(question_to_submit)
assignment_to_submit = StudentAssignmentSubmission(assignment_id = int(assignment_id),
student_id = int(current_user.id),
has_submitted = True,
submission_date = date.today())
db.session.add(assignment_to_submit)
db.session.commit()
flash(f"'{assignment.name}' has been succesfully submitted.")
return redirect(url_for('classroom_assignments_list', class_id = class_id, paper_id = paper_id))

My suggestion is not to create multiple forms, but to dynamically create one form for all the necessary questions.
In this case it is possible to assign a unique id to each field, which indicates that it belongs to the question.
The following example shows you a possible implementation.
An answer field is added to the form for each question, which has the id of the question in its name. Thus, the respective question can be assigned when rendering and querying the input data.
Flask
from flask import (
Flask,
render_template,
request
)
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm
from wtforms import TextAreaField, SubmitField
from wtforms.validators import DataRequired
import random
app = Flask(__name__)
app.secret_key = 'your secret here'
db = SQLAlchemy(app)
class Question(db.Model):
id = db.Column(db.Integer, primary_key=True)
type = db.Column(db.String)
owner = db.Column(db.Integer)
title = db.Column(db.String)
description = db.Column(db.Text)
with app.app_context():
db.drop_all()
db.create_all()
qs = [Question(
type=random.choice(['code', 'text']),
owner=1,
title=f'Question {i+1}',
description=f'Your description here.'
) for i in range(3)]
db.session.add_all(qs)
db.session.commit()
def form_factory(qs):
class F(FlaskForm):
submit = SubmitField('Submit')
for q in qs:
field = TextAreaField(
q.type.title(),
validators=[
# DataRequired()
],
)
setattr(F, f'q-{q.id}', field)
return F
#app.route('/', methods=['GET', 'POST'])
def index():
questions = Question.query.filter_by(owner=1).all()
form = form_factory(questions)(request.form)
if form.validate_on_submit():
for q in questions:
field = getattr(form, f'q-{q.id}')
print(f'Question-{q.id}\n{field.data}\n')
return render_template('index.html', **locals())
HTML (templates/index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Index</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
</head>
<body>
<div class="container my-3">
<form method="post">
{{ form.hidden_tag() }}
<div class="accordion mb-3" id="accordionExample">
{% for q in questions -%}
<div class="accordion-item">
<h2 class="accordion-header" id="heading-{{loop.index}}">
<button
class="accordion-button collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapse-{{loop.index}}"
aria-expanded="false"
aria-controls="collapse-{{loop.index}}"
>
{{ q.title }}
</button>
</h2>
<div
id="collapse-{{loop.index}}"
class="accordion-collapse collapse"
aria-labelledby="heading-{{loop.index}}"
data-bs-parent="#accordionExample"
>
<div class="accordion-body">
<div class="mb-3">
{{ q.description|safe }}
</div>
{% set field = form|attr('q-{}'.format(q.id)) -%}
<div>
{{ field.label(class_='form-label') }}
{{ field(class_='form-control' + ('', ' editor')[q.type=='code']) }}
</div>
</div>
</div>
</div>
{% endfor -%}
</div>
<div class="d-grid gap-2">
{{ form.submit(class_='btn btn-primary') }}
</div>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script>
</body>
</html>

Related

How to get different names in request.post method

I got a problem, I'm making a web for SQLServer Agent Jobs execution, where if my job got parameters it redirects me to another page where I can enter my parameters. My problme is submiting these parameters. I don't know how to send all the parameters from my html to my view.py. All inputs have different names and I really don't know how to make the request accept different input names. I'll add some code below. IM commenting the line where I have the problem
parameters.html
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %} Parametros {% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
</head>
<div class="container">
<div class="row">
<div class="col-md-4 offset-md-4">
<br><br>
<div class="card">
<div class="card-header">
<h3>ParĂ¡metros</h3>
</div>
<div class="card-body">
<form method="post">
{% csrf_token %}
{% for i in parameters %}
<!-- Parameters input -->
<div class="form-outline mb-4">
<label class="form-label" for="form2Example1">{{ i.parameter_name }}</label>
{% if i.parameter_type == 'Int' %}
<input type="number" id="form2Example1" class="form-control" name="int" />
{% else %}
{% if i.parameter_type == 'Decimal' %}
<input type="text" id="form2Example1" class="form-control" name="decimal" />
{% else %}
{% if i.parameter_type == 'String' %}
<input type="text" id="form2Example1" class="form-control" name="string" />
{% else %}
{% if i.parameter_type == 'Date' %}
<input type="date" id="form2Example1" class="form-control" name="date" />
{% endif %}
{% endif %}
{% endif %}
{% endif %}
</div>
{% endfor %}
<!-- Submit button -->
<button type="submit" class="btn btn-primary btn-block mb-4">Ejecutar</button>
</form>
</div>
</div>
</div>
</div>
</div>
views.py
#job execution
def ejecutar(request, job_name):
cursor = connection.cursor()
job = Jobs.objects.get(job_name=job_name)
if job.flag_params == 0:
cursor.execute("EXEC msdb.dbo.sp_start_job '" + job.job_name + "'")
return HttpResponse("""<html><script>alert("Job ejecutado");window.location.replace('/home');</script></html>""")
else:
#redirects to parameters.html
redirect('parameters.html', job_name=job.job_name)
#get job parameters
parameters = Parametros.objects.filter(job_id=job.id)
if request.method == 'POST':
for parameter in parameters:
#update parameters
#Here I got the problem, in request.POST(), how can I get different input names from parameters.html (decimal, int, string, date)
cursor.execute("UPDATE Jobs_parametros SET parameter_value='" + request.POST['decimal'] + "' WHERE parameter_name='" + parameter.parameter_name + "' AND job_id='" + str(job.id) + "'")
cursor.execute("EXEC msdb.dbo.sp_start_job '" + job.job_name + "'")
return HttpResponse("""<html><script>alert("Job ejecutado");window.location.replace('/home');</script></html>""")
return render(request, 'parameters.html', {'parameters': parameters})
models.py
class Usuarios(models.Model):
id = models.AutoField(primary_key=True)
username = models.CharField(max_length=100)
class Jobs(models.Model):
id = models.AutoField(primary_key=True)
job_name = models.CharField(max_length=100)
flag_activo = models.BooleanField(default=True)
flag_params = models.BooleanField(default=False)
class DetailJobUser(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(Usuarios, on_delete=models.CASCADE)
job = models.ForeignKey(Jobs, on_delete=models.CASCADE)
class Parametros(models.Model):
id = models.AutoField(primary_key=True)
job = models.ForeignKey(Jobs, on_delete=models.CASCADE)
parameter_name = models.CharField(max_length=100)
parameter_type = models.CharField(max_length=100)
parameter_value = models.CharField(max_length=100)
Thanks for your answers :D

checking which decimal is bigger django

Im having problem with one line of code... Im trying to check which decimal is greater, i checked few answers from stackoverflow and they didnt work (probably cuz they were from 7 years ago :P) anyways here is my view:
auction = all_auctions.objects.get(id= my_id)
comment_num = auction.comments.all().count()
all_comments = auction.comments.all()
context = {
"auction": auction,
"bid_auction": auction.bid.all(),
"comment_num": comment_num,
"all_comments": all_comments
}
if request.method == "POST" and "bid" in request.POST:
if request.user.is_authenticated:
highest_bid = request.POST["bid"]
if not Decimal(highest_bid) <= Decimal(int(auction.bid.all().reverse()[0])):
all_bid = bids.objects.create(bid = highest_bid)
auction.bid.add(all_bid)
if request.method == "POST" and "watch"in request.POST:
if request.user.is_authenticated:
if auction not in request.user.watchlist.all():
request.user.watchlist.add(auction)
else:
request.user.watchlist.remove(auction)
if request.method == "POST" and "comment" in request.POST:
comment = request.POST["comment"]
if request.user.is_authenticated:
comment_now = comments.objects.create(comment = comment)
auction.comments.add(comment_now)
return render(request, "auctions/dynamic_auction.html", context)
and there is the problematic line:
if not Decimal(highest_bid) <= Decimal(int(auction.bid.all().reverse()[0])):
in models:
from django.contrib.auth.models import AbstractUser
from django.db import models
class comments(models.Model):
comment = models.TextField(blank=False)
class bids(models.Model):
bid = models.DecimalField(decimal_places = 2, max_digits = 100000)
class all_auctions(models.Model):
category = models.CharField(max_length= 14, default = "none")
title = models.CharField(max_length= 14)
description = models.TextField(blank=False)
photo_url = models.CharField(max_length= 500000)
bid = models.ManyToManyField(bids, blank = True, related_name = "bid_auction")
comments = models.ManyToManyField(comments, blank = True, related_name = "comments")
class User(AbstractUser):
created = models.ManyToManyField(all_auctions, blank = True, related_name = "created")
watchlist = models.ManyToManyField(all_auctions, blank = True, related_name = "watchlist")
user_bid = models.ManyToManyField(bids, blank = True, related_name = "user_bid")
and in the template where i can place bids and view them:
{% extends "auctions/layout.html" %}
{% block body %}
<div>
<h2>{{auction.title}}</h2>
<div style = "width: 1400px; padding: 10px; margin: 10px;" class = auction_class>
<img src="{{auction.photo_url}}" alt="no image">
<div>
{{auction.description}}
<p></p>
{% for bid in bid_auction %}
{% if forloop.last %}
bid:{{bid.bid}}
{% endif %}
{% endfor %}
<p></p>
{{auction.category}}
<p></p>
{% if user.is_authenticated %}
<form method = "POST"> {% csrf_token %}
<input class="btn btn-primary" type="submit" name = "watch" value="Add to watchlist">
</form>
<form method = "POST"> {% csrf_token %}
<div>
<input style = "margin:10px;" class="form-control" name = "bid" type="text" placeholder= "Bid...">
<input class="btn btn-primary" type="submit" value="Place Bid">
</div>
</form>
<form method = "POST"> {% csrf_token %}
<div>
<input style = "margin:10px;" class="form-control" name = "comment" type="text" placeholder= "Comment...">
</div>
</form>
{% endif %}
<p></p>
comments:
<p></p>
{% if comment_num == 0 %}
no comments
{% endif %}
<p></p>
<p></p>
{% for com in all_comments %}
{{ com.comment }}
<p></p>
{% endfor %}
</div>
</div>
</div>
{% endblock %}
and yes i made it so the last object of the bids queryset is shown, i have no idea how to show the biggest one, so also i would appreciate help with that . thanks :D
I guess this:
if not Decimal(highest_bid) <= Decimal(int(auction.bid.all().reverse()[0].bid)):
should work (note .bid in the end).
And why you can't use [-1]?
Although I haven't worked with django alot but since you want to compare decimals you could technically create a class which inherits the class Decimal() and then return a float and compare that float. Although I think this won't work (I haven't tried it because I don't like django in particular) but this could be a possibility.
EDIT:
Use a function to return a float from the class.
Example Code:
class DecimalNumber(Decimal):
def __init__(self, value, par2...):
self.value = value
...
def return_float(self):
float = self.value
return float

Comment is not displaying in django after submission

I have tried to submit a comment from app rather than django admin. Comment is not displaying when submitted from my created app. But it is displaying when I add a comment from django admin app. May be I am doing something wrong in views.py file. Can anyone help me on this? Thank you.
views.py:
#login_required
def book_review(request,id):
book = get_object_or_404(Bookslist, id=id)
comment=Comment.objects.all().filter(post_id=id)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.user_id = request.user
post.message= comment
post.save()
return redirect('book_review', id=id)
else:
form=CommentForm()
return render(request, "books/book_review.html", {'book':book, 'comment':comment, 'form': form})
models.py:
class Comment(models.Model):
message= models.TextField('Message',null=True)
date_comment=models.DateTimeField(default=now, null=True)
user_id= models.ForeignKey(User, on_delete=models.CASCADE,null=True)
post_id=models.ForeignKey(Bookslist,on_delete=models.CASCADE,null=True)
forms.py:
from django import forms
from .models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['message', ]
book_review.html:
{% extends 'books/base.html' %}
{% load static %}
{% block stylesheet %}
<link rel="stylesheet" href="{% static 'accounts/accounts.css' %}">
{% endblock %}
{% block content %}
<div class = "container">
<ul class = "nav nav-tabs" id = "myTab" role = "tablist">
<li class = "nav-item">
<a class = "nav-link active" id = "summary-tab" data-toggle = "tab"
href = "#summary" role = "tab" aria-controls = "summary"
aria-selected = "true">Summary</a>
</li>
<li class = "nav-item">
<a class = "nav-link" id = "characters-tab" data-toggle = "tab"
href = "#characters" role = "tab" aria-controls = "characters"
aria-selected = "false">Characters</a>
</li>
<li class = "nav-item">
<a class = "nav-link" id = "relatedbooks-tab" data-toggle = "tab"
href = "#relatedbooks" role = "tab" aria-controls = "relatedbooks"
aria-selected = "false">Related Books</a>
</li>
</ul>
<div class = "tab-content" id = "myTabContent">
<div class = "tab-pane fade show active" id = "summary" role = "tabpanel"
aria-labelledby = "summary-tab"><br><br>
{{book.summary}}
</div>
<div class = "tab-pane fade" id = "characters" role = "tabpanel"
aria-labelledby = "characters-tab"><br><br>
{{book.c1}}<br><br>{{book.c2}}<br><br>{{book.c3}}<br><br>{{book.c4}}<br><br>{{book.c5}}<br><br>
{{book.c6}}<br><br>{{book.c7}}<br><br>{{book.c8}}<br><br>{{book.c9}}<br><br>
{{book.c10}}
</div>
<div class = "tab-pane fade" id = "relatedbooks" role = "tabpanel"
aria-labelledby = "relatedbooks-tab">Content for related books tab</div>
</div>
<br>
<br>
<div class="container">
<form method="post" class="mb-4">
{% csrf_token %}
<div class="form-group">
<label for="exampleFormControlTextarea1">Leave your comment:</label>
<textarea class="form-control" id="exampleFormControlTextarea1" rows="3"></textarea>
</div>
<button type="submit" class="btn btn-success">Post</button>
</form>
{% for com in comment %}
<div class="card mb-2">
<div class="card-body p-3">
<div class="row">
<div class="col-2">
<img src="{% static 'images/icon2.webp' %}" alt="{{ com.user_id }}" class="w-100">
<small>Posts: {{ com.count }}</small></div>
<div class="col-10">
<div class="row mb-3">
<div class="col-6">
<strong class="text-muted">{{ com.user_id }}</strong>
</div>
<div class="col-6 text-right">
<small class="text-muted">{{ com.date_comment }}</small>
</div>
</div>
{{ com.message }}<br><br>
{% if com.user_id == user %}
<button type="button" class="btn btn-primary">Reply</button>
{% endif %}
</div>
</div></div></div></div>
{% endfor %}
</div>
</div>
</div>
<!-- jQuery library -->
<script src = "https://code.jquery.com/jquery-3.2.1.slim.min.js"
integrity = "sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
crossorigin = "anonymous">
</script>
<!-- Popper -->
<script src = "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
integrity = "sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
crossorigin = "anonymous">
</script>
<!-- Latest compiled and minified Bootstrap JavaScript -->
<script src = "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
integrity = "sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
crossorigin="anonymous">
</script>
{% endblock %}
You need to add the post_id to the newly created comment.
Like so:
post.post_id = id
Also make sure you are using the correct naming for your variables i believe post should be comment in this scenario based on the form being of CommentForm: model = Comment
This replacement would make for sense for the code:
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.user_id = request.user
comment.message= comment
comment.post_id = id
comment.save()

Flask - Nested FieldList doesn't submit correctly

Here is a form with fieldlist nested in a other fieldlist.
The purpose is to create an Excel like form.
There is one row per city and one column per date (cities and dates are dynamic lists).
I append the dates to the city and then I append the cities to the main form.
The form is loading as expected :
But after submition, it looks like that :
How can I prevend the second appending after submition ?
Here is my py file :
from flask import Flask
from flask import Flask, flash, redirect, render_template, request, url_for
from flask_wtf import FlaskForm
from wtforms import StringField, FieldList, FormField, SubmitField
app = Flask(__name__)
app.config['SECRET_KEY'] = 'key'
class DateForm(FlaskForm):
class Meta:
csrf = False
date = StringField('date')
class CityForm(FlaskForm):
class Meta:
csrf = False
city = StringField('city')
dates = FieldList(FormField(DateForm))
class MainForm(FlaskForm):
cities = FieldList(FormField(CityForm))
submit = SubmitField('Save')
#app.route('/', methods=['GET','POST'])
def home():
cities = ['NY', 'LA']
dates = ["2018", "2019"]
form = MainForm()
if form.validate_on_submit():
flash("validated on submit !", "alert alert-danger")
redirect(url_for('home'))
for c in cities:
city = CityForm()
city.city = c
for d in dates:
date = DateForm()
date.date = d
city.dates.append_entry(date)
form.cities.append_entry(city)
return render_template('template.html', form = form )
if __name__ == "__main__":
app.run(host="127.0.0.1", port="5000" ,debug=True)
And here is my html template :
<html>
<head>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-sm-6 offset-sm-3 p-0">
<form method="POST" action="{{ url_for('home')}}">
{{ form.hidden_tag() }}
<div class="form-group">
<table>
{% for c in form.cities %}
<tr>
<td>{{ c.city }}</td>
{% for d in c.dates %}
<td>{{ d.date.data }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
</div>
<div class="form-group">
{{ form.submit(class="btn btn-secondary") }}
</div>
</form>
</div>
</div>
<div class="row">
<div class="col-sm-6 offset-sm-3 p-0">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<p class="{{ category }}">{{ message }}</p>
{% endfor %}
{% endif %}
{% endwith %}
</div>
</div>
</div>
</body>
</html>
As per comments - missing a return:
return redirect(url_for('home'))

Django Voting : Sort by Votes

Lots of documentation on this already, but haven't been able to get any of them to work for me. Like the title implies, trying to get a set of objects that use Django Voting to sort based on the number of their votes (high to low). Have tried this and a few others, but nothing fruitful.
Using Django Voting, here is URL Conf & HTML
#urls.py
url(r'^$', 'questions'),
url(r'^(?P<object_id>\d+)/(?P<direction>up|down|clear)/vote/$',
vote_on_object, dict(model = Question, template_object_name = 'question',
template_name = 'qanda/confirm_vote.html', post_vote_redirect = '/home/', allow_xmlhttprequest=True)),
Questions in the URLconf leads to the questions view which renders questions.html
#questions.html
{% load voting_tags %}
{% votes_by_user user on the_question as vote_dict %}
{% scores_for_objects the_question as score_dict %}
<div class="question">
{% if the_question %}
<ul>
{% for question in the_question %}
{% dict_entry_for_item question from vote_dict as vote %}
{% dict_entry_for_item question from score_dict as score %}
<div class="votearrowdiv">
<div class="upvotearrow"></div></a>
<form class="linkvote" id="linkup{{ question.id }}" action="/home/{{ question.id }}/{% if vote and vote.is_upvote %}clear{% else %}up{% endif %}/vote/" method="POST">
{% csrf_token %}
<input type="image" id="linkuparrow{{ question.id }}" src="{{ media_url }}img/aup{% if vote and vote.is_upvote %}mod{% else %}grey{% endif %}.png">
</form>
<div class="downvotearrow"></div></a>
<form class="linkvote" id="linkdown{{ question.id }}" action="/home/{{ question.id }}/{% if vote and vote.is_downvote %}clear{% else %}down{% endif %}/vote/" method="POST">
{% csrf_token %}
<input type="image" id="linkdownarrow{{ question.id }}" src="{{ media_url }}img/adown{% if vote and vote.is_downvote %}mod{% else %}grey{% endif %}.png">
</form>
</div>
<li>
<div class="votecounter"><div class="numbercount">
<span class="score" id="linkscore{{ question_id }}"
title="after {{ score.num_votes|default:0 }} vote{{ score.num_votes|default:0|pluralize }}">
{{ score.score|default:0 }}
</span>
</div></div>
{{ question.question_text }}
{% endfor %}
{% endif %}
Here's my current view:
#views.py
def questions(request, movie_id):
p = Movie.objects.get(pk=movie_id)
k = Question.objects.filter(movie=p).order_by('q_pub_date')
l = k.reverse()
return render_to_response('qanda/questions.html', {'movie':p, 'the_question':l}, context_instance = RequestContext(request))
I know I can't sort using "score" because it's not in the model. What do I need to change in my view to sort this correctly?
EDIT:
Robert, here's models.py. Tried your solution, and a number of variations, but I don't have a voting attribute to the model. Take a look:
#models.py
class Question(models.Model):
movie = models.ForeignKey(Movie, blank=True, null=True)
question_text = models.CharField(verbose_name = "Question", max_length = 250)
question_detail = models.CharField(verbose_name = "Details (Optional)", max_length = 5000, blank = True, null = True)
q_pub_date = models.DateTimeField(auto_now_add = True)
q_author = models.ForeignKey(User)
Any insight?
It'd be handy if you posted your model.py, but I'm going to make some guesses.
Firstly, this might help:
#views.py
def questions(request, movie_id):
p = Movie.objects.get(pk=movie_id)
k = Question.objects.filter(movie=p).order_by('-q_pub_date')
...
(don't need to use reverse, can just begin it with -)
I'm going to guess that your score could be sorted as follows:
k = Question.objects.filter(movie=p).order_by('movie__score', '-q_pub_date')
The __ (double underscore) will refer to an attribute of related model.
I've been known to live and die by this: https://docs.djangoproject.com/en/dev/topics/db/queries/#related-objects

Categories