reacting to users posts - python

Regardless of my specific problem below, whats an effective way to respond multiple times to a users posts on the same page. So that on each progressive post on the page can capture new requests and display new information? Forgive me if i'm not describing the problem correctly.
I'm trying to build a page that reacts the the users posts. But i'm running into a :
Bad Request
The browser (or proxy) sent a request that this server could not
understand.
I'm guessing because in my current solution:
#app.route('/survey', methods = ['GET', 'POST'])$
#contributer_permission.require(403)$
def survey():
organization_id = None
survey_header_id = None
survey_section_id = None
organization_selected = None
survey_header_selected = None
survey_section_selected = None
if request.method== 'POST':
if not organization_id:
organization_id = request.form['organization_id']
organization_selected = Organization.query.get(organization_id)
elif not survey_header_id:
survey_header_id = request.form['survey_header_id']
survey_header_selected = SurveyHeader.query.get(survey_header_id)
elif not survey_section_id:
pass
else:
pass
return render_template('survey.html',
organization_class = Organization,
organization_selected = organization_selected,
organization_id = organization_id,
survey_header_id = survey_header_id,
survey_header_selected = survey_header_selected,
survey_section_id = survey_section_id,
survey_section_selected = survey_section_selected)
once i receive the post carrying the survey_header_id. It reloops and
organization_id becomes none
Here is the the accompanying html/json
{% extends "base.html" %}
{% block content %}
<div class ="entries"> <!-- should be a DIV in your style! -->
<form action="{{url_for('survey') }}" method="post" class="add-entry"/>
<dl>
{% if not organization_id %}
{% for organization in organization_class.query.all() %}
<dt><input type="radio", name="organization_id",
value="{{ organization.id }}"/>{{ organization.name }}</dt>
{% endfor %}
<dt><input type ="submit", name="submit_organization"/>
{% elif not survey_header_id %}
<h1>{{ organization_selected.name }}</h1>
{% for survey_header in organization_selected.survey_headers.all() %}
<dt><input type="radio", name="survey_header_id"
value="{{ survey_header.id }}"/>{{ survey_header.name }}
{% endfor %}
<dt><input type ="submit", name="submit_survey_header"/>
{% elif not survey_section_id %}
<p>hi</p>
{% else %}
{% endif %}
<dl>
</form>
</div>
{% endblock %}
What should i be doing?

Bad Request is generally the result of accessing a parameter that doesn't exist, such as request.form['organization_id'] when there is no element in the form with that name.
Your survey route will always attempt to retrieve organization_id from the form because you set it to None, and there's nothing to change that before it's tested. On the second Post, your template does not create an organization_id element at all because the value was passed from the previous Post, so you see an error when survey() still attempts to retrieve it.
You need some way to pass your submitted values from one step to the next. For example, you could write them to hidden or disabled fields within the form so you can grab them and send them back out to the template after each Post, or store your state somewhere else like the session.

Related

Python Flask forms not capturing form input

I am running the latest Flask, Python 3.10. The app is installed on a Windows IIS Server. I have three forms on a single page. There is one submit button. Each form has a unique ID.
When I fill out one or all of the forms and hit the Submit button there is a view Class handler for the POST request. It is being called but the form data is missing.
One of the form elements looks like the following; the other two are similar but without the render_submit_field call:
<form id="add_session_form" action="{{ url_for('session_api') }}" method="POST" novalidate
role="form">
{{ session_form.hidden_tag() }}
{% for field in session_form %}
{% if not field.flags.hidden %}
{% if field.type == 'BooleanField' %}
{{ render_checkbox_field(field, tabindex=loop.index * 300) }}
{% elif field.type == 'SelectField' or field.type == 'SelectMultipleField' %}
{{ render_select_field(field, tabindex=loop.index * 300) }}
{% elif field.type == 'SubmitField' %}
{{ render_submit_field(field, tabindex=loop.index * 300) }}
{% else %}
{{ render_field(field, tabindex=loop.index * 300) }}
{% endif %}
{% endif %}
{% endfor %}
</form>
The POST method of the view Class looks like:
#staticmethod
def post():
try:
db_session = db()
school_form = SchoolForm(obj=current_user)
class_form = ClassForm(obj=current_user)
session_form = SessionForm(obj=current_user)
school_list = SchoolAPI.get_all_schools()
if school_form.validate_on_submit():
# Copy form fields to user_profile fields
school = School()
school_form.populate_obj(school)
db_session.add(school)
db_session.commit()
school_form = SchoolForm()
if class_form.validate_on_submit():
class_ = Class()
class_form.populate_obj(class_)
db_session.add(class_)
db_session.commit()
classes_form = ClassForm()
if session_form.validate_on_submit():
session = Session()
session_form.populate_obj(session)
db_session.add(session)
db_session.commit()
session_form = ClassForm()
return render_template(
'main/user_page.html',
current_user=current_user,
school_form=school_form,
class_form=class_form,
session_form=session_form,
school_list=school_list
)
except Exception as e:
return Response(repr(e), status=HTTPStatus.BAD_REQUEST)
The curious part is that this was working at the beginning of 2022. I was pulled onto another project and have recently been put back on this one. I updated all dependencies and am not seeing any errors.
I am not sure what to check at this point.
Ideas?

