Do not understand how to pass data obtained from form.data in view as choices to form field. I would really appreciate any help.
So, here is what I am doing: I choose data using checkboxes at page, and then I need to display chosen data (their names) at the same page with additional textfield near each. Later all textfields must be filled and 'names' and inserted data must be sent to server.
As I understand for this I need to render data with textfield using form. I suppose it could be this one:
forms.py
import methods
class MultiTextField(SelectMultipleField):
widget = widgets.TableWidget()
option_widget = widgets.TextInput()
class SelectedForm(Form):
choices = methods.selected()
value = MultiTextField('value',choices = choices)
views.py
from forms import ...
selected_data = {}
def return_selected():
return selected_data
methods.py
from views import return_selected
def selected():
data = return_selected()
choices = []
for key, value in data.iteritems():
for item in value:
choices.append((item, key))
return choices
Variable selected_data stores data that were chosen through other form.
If I run this code I got error ImportError: cannot import name return_selected. Probably it is because I import views in methods, methods in forms and forms in views o.O
I see no other way how to make what I need, but it does not work as it is.
Well, I found how to pass choices.
forms.py
class MultiTextField(SelectMultipleField):
widget = widgets.TableWidget()
option_widget = widgets.TextInput()
class SelectedForm(Form):
name = MultiTextField()
views.py
#app.route('/home', methods=['GET', 'POST'])
def show_work():
selected = SelectedForm(request.form)
choices = []
for key, value in selected_data.iteritems():
choices.append((key, value))
selected.name.choices = choices
return render_template('home.html', selected=selected)
It is possible to add form choices in views with selected.name.choices = choices.
But this form puts data in strange way:
home.html
<form name="selected" action="{{url_for('selected_work', url='result') }}" method="post">
<p> {{selected.name}}</p>
<input type="submit" value="Calculate">
</form>
in choices was lets say: [(1,"Apple"), (2,"Apple")] but html show label 'Apple' and near it textfield with inserted number 1 and again label 'Apple' with number 2 inserted in textfield.
When I submit form ofcourse it sends inserted data in textfield, which is ['1', '2']. But somehow I need obtain: [(Apple, 1 and value inserted in textfield),(Apple, 2, inserted value)].
You have a circular dependency. Views import forms, which imports methods, which imports forms.
But your proposed solution is extremely dangerous. You are storing data at module level, which is absolutely not thread-safe and will lead to information leaking between users. Do not do this. Store the data in the session, which is per-user.
Thank you Daniel for your advice about session. I store chosen data in session now.
Here is how I solved my task. I decided not to use wtforms form for rendering data chosen with checkboxes. I keep all choices in session
views.py
import methods
from forms import ...
def make_record(works):
if session['data']:
if not works:
pass
else:
for work in works:
session['data'].append(work)
else:
session['data'] = [works]
#app.route('/home', methods=['GET', 'POST'])
def show_work():
demount = DemountForm(request.form)
tile = TileForm(request.form)
chosen = []
for each in session['data']:
chosen.append(each)
selected = methods.get_selected(chosen)
return render_template('home.html', demount=demount, tile=tile, selected = selected)
#app.route('/home/<path:url>', methods=['GET', 'POST'])
def select_work(url):
db = url
if db:
form = DemountForm(request.form)
work = form.name.data
make_record(work)
return redirect(url_for('show_work'))
methods.py
def get_selected(ids):
selected = {}
for each in ids:
data = db_session.query(Work.name, Work.dimension).filter(Work.id==each).all()
data = data[0]
selected[each] = data
return selected
home.html
{% if selected %}
<div id="chosen">
<form action="/home">
{% for key, value in selected.iteritems() %}
{{value[0]}}<input type="text" id="{{key}}">{{value[1]}}<br>
{% endfor %}
<input type="submit" value="Submit">
</form>
</div>
{% endif %}
In such way I obtained what I needed. On one page I got menu with checkboxes and div which display choices and textfields near each to insert additional data.
Related
I'm currently just learning Django and I'm doing electronic grade book. I have tried everything, have read all the documentation, but nothing helps. It seems I miss a simple logic somewhere. I need to make two pages:
The first one "teacher_interface" is a simple inteface for the teacher with just one drop-down list, teacher chooses the necessary class (i.e 1C, 2B, 4C) and the button "Students", which should somehow take the chosen class from drop-down list input and redirect to the second page "class_students".
The second "class_students" is alike the "teacher_interface", but with the table of students of the chosen class.
I have the One-to-many relation between classes Student and Class:
Firstly, I tried redirecting from "teacher_interface" to "class_students", using in template:
{% url "name" %}
Parts of code: 1) models.py https://dpaste.org/eqxm 2) urls.py https://dpaste.org/eUEO 3) views.py https://dpaste.org/ap8D#L 4) template teacher_interface.html https://dpaste.org/v4m9 5) template class_students.html https://dpaste.org/0gXK
But it shows me: Reverse for 'class_students' with no arguments not found. 1 pattern(s) tried: ['school/teacher/(?P<class_id>[0-9]+)/class/$']
I tried everything, but nothing helped, this and the similar: Django - getting Error "Reverse for 'detail' with no arguments not found. 1 pattern(s) tried:" when using {% url "music:fav" %} I understood maybe this two options of redirect will not work in my case:
{% url 'class_students' class.id %}
{% url 'class_students' class_id %}
I also don't know if it's possible to do on the same page.
So I decided to redirect using redirect from django.shortcuts. I changed my teacher_interface view, so that it took the id of the chosen by the teacher class if request method is POST and redirected. I also made this change in my template "teacher_interface.html":
from
action="{% url 'class_students' %}"
to
action=""
Changed view:
def teacher_interface(request):
class_queryset = Class.objects.order_by("class_number", "group")
class_id = None
if request.method == "POST":
class_id = Class.objects.get("id")
return redirect("class_students", class_id)
context = {
"class_queryset": class_queryset,
"class_id": class_id,
}
return render(request, "teacher_interface.html", context)
But when I choose the class and click the "Students" button, it shows me: Cannot resolve keyword 'i' into field. Choices are: class_number, curriculum, discipline, group, id, student, task, type_of_class, type_of_class_id. Id is certainly is a key, but it tries to resolve only "i".
I tried/read everything here, but nothing works.
I even wrote the default like this:
class_id = Class.objects.get("id", "default")
I am sure I just don't understand properly how to get teacher's choice, pass it to another or the same function and redirect, saving this information. I will be really grateful for you help, even if you just advise what I can read to figure it out.
Ok, you are missing some basic conpects.
on your views.py
def teacher_interface(request):
class_queryset = Class.objects.order_by("class_number", "group")
context = {
"class_queryset": class_queryset,
}
return render(request, "teacher_interface.html", context)
this is correct, you will pass you query to your template
on your template change some things to look like this:
<form method="POST" >{% csrf_token %}
<select name="input1">
{% for class in class_queryset %}
<option value="{{ class.id }}">{{ class }}</option>
{% endfor %}
</select>
<input type="submit" value="Students"/>
</form>
then you need to change your teacher_interface view:
You need to import redirect on your views.py
def teacher_interface(request):
class_queryset = Class.objects.order_by("class_number", "group")
context = {
"class_queryset": class_queryset,
}
if request.method == 'POST':
class_id = request.POST.get('input1') # I'm not sure if this will get the {{class.id}} value, if don't, print(request.POST.get) and check how to get the value
return redirect('class_students', class_id=class_id) # will make a get request on the class_students view
return render(request, "teacher_interface.html", context)
def class_students(request, class_id):
# the parameter need to be 'class_id' because this is what you put on your urls '<int:class_id>', if possible, remove that /class.
# ADD CLASS ID AS PARAMETER, THAT WILL ENABLE YOU TO ACESS AN SPECIFIC CLASS
# Import get_object_or_404 (google it and you will find easily)
class = get_object_or_404(Class, pk=class_id) # this avoid internal server error.
# pass your class on the context
return render(request, "class_students.html")
Background:
I'm building a personal dictionary web-application, and have a queryset of terms and definitions. In my web app, I have an edit page, where I want to show a ModelFormSet that allows the user to edit any/all entries, and delete them if needed - the form has a delete button for each row and a submit button to save changes to the form.
The current issue is that when I click on a set to edit, the formset shows up correctly, and when I change a term and hit "Submit" the form updates to show the change. However, when I go back to my "View Set" page, the term hasn't been updated, which I assume means the change didn't go through to the actual database - the form itself was simply submitted. This is what I would like to currently fix, and I also want to make it so that the delete button deletes the entry.
What I've Tried:
I've gone through every StackOverflow question I could find relating to the topic, and various solutions were: add an instance parameter when passing in "request.POST", re-initialize the formset after saving, change the "action" url in the HTML page, etc., but every solution I try either results in another error or doesn't change anything.
I also checked the documentation but the examples provided were for simpler examples, and I'm not sure where exactly my error is in this case.
Finally, I also used {{ form.errors }} to see if the forms were being validated, and this resulted in an empty bracket and no issues, so I think I know for sure that the form is being validated, and the POST request is working.
Code:
MODELS.PY
class Set(models.Model):
title = models.CharField(max_length = 64, null = False, blank = False)
description = models.CharField(max_length = 255, null = False, blank = True)
def __str__(self):
return self.title
class Entry(models.Model):
set = models.ForeignKey(Set, on_delete=models.CASCADE)
term = models.TextField()
definition = models.TextField()
def __str__(self):
return self.term
FORMS.PY
from django.forms import ModelForm
from django.forms.models import modelformset_factory
from .models import Set, Entry
class SetForm(ModelForm): # Form that maps to Set
class Meta:
model = Set
fields = ['title', 'description']
class EntryForm(ModelForm):
class Meta:
model = Entry
fields = ['set', 'term', 'definition']
VIEWS.PY
def editEntry(request, set_id):
EntryFormSet = modelformset_factory(Entry, EntryForm, extra=0)
set_obj=Set.objects.get(id=set_id)
entry_list = set_obj.entry_set.order_by("term")
entry_formset=EntryFormSet(queryset=entry_list)
if request.method == 'POST':
instances=entry_formset.save()
for instance in instances:
instance.save()
entry_formset = EntryFormSet(queryset=instances)
else:
entry_formset = EntryFormSet(queryset=entry_list)#formset_factory(entry_form)
return render (request, 'dictTemplates/editEntries.html', {'entry_formset': entry_formset})
EDIT ENTRIES.HTML
<h1 style="text-align:center"><strong></center>Edit Entries Page</strong></h1>
<form method="POST" action = "">
{% csrf_token %}
{{ entry_formset.management_form }}
<center><table id="entriesFormSet" class="table">
<input type ="submit" value ="Submit Form">
<tr>
<th><h3>Terms</h3></th>
<th><h3>Definitions</h3></th>
</tr>
<tbody>
{% for form in entry_formset %}
<tr>
<td>{{ form.term }}</td>
<td>{{ form.definition }}</td>
<td class="delete-entry-button"><input type = "submit" value = "Delete Term"></td>
</tr>
{% endfor %}
</tbody>
</table></center>
</form>
URLS.PY
urlpatterns = [
path('', views.home, name='Home'),
path('about/', views.about, name='About'),
path('sets/', views.sets, name='Sets'),
path('sets/create/', views.createSet, name='createSet'),
path('sets/edit/(?P<set_id>[\d]+)', views.editSet, name='editSet'),
path('sets/delete/(?P<set_id>[\d]+)', views.deleteSet, name='deleteSet'),
path('sets/view/(?P<set_id>[\d]+)', views.viewSet, name='viewSet'),
path('entrys/create/(?P<set_id>[\d]+)', views.createEntry, name='createEntry'),
path('entrys/edit/(?P<set_id>[\d]+)', views.editEntry, name='editEntry'),
path('entrys/delete/(?P<entry_id>[\d]+)', views.deleteEntry, name='deleteEntry'),
]
The desired result is that clicking "Submit" results in an updated form plus changes in the database, but right now, clicking "Submit" only results in an updated form at that moment - clicking to another page or any other action makes the form revert to the original state.
I think the problem is that my Submit button somehow isn't "mapping" to saving the form into the database, that there's a missing connection there somewhere, but I'm not sure how to find it.
Please let me know if I should format/post this question differently, as it's my first SO question. Thank you so much!! Any help would be very much appreciated!
Not sure if something else is also wrong, but your view doesn't instantiate your formset well. Try with the following code:
EntryFormSet = modelformset_factory(Entry, EntryForm, extra=0)
if request.method == 'POST':
entry_formset=EntryFormSet(data=request.POST)
if entry_formset.is_valid():
entry_formset.save() # this will save all the instances
else:
set_obj=Set.objects.get(id=set_id)
entry_list = set_obj.entry_set.order_by("term")
entry_formset=EntryFormSet(queryset=entry_list)
return render(request, 'dictTemplates/editEntries.html', {'entry_formset': entry_formset})
If you want you may assign return value to formset save like instances = entry_formset.save() and debug which instances are saved (that returned value will be a list of those that are saved to database) by passing it to template together with formset in context and show the value in template.
I am creating quiz-like web application for learning languages using Flask, Jinja, WTForms, SqlAlchemy etc. Once an user completes such a language course by successfully going through all levels stored in JSON file I want the app offer him a practice mode, where the user will answer randomly selected levels.
When I run the app, I can see radio buttons generated with values from random level as I want, but when I choose any answer and submit it, form.validate_on_submit() returns False and form.errors returns {'practiceform': [u'Not a valid choice']}. When I hard-code value to currentLevel variable, it works properly.
views.py
#user_blueprint.route('/courses/<course>/quiz/practice',methods=['GET','POST'])
#login_required
def practice(course):
courseClass = class_for_name("project.models", course.capitalize())
courses = courseClass.query.filter_by(email=current_user.email).first()
maxLevel = courseClass.query.filter_by(email=current_user.email).first().get_maxLevel()
currentLevel = randint(0, maxLevel-1) # If this value is hard-coded or fetched from db, it works correctly
dic = generateQuestion(course, currentLevel)
display = dic["display"]
correct = dic["correct"]
options = dic["options"]
form = PracticeForm(request.form)
form.practiceform.choices = [(option, option) for option in options]
if form.validate_on_submit():
practiceForm = form.practiceform.data
if ((practiceForm == correct) and courses):
# Do something
flash("Nice job", 'success')
return redirect(url_for('user.practice', course=course))
else:
# Do something else
flash("Wrong answer", 'danger')
return redirect(url_for('user.practice', course=course))
return render_template('courses/practice.html', form=form, display=display)
forms.py
class PracticeForm(Form):
practiceform = RadioField('practice')
practice.html
{% extends "_base.html" %}
{% block content %}
<form action='' method='POST' role='form'>
<p>
<!-- Tried put form.csrf, form.csrf_token, form.hidden_tag() here -->
{{ form.practiceform() }}
</p>
<input type="submit" value="submit" />
</form>
{% endblock %}
So what am I missing there? What makes difference between lets say hardcoded level 25, which works properly or if the number 25 is randomly generated within randint?
My guess is that option is a int, bug WTForms get a str from request.form.
When data comes back from requests it is treated as a string by WTForms unless you specify a type explicitly with the coerce kwarg of the wtforms.fields.*Field constructor:
practiceform = RadioField('practice', coerce=int)
So I found that randint() caused the problem because the practice(course) method was called on both GET and POST actions which led to having two different integers -> two different forms most of the time. So I refactored the code. kept the practice(course) method for GET action and created a new method which handles POST action and this solved the problem.
I have a a template with an html table in django. At the top of the view is a small form for input. Something like:
{% csrf_token %}
<form method="post" name="some_name" action="/myApp/">
search button here
...
</form>
<table>
loop through data and make table here
</table>
I when I access the webpage, I initialize data into the table. I have a backend sqlite database with > 25 million rows. I'd like to use the user input from the form to filter the data. I've already tried to get the data from the form, but when I try to apply objects.all.filter(some_condition), I don't see the table update. What am I doing wrong? Or has anyone else come up with a solution to this type of problem? I know this should be simple, but I've had a time figuring it out. Thanks!
UPDATE:
views.py
def showTable(request):
if request.method == 'POST':
#I have a table of aircraft entries.The EntriesTable has a ForeignKey
#referencing an AircraftTable that has the numbers of aircraft
#some_name will reference the number of the aircraft
aircraft = request.POST.get('some_name', None)
query_results = EntriesTable.objects.all().filter(aircraft__exact = filterable)
template=loader.get_template('myApp/showTable.html')
context=RequestContext(request, {'query_results': query_results,})
return HttpResponse(template.render(context))
else:
query_results = EntriesTable.objects.all().filter(start_time__range(start, stop)) #Assume that I have correctly filtered on start/stop times. I am able to render that so far without issue.
context= RequestContext(request, {'query_results': query_results,})
return HttpResponse(template.render(context))
query_results = EntriesTable.objects.filter(aircraft__exact = filterable)
You have an error in your view, the syntax should me:
NameOfModelClass.objects.filter(conditions)
I believe that will fix the problem
I have invite form with two fields defined as person and email as follows:
class InviteForm(Form):
person = TextField("person", validators=[validators.Required("Please enter persons name.")])
email = EmailField("email", validators=[validators.Required("Please enter valid email."), validators.Email("Please enter valid email.")])
def validate(self):
return validate_form(self)
Where validate_form function is a cusotm validator which check few conditions for invite.
My requirement is to allow users to invite more than one person at a time. To achieve this I have added jquery function which replicates these fields in html form and allow to invite multiple people.
But the problem is in my view function when I extract results from post request it gives only first persons information. How can I get all the persons details. My view is defined as follows:
#app.route("/invite", methods=["GET", "POST"])
def invite():
invite_form = InviteForm()
if invite_form.validate_on_submit():
print invite_form.person
print invite_form.email
This gives only one field, instead of array of fields.
Is this possible with python wtf? How?
What you're looking for is FormField which lets you build a list of the WTF Fields (or groups even).
Here's a simple example-- it'll render three string fields as a html list because that's the minimum required. If you want to add extras via javascript, then just adhere to the same naming scheme, in the case below, a fourth would have a name of person-3.
from flask import Flask, render_template_string
from flask.ext.wtf import Form
from wtforms import FieldList, StringField
app = Flask(__name__)
app.secret_key = 'TEST'
class TestForm(Form):
person = FieldList(StringField('Person'), min_entries=3, max_entries=10)
foo = StringField('Test')
#app.route('/', methods=['POST', 'GET'])
def example():
form = TestForm()
if form.validate_on_submit():
print form.person.data
## [u'One', u'Two', u'Three']
return render_template_string(
"""
<form method="post" action="#">
{{ form.hidden_tag() }}
{{ form.person }}
<input type="submit"/>
</form>
""", form=form)
if __name__ == '__main__':
app.run(debug=True)