Queryset object in Django form not iterable - django

I'm trying to get the form to create the fields based on what exam page the user is on. In the error page, all local variables have the correct value for form and view, but I keep getting ExamQuestion object not iterable and an error at line 0 of the template. It also highlights the render() at line 44 in the view as the source of the problem. If I change line 28 from exam__name=exam_name to exam__name="exam_name", basically turning the variable into a str, the page runs but no data is passed.
In the error console choice_list shows querysets as individual list items as it should for forms.py
How do I make the object ExamQuestion iterable? I've been stumped for a week now. I've written a hundred ways at this point.
I know it's listing questions instead of answers for the questions, I'm just trying to get it to load ANY queryset and freaking run at this point.
view
def exampage(request, exam_name):
exams = Exam.objects.all()
questionlist = ExamQuestion.objects.filter(exam__name=exam_name)
choicelist = ExamChoice.objects.filter(question__exam__name=exam_name)
form = ExamTest(request.POST, exam_name=exam_name)
if request.method == "POST":
if form.is_valid():
#form.save()
#choice = form.cleaned_data.get('choice')
return redirect('exampage.html')
return render(request, 'exams/exampage.html', {'exams': exams,'questionlist': questionlist, 'exam_name': exam_name, 'choicelist': choicelist, 'form': form, 'choice': choice})
else:
form = ExamTest(exam_name=exam_name)
return render(request, 'exams/exampage.html', {'exams': exams,'questionlist': questionlist, 'exam_name': exam_name, 'choicelist': choicelist, 'form': form})
form
class ExamTest(forms.Form):
def __init__(self, *args, **kwargs):
exam_name = kwargs.pop('exam_name')
super(ExamTest, self).__init__(*args, **kwargs)
#choice_list = [x for x in ExamQuestion.objects.filter(exam__name="dcjs01")]
#choice_list = []
x = ExamQuestion.objects.filter(exam__name=exam_name)
#for q in x:
# choice_list.append(q)
self.fields["choices"] = forms.ChoiceField(choices=x, label="testlabel")
template
{% extends 'portal/base.html' %}
{% block content %}
<h1>{{ exam_name }} Page</h1>
{{ exam_id }}
<hr>
{% for exam in exams %}
<li>{{ exam }}</li>
{% endfor %}
<h1>! {{ questionlist }} !</h1>
<form method="post" action="#">
{% csrf_token %}
formtest{{ form }}
<button type="submit"> finish test </button>
</form>
{% endblock %}

The first part of the question is - you getting the ExamQuestion not iterable error:
here I think is the problem, that you, in the Form init function pass the Queryset (objects.filter(xxx)), but not the .all() which will select it.
the second thought is - would'n it be better to pass the questions as a parameter to the Form, as you previously selected all the question for this particular exam?

figured it out. choices=x needs to be a tuple
self.fields['name'] = forms.ChoiceField(choices=tuple([(name, name) for name in x]))

Related

Flask form not getting validated