Django Formset: object has no attribute 'get'

I think I'm almost there, but the last part just doesn't want to work :(. I hope you can help as I'm not seeing it anymore after 2 days.
Within my FormWizard I'm trying to (1) show a Formset based on a Slider Input in a previous set (setting the Extra value) and (2) in each Form within the Formset I want to show a ChoiceField (forms.Select) based on a Text-input in a previous step.
With a lot of searching on stackoverflow I am able to do step 1. Step 2 is almost working, except for the fact that the ChoiceField doesn't update with the new values from the Text-input. This is my code in views.py:
class FormWizardView(LoginRequiredMixin, SessionWizardView):
template_name = 'test/test.html'
def get_form_initial(self, step):
if step == 'formset_step':
form_class = self.form_list[step]
data = self.get_cleaned_data_for_step('slider_step')
if data is not None:
# To set the extra value in formset based on slider input
extra = data['number_slider']
form_class.extra = extra
# To set the ChoiceField value in formset based on text-input
form1_cleaned_data = self.get_cleaned_data_for_step('text_input_step')
formset = form_class().forms
for form in formset:
if form1_cleaned_data:
form.fields['text_input'].choices = [item for item in form1_cleaned_data.items()]
# Print form to test if the form updates
print(form)
return formset
return super(FormWizardView, self).get_form_initial(step)
def done(self, form_list, **kwargs):
do something
return something
I'm trying to return the formset, but I get the error 'TestForm' object has no attribute 'get'. I am probably returning the wrong thing here, but whatever I try to return, it doesn't work. Returning super(FormWizardView, self).get_form_initial(step) just gives me the empty ChoiceField and returning the form gives me the error object of type 'TestForm' has no len().
I also printed out the form in my console, and that seems to work properly. Does anyone know what I should return in order to get the populated ChoiceField?
Many thanks!
EDIT:
Thanks for your answer! When I modify my get_form:
def get_form(self, step=None, data=None, files=None):
if step == 'formset_step':
form_class = self.form_list[step]
data = self.get_cleaned_data_for_step('slider_step')
if data is not None:
# To set the extra value in formset based on slider input
extra = data['number_slider']
form_class.extra = extra
# To set the ChoiceField value in formset based on text-input
form1_cleaned_data = self.get_cleaned_data_for_step('text_input_step')
formset = form_class().forms
for form in formset:
if form1_cleaned_data:
form.fields['text_input'].choices = [item for item in form1_cleaned_data.items()]
# Print form to test if the form updates
print(form)
return super(FormWizardView, self).get_form(step, data, files)
I get the error ['ManagementForm data is missing or has been tampered with']. When browsing through StackOverflow it seems a template problem (and specifically not setting {{ wizard.management_form }}, but I took the plain code from the Django FormTools doc which should normally work. In my template I have this:
{% extends "base.html" %}
{% load i18n %}
{% block head %}
{{ wizard.form.media }}
{% endblock %}
{% block content %}
<p>Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
<form action="" method="post">{% csrf_token %}
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{{ wizard.form }}
{% endif %}
</table>
{% if wizard.steps.prev %}
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.first }}">{%
˓→trans "first step" %}</button>
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">{%
˓→trans "prev step" %}</button>
{% endif %}
<input type="submit" value="{% trans "submit" %}"/>
</form>
{% endblock %}
Am I not seeing something in the template or my get_form function not correct? Many thanks for looking at my problem :)
Method get_form_initial from form wizard should return a dictionary with initial data for the form that will be created, not the form itself. If you want to modify whole form, try modifying get_form method instead.

How to update multiple objects at once in Django?

As the image presents I have to update multiple objects at once, e.g. to update the status of each object. If the status is active, the object will display on another page and vice versa. The goal is to change the status of one object (or all) with one button.
At the moment if I click on 'Update_all' I got only one value.
Django's Admin Page to take action on multiple objects would be a nice solution, but I have no idea, how this template is constructed although I considered the html code of the template.
Another try I attempted - similiar to the image above - was this one:
My template
<div class="container">
{% if forms.items %}
{% for key,value in forms.items %}
<form class="custom-form-manage-habis" method="post" action="">
{% csrf_token %}
<div class="container custom-div-manage-habits">
{{key}}
 
{{value}}
</div>
<hr>
{% endfor %}
{% else %}
<p>There are no habits to manage.</p>
{% endif %}
<input type="submit" value="Apply changes" {% if not forms.items
%}style="display:none"{% endif %} ></input>
</form>
</div>
...and in my view:
def post(self, request):
print('postFuction')
print(request.POST)
form_collection = {}
habit_update_list = request.POST.getlist('is_active')
print(habit_update_list)
habits = Habit.objects.filter(created_by=request.user.userprofile)
i = 0
for habit in habits:
print('I: ' + str(i))
form = HabitToManageForm(request.POST, instance=habit)
if form.is_valid():
habit = form.save(commit=False)
habit.is_active = habit_update_list[i]
print(habit)
habit.save()
else:
print('Error while check if form is valid')
i += 1
return redirect('manage_habits')
The problem here that I get indeed values of the objects, but unsorted and therefore it may be true that I get a value of object2, which will saved in object4.
So, is there a common practice or best way to handle this problem? Or may someone has any hints how I adopted the django admin template approach "select an object, then change it".
EDIT
Form:
class HabitToManageForm(forms.ModelForm):
class Meta:
model = Habit
fields = ('is_active',)

Django management form missing or tampered with - management form is in the template