My flask form is not getting validated.
#admin_blueprints.route('/ManageMovies',methods=['GET', 'POST'])
def ManageMovie():
form = SetShowForm(request.form)
if request.method == 'POST' and form.validate():
print(form.movie.data)
return redirect(url_for('admin.AdminHome'))
engine = create_engine('mssql+pyodbc://DESKTOP-6UNRAN0/movie_f?driver=SQL Server?
Trusted_Connection=yes')
form.movie.choices = [(movie.m_id, movie.m_name)for movie in (engine.execute('select * from
MovieMaster'))]
form.show_time.choices = [(time.s_id, time.s_time) for time in (engine.execute('select * from
ShowTime'))]
return render_template('manage_movies.html',form=form)
my template code is
{% extends "master.html" %}
{% block content %}
<form method="POST">
{{ form.hidden_tag() }}
{{form.movie.label}}{{form.movie(class="form-control")}}
<br>
{{ form.show_time.label }} {{form.show_time(class="form-control")}}
<br>
{{form.price.label}} {{ form.price(class="form-control") }}
<br>
{{form.submit(class="btn btn-success")}}
</form>
{% endblock %}
my flask form
class SetShowForm(FlaskForm):
movie = SelectField('Movie Name', choices=[])
show_time = SelectField('Set Show Time',choices=[])
price = IntegerField('Price')
submit = SubmitField("Set")
Once I click on my submit button, the same page gets rendered again instead of entering my (if request.method == 'POST' and form.validate():) statement and printing the data. I have no idea what is going wrong. I am filling all the fields. Are there any rule for form validation.
I believe you need to use:
if form.validate_on_submit():
And you don't need to check for "POST" because validate_on_submit does that too.
Try it
#admin_blueprints.route('/ManageMovies',methods=['GET', 'POST'])
def ManageMovie():
form = SetShowForm()
if form.validate_on_submit():
print(form.movie.data)
return redirect(url_for('admin.AdminHome'))
return render_template('manage_movies.html',form=form)
And set choices values in setShowForm() directly

Django: how to display my own dynamically-made fields

I have a SurveyForm where I create dynamically my fields. Here's the most basic code I could do and still have my problem:
class SurveyForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields = []
self.fields_alone = []
new_field = forms.CharField(label=f'1 - 2: This is the question',
widget=widgets.Input(attrs={}))
self.fields.append(('id_1', new_field))
self.fields_alone.append(new_field)
self.fields = OrderedDict(self.fields)
In my template, when I do a "classical" loop I can display the fields and it works, but the second loop, which is supposed to access to the same fields, doesn't work:
<form action="" method="post">
{% for field in form %}
{{ field.label }} :{{ field }} <br>
{% endfor %}
{% for field in form.fields_alone %}
{{ field.label }} :{{ field }} <br>
{% endfor %}
</form>
The second loop display the field as a string like <django.forms.fields.CharField object at 0x0000012C00EF60D0>
What am I missing to display is like the "classical" loop?
I found the problem. It's in the source code of Django.
Django can only convert fields to HTML if they are BoundField. The problem is that you can access to the BoundField "version" of the fields of your form through iteration on the form itself.
In the django/forms/forms.py:
def __getitem__(self, name):
# blabla code
if name not in self._bound_fields_cache:
self._bound_fields_cache[name] = field.get_bound_field(self, name)
So you can get HTML code only through an iteration on the form or direct access to a field via form['myfield'].
So in my form I did:
class SurveyForm(forms.Form):
def field_by_id(self, field_id):
return self[field_id]
Then I've made a template tag in my application which is:
#register.filter(name='field_by_id')
def field_by_id(arg1, arg2):
"""
Returns the result of field_by_id() (= method has to exist in arg1!)
Usage: {{ form|field_by_id:XXX }} (XXX = field id string)
:param arg1: object of class Form
:param arg2: field id string
:returns corresponding field
"""
return arg1.field_by_id(arg2)
and then in my template, I use it like this:
{% for question_group, questions in form.question_groups.items %}
{% for question, answers in questions.items %}
{% for answer in answers %}
{% with form|field_by_id:answer as field %}
{{ field.label }} :{{ field }} <br>
{% endwith %}
{% endfor %}
{% endfor %}
{% endfor %}
And it works. Tricky solution, but I have many sub-groups (I could have used FormSet's for a single sub-group).
The fields list is meant to store the form fields. Whereas fields_alone isn't.
That is also the reason why you are able to loop over the form itself and not over form.fields.
Since I cant figure out why you need to have this other list I suggest that you add all the fields to the actual fields list.
Comment if there are any further questions to this problem
Here:
self.fields = []
You're overwriting the form's own fields ordereddict. You just want to add to it:
self.fields["whatevernameyouwant"] = new_field
(works on django 1.11, not tested on 2.x / 3.x)
EDIT:
the problem is when I try to use the fields through another property
You mean the form.fields_alone thing ? Well, you can always make it a property instead:
class YourForm(...):
def __init__(...)
# ....
self._fields_alone = []
self.fields["whatevernameyouwant"] = new_field
self.fields_alone.append("whatevernameyouwant")
#property
def fields_alone(self):
for name in self._fields_alone:
yield self[name]
But it won't remove this field from the "normal" form iterator (for field in form) so you'll have the field twice, so you'd also need to redefine __iter__ too:
def __iter__(self):
for name in self.fields:
if name not in self._fields_alone:
yield self[name]

django not getting the online parameter when submitting a form

I submit a form with two inputs to search a table. My code doesn't get the value of 'namequery' instead of displaying all data in table. What did I do wrong here? Thanks for any help!
The url is
http://..../chinook/search/?namequery=rand&affquery=
search.html
<h3 class="labs-background-title">Search results for <em id="search-name">{{ namequery }}</em>:</h3>
{% if object_list %}
{% for obj in object_list %}
{{ obj.lname }} <br />
{{ obj.clustering }} <br />
{% endfor %}
{% else %}
<h3>No matches found.</h3>
{% endif %}
views.py
class SearchView(generic.ListView):
model = Pitable
template_name = 'chinook/search.html'
def get_queryset(self):
try:
namequery = self.kwargs['namequery']
except:
namequery = ''
if (namequery != ''):
object_list = self.model.objects.filter(lname = namequery)
else:
object_list = self.model.objects.all()
return object_list
The return page display all data, shows the {{namequery}} is empty. Thanks!
There are quite a few things wrong here.
Firstly, 'namequery' is not a URL kwarg, it is a GET query parameter. You need to get it from self.request['namequery'].
Secondly, never ever use a blank except. The only exception that could possibly happen there is KeyError, so catch that. But a better way of writing all four lines would be:
namequery = self.request.GET.get('namequery')
Thirdly, to display the value of namequery in the template you need to add it to the context data.
def get_context_data(self, **kwargs):
context = super(SearchView, self).get_context_data(**kwargs)
context['namequery'] = self.request.GET.get('namequery')
return context

Django - AttributeError => 'set' object has no attribute 'get'

I'm going through the book Django 1.0 Website Development where you build a small social bookmarking application. I'm at chapter 5 where you create a form to add bookmarks and although I've followed the instructions and have been struggling on this error for days. I get the error:
AttributeError at /save/
'set' object has no attribute 'get'
The error is being thrown on line 6 of the template {{ form.as_p }}
The views.py code is:
def bookmark_save_page(request):
if request.method == 'POST':
form = BookmarkSaveForm(request)
if form.is_valid():
# create or get link.
link, dummy = Link.objects.get_or_create(
url=form.cleaned_data['url']
)
# create or get bookmark.
bookmark, created = Bookmark.objects.get_or_create(
user=request.user,
link=link
)
# if bookmark is being updated, clear the old tag list
if not created:
bookmark.tag_set.clear()
# create new tag list
tag_names = form.cleaned_data['tags'].split()
for tag_name in tag_names:
tag, dummy = Tag.objects.get_or_create(name=tag_name)
bookmark.tag_set.add()
# save bookmark to database
bookmark.save()
return HttpResponseRedirect(
'/user/%s/' % request.user.username
)
else:
form = BookmarkSaveForm()
variables = RequestContext(request, {
'form' : form
})
return render_to_response('bookmark_save.html', variables)
And the template code is:
{% extends "base.html" %}
{% block title %}Save Bookmark{% endblock %}
{% block head %}Save Bookmark{% endblock %}
{% block content %}
<form method="post" action=".">{% csrf_token %}
**{{ form.as_p }}**
<input type="submit" value="save" />
</form>
{% endblock %}
Any help would be much appreciated as I'm stuck at this point in the book and can't seem to find an answer. Thanks!
Is this an error for you?
for tag_name in tag_names:
tag, dummy = Tag.objects.get_or_create(name=tag_name)
bookmark.tag_set.add() # not adding the tag?
Shouldn't it be: bookmark.tag_set.add(tag) ? The .add() doesn't actually cause an error, but I know you aren't adding your tag.
Without seeing the traceback, I'm guessing.
My other guess is that you might be using the RequestContext wrong?
return render_to_response('bookmark_save.html',
{'form': form},
context_instance=RequestContext(request))
I believe the way you are using it now is meant for the non-shortcut approach of using an HttpResponse()

Displaying None Field Errors in Django Template

I want to display my non_field_erors in my template. So far, I can display all kind of errors of my forms with:
-> base.html
{% if form.errors %}
{% for field in form %}
{% if field.errors %}
<div class="ui-state-error ui-corner-all notification" >
<p>
<span class="ui-icon ui-icon-alert"></span>
{{ field.label_tag }}:{{ field.errors|striptags }}
<a class="hide" onClick="hideBar(this)">hide</a>
</p>
</div>
{% endif %}
{% endfor%}
{% endif %}
AND
{{ form.non_field_errors }}
I've added a new form which has only an IntegerField:
class MerchantForm(forms.ModelForm):
price = forms.IntegerField(widget=forms.TextInput(attrs={'class':'small'}))
def clean_price(self):
price = self.cleaned_data.get('price')
if price == 120:
raise forms.ValidationError('error blah.')
return price
When I post price as 120, I don't get any validation errors in my page.
And my view is:
def bid(request,product_slug):
.
.
form = MerchantForm()
context = RequestContext(request,{
'form':form,
....
})
if request.method == 'POST':
form = MerchantForm(request.POST)
if form.is_valid():
return HttpResponse('ok')
# else:
# return HttpResponse(form.errors.get('__all__'))
return render_to_response('bid.html',context_instance=context)
I can retrieve the error with commented lines but I don't want to do that in views.py. Any ideas ?
Oh dear.
First of all, why are you asking about non_field_errors when the code snippet you post clearly has the error as being raised in clean_price, and therefore is associated with ths price field?
Secondly, your view code is upside down. You create an empty form instance, and add it to the context. Then you create another form instance, bound to the POST data, but don't put it into the context. So the template never sees the bound form, so naturally you never see any validation errors in the template.