I have a template with lots of forms on it, all wrapped in one form element. I have on MultiForm that is comprised of 4 regular forms, and two formsets. The formsets have been overridden to use custom formset classes.
I render the management forms in the templates, and can see the relevant info in the post.
For the formsets, I initialize the page with only one form visible.
When I try to submit the combined form I get the following error:
ManagementForm data is missing or has been tampered with
I have searched everywhere for the answer, and read about 15 posts on stack overflow with the same error, but none of the solutions seem to help.
The error page highlights the following line:
{{ beneficiaries.management_form }}
Template:
<form class='pension_form' id='implementation_form' action="{% url "confirmation_form" %}" method="post">
{% csrf_token %}
<ul>
{{ the_form.user_info.as_ul }}
</ul>
<ul>
{{ the_form.spouse_info.as_ul }}
</ul>
<div class='formset_container'> {{ children.management_form }}
{% for form in children %}
<div class='formset'><ul>{{ form.as_ul }} </ul><a class="glyphicon glyphicon-plus"></a></div>
{% endfor %}
</div>
<ul>
{{ the_form.employer_info.as_ul }}
</ul>
<ul>
<li>{{ the_form.beneficiary_general.WHO_BENEFITS }}</li>
</ul>
<div id='beneficiary_info_container' style='display:none;'>
<div class='formset_container'>
{{ beneficiaries.management_form }}
{% for form in beneficiaries %}
<div class='formset' >
<ul>{{ form.as_ul }}</ul><a class="glyphicon glyphicon-plus"></a></div>
{% endfor %}
</div>
<ul><li id='inheritance_order'>
{{ the_form.beneficiary_general.BENEFICIARIES_DIE.label_tag }}
{{ the_form.beneficiary_general.BENEFICIARIES_DIE }}
</li>
</ul>
</div>
<button class='btn btn-default main-btn'>{% trans "_Continue" %}
</form>
View:
def show_confirmation_form(request):
ChildFormSet = formset_factory(ChildInfo, formset=ChildInfoFormSet,
extra=14, can_delete=True)
BeneficiaryFormSet = formset_factory(BeneficiaryInfo, formset=BeneficiaryInfoFormSet,
extra=10, can_delete=True)
multi_form_prefix = 'main_form'
child_prefix = 'children_info'
beneficiary_prefix = 'beneficiary_info'
if request.method == 'POST':
form = ConfirmationForm(request.POST, prefix=multi_form_prefix)
children_forms = ChildFormSet(request.POST, prefix=child_prefix)
beneficary_forms = BeneficiaryFormSet(request.POST,
prefix=beneficiary_prefix)
if form.is_valid():
#not ready yet
return HttpResponseRedirect('/thanks/')
else:
form = ConfirmationForm(prefix=multi_form_prefix)
children_forms = ChildFormSet(prefix=child_prefix)
beneficary_forms = BeneficiaryFormSet(prefix=beneficiary_prefix)
context = {'the_form' : form, 'children' : children_forms,
'beneficiaries' : beneficary_forms}
return render(request, "confirmation_form.html", context)
Forms.py
class BeneficiaryInfo(forms.Form):
SHEM_PRATI_MUTAV = forms.CharField(label=_("First_Name"))
SHEM_MISHPACHA_MUTAV = forms.CharField(label=_("Last_Name"))
MISPAR_ZEHUT_MUTAV = forms.IntegerField(label=_("Mispar_Zehut"))
ACHUZ_HALUKA = forms.IntegerField(label=_("Percent_Allocate"))
class BeneficiaryInfoFormSet(BaseFormSet):
def clean(self):
"""
Adds validation to check that no two links have the same anchor or URL
and that all links have both an anchor and URL.
"""
if any(self.errors):
return
teudot_zehut = []
distribution_total = 0
for form in self.forms:
if form.cleaned_data:
teudat_zehut = form.cleaned_data['MISPAR_ZEHUT_MUTAV']
#allow empty forms.
if teudat_zehut:
if teudat_zehut in teudot_zehut:
form.add_error(None, 'No mutavim can share teudot_zehut')
distribution_total += int(form.cleaned_data['ACHUZ_HALUKA'])
if distribution_total != 100:
form.add_error(None, 'Distribution Total must be 100')
In case someone runs into a similar problem:
The problem was the I only showed the formsets if a certain checkbox was checked, and the management form was in the hidden area. I moved it out of the div that was hidden and it worked perfectly.

Getting user by primary key in flask

I'm working with flask and have a html page that contains of user name(user.html) which took from table, Now How can I see more detail by clicking on each of users(which route to profile)?
I don't use login for app So I don't want to use g
app.py
# am I doing it right?
#app.route('/profile/<int:id>')
def profile(id=None):
detail = Contacts.query.get(id)
return render_template('profile.html', detail= detail , id=id)
user.html
{% extends "layout.html" %}
{% block content %}
<h2>show user</h2>
{% for contact in contact %}
# I got error when I click on each user name to see their 'profile'
#I guess because of id How can Solve it?
#error BuildError: ('profile', {}, None)
<strong>name:</strong><a href={{url_for('profile')}}>
{{ contact.name}}</a><br>
{% endfor %}
{% endblock %}
profile.html
{% extends "layout.html" %}
{% block content %}
<h2>show user profile</h2>
# how can I make it specific for each row of table(each user)?
{% for detail in Contacts %}
<strong>name:</strong> {{ detail.name}} <br>
<strong>email:</strong> {{ detail.email }} <br>
<strong>age:</strong> {{ detail.age}} <br>
<br>
{% endfor %}
{% endblock %}
model.py
class Contacts(db.Model):
__tablename__ = "Contacts"
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(50))
email = db.Column(db.String(50))
age = db.Column(db.Integer)
submit = SubmitField("Submit")
I noticed two things in your code:
# this
<a href={{url_for('profile')}}>
# should be
<a href={{url_for('profile', id=contact.id)}}>
# otherwise Flask can't find the route, because it needs an id
And the other one:
{% for detail in Contacts %}
There is no such thing as a Contacts variable in your template, because your view function does not send it. Just get rid of the loop and use detail directly, because it's what you sent to the template.

Categories