Unable to grab posted form info using flask-wtf-forms - python

I'm trying to extend the flask-base project http://hack4impact.github.io/flask-base/templates/ . I have the following view functions:
#login_required
#main.route('/cities', methods=['GET', 'POST'])
def cities():
if request.method =='POST':
print(request.data)
redirect('/selected')
cities_list = []
recordset = Postings.query.all()
for r in recordset:
city = str(r.city)
elapsed_hours = round((time.time() - int(r.datetime)) / 3600, 1)
cities_list.append(str(r.city) + '-' + str(r.email) + '-' + str(elapsed_hours))
form = PostingSelectForm()
form.posting.choices = [(item, item) for item in cities_list]
return render_template("main/cities.html",
form = form)
#login_required
#main.route('/selected')
def selected():
return 'ddd'
I'm using a macro to generate a form as described in the docs above. The macro is called render_template and works normally to produce a form which I am able to submit. You can see the submitted info in the screenshot. However I am not able to capture the info in the lines:
print(request.data)
redirect('/selected')
and the redirect does not occur. When I look at the html of the wtf-form generated by the macro. it starts with;
<form action="" method="POST" enctype="" class="ui form
">
<div style="display:none;"><input id="csrf_token" name="csrf_token" type="hidden" value="1516882441##491001e55a57ceac4053b7937d21e61768f66d0b"></div>
So I'm wondering if the lack of an action is the problem here. I don't know what posting to "" does. Also if that is the problem how would I modify my form to include an action given that is generated by this macro. My forms code is:
class PostingSelectForm(Form):
posting = SelectField(
'posting',
choices=[('cpp', 'C++'), ('py', 'Python'), ('text', 'Plain Text')]
)
submit = SubmitField('Select')

Related

Why is django returning my HTML code when using a variable with the value retained from a filter?

I'm a beginner at learning Django and I have a website with a part that allows you to list your uncompleted . So far, I only want to display it and whenever I use a filter with this second variable ({"items": todos} that I want to be displayed in the template it returns the HTML skeleton.
*Everything works fine when I don't add this variable in the return statement. I have even tried printing the result from the filter and it works perfectly.
def dashboard(response):
print(response.user.username)
if response.method == 'POST':
form = CreateNewList(response.POST)
if form.is_valid():
n = form.cleaned_data['name']
t = ToDoList(name=n)
t.save()
response.user.todolist.add(t)
else:
form = CreateNewList()
todos = ToDoList.objects.filter(user=response.user)
print(todos)
return render(response, 'classy_main/dashboard.html',{"items": todos},{"form":form})
return render(response, 'classy_main/dashboard.html',{"form":form, "list": t})
This is how the html looks(it returns the whole page from the "" to the script tags at the bottom, but just to give you an idea):
<div class="news links nav-content">News</div>
<div class="more links nav-content">More</div>
<div class="notification">
<a href="#" class="notification">
<span><i class="fa fa-bell" style="font-size: 30px"></i></span>
<span class="badge"></span>
</a>
</div>
<div class="logOut">
is what I get back instead of the real page.
I finally fixed it, i just set the same able for both case so u could return the same thing for either case while still performing different tasks
def dashboard(response):
print(response.user.username)
if response.method == 'POST':
form = CreateNewList(response.POST)
if form.is_valid():
n = form.cleaned_data['name']
todos = ToDoList(name=n)
todos.save()
response.user.todolist.add(todos)
items = ToDoList.objects.filter(user=response.user)
else:
form = CreateNewList()
items = ToDoList.objects.filter(user=response.user)
print(items)
return render(response, 'classy_main/dashboard.html',{"form":form, "items": items})
i still dont know why it was returning the html skeleton page instead, anyways now i have a new problem when i refresh the page if there's some value(text) in the input field it adds it again to my todolist

Why does my Submit button renders a page that is blank when it is supposed to contain the data that was just updated?

I'm trying to update the values of my database using a HTML Form.
When I Click Edit it brings me to the edit the values above.
However as I am clicking the submit button, it returns me a database but with no other values.
Is there anyone that can help me understand what I did wrong and point me to the right documentation (if any)
editclaims.html:
<div class="arrange2">
<h1>Edit Claim Form - #{{claims.id}} </h1>
</div>
<form method="POST" action="/update/{{claims.id}}">
{% csrf_token %}
views.py:
def editclaims(request,id):
context = initialize_context(request)
user = context['user']
claims = SaveClaimForm.objects.get(id=id)
if request.method == 'POST':
name = request.POST['name']
email = request.POST['email']
claim = request.POST['claim']
claimtype = request.POST.get('claimtype')
description = request.POST['description']
receipt = request.FILES['receipt']
cheque = request.POST.get('Cheque')
form = SaveClaimForm(name=name, email=email, claim=claim, claimtype=claimtype, description=description, receipt=receipt, cheque=cheque)
form.save()
return render(request, "Login/editclaims.html", {'claims':claims, 'user':user})
urls.py:
urlpatterns = [
path('existingclaims/', views.viewclaims, name='existingclaims'),
path('editclaims/<int:id>', views.editclaims, name='editclaims'),
path('update/<int:id>', views.updateclaims, name='updateclaims'),
]
It may not resolve all your problems but it will be more readable as answer.
When you get data from HTML then you create new object SaveClaimForm and it will have new ID and you will have the same object in two rows.
You have to get original Claim from database and update values in this object and save it - and then it will save it with original ID and you will have only one `object in database
def editclaims(request,id):
context = initialize_context(request)
user = context['user']
# get original object
claims = SaveClaimForm.objects.get(id=id)
if request.method == 'POST':
# update original object
claims.name = request.POST['name']
claims.email = request.POST['email']
claims.claim = request.POST['claim']
claims.claimtype = request.POST.get('claimtype')
claims.description = request.POST['description']
claims.receipt = request.FILES['receipt']
claims.cheque = request.POST.get('Cheque')
# save it with original `ID`
claims.save()
return render(request, "Login/editclaims.html", {'claims':claims, 'user':user})
BTW:
Django has special class ModelForm to create forms in HTML. It may also have methods to check if data in HTML are correct - ie. if fields are not empty, if email is correctly constructed (name#domain.com), if phone has only numbers, etc. So using ModelForm can be more useful then writing all manually in code.

Dynamic ChoiceField unable to be validated in form

I have a form that's being given a dictionary of selection, it populates it correctly but on form submit it is not valid. When attempting to print errors, non_field_errors there are just blanks. When I am redirected to the form, now the choice field is populated by one choice and the csrf token from previous submit.
I've tried assigning choices in different ways such as self.fields['calendar'] = forms.ChoiceField(choices=choice_list) directly assign in a different way. self.fields['calendar'].choices = choice_list, a custom validator that ignores the validation, and inline debugging.
Form model:
class CalendarSelectionForm(forms.Form):
calendar = forms.ChoiceField(label="Calendar")
def __init__(self, calendars=None, *args, **kwargs):
super(CalendarSelectionForm, self).__init__(*args, **kwargs)
choice_list = [(calendar_id, calendar_name) for calendar_id, calendar_name in calendars.items()]
if calendars:
self.fields['calendar'].choices = choice_list
View:
if request.method == "POST":
print(request.POST)
cal_sync_form = CalendarSelectionForm(request.POST)
print("Non-field errors " + str(cal_sync_form.non_field_errors()))
print("Reg form errors " + str(cal_sync_form.errors))
# print("Field val " + str(cal_sync_form.calendar))
print("Field data " + str(cal_sync_form.data))
print("Field fields " + str(cal_sync_form.fields) + " Form is " + str(cal_sync_form.is_valid()))
if cal_sync_form.is_valid():
data = cal_sync_form.cleaned_data
print(data)
return render(request, 'management/gcal_sync_dashboard.html')
else:
return render(request, 'management/acct_select.html', {'form': cal_sync_form})
Form template:
<form class="form-import" action="/manage/gcal/sync/" method="post" id = "">
{% csrf_token %}
{{ form.calendar }}
{{ form.errors }}
{{ form.non_field_errors }}
<div class="push clearfix"></div>
<div class="col-sm-6 no-pad push"><input class="btn btn-brand btn-little button filson push-half" type="submit" value="Select email"><i class="fa fa-plus"></i></input>
</div>
</form>
The goal is to validate a posted form, the current print statements print out
<QueryDict: {'csrfmiddlewaretoken': ['sJHE8JJAzmeS0nRjaYZg5KdMlevJiInYY0G4YFJeITH1cVjciIdR1Dq1N28loUIL'], 'calendar': ['email#email.io']}>
Non-field errors
Reg form errors
Field data {}
Field fields OrderedDict([('calendar', <django.forms.fields.ChoiceField object at 0x117323080>)]) Form is False
In your view, you make a call to the CalendarSelectionForm constructor with request.POST as first positional argument. So that means that you call the __init__ function, and request.POST is passed as the calendars parameter.
You can fix this by constructing your form with named parameters. You furthermore will need to pass the same parameter to calendars as you did when you rendered the form with the GET request, since otherwise the choices do not per se match, and the user might have picked an option that is in that case not available during the POST request. Like:
if request.method == 'POST':
cal_sync_form = CalendarSelectionForm(calendars=my_calendars, data=request.POST)
# ...
with my_calendars the same value you pass when you constructed the form in the GET case.

Flask render_template clears all other forms

I have multiple forms with one submit button for each of them, all in one template. render_template returns only empty StringFields. How can I render without touching my other StringFields?
#app.route('/', methods=['GET','POST'])
def index():
msform = msForm(request.form)
synform = synForm(request.form)
if request.method == 'POST' and msform.validate() and msform.ms_submit.data:
processed_text = model.most_similar(positive=[msform.ms_3.data, msform.ms_2.data], negative=[msform.ms_1.data])
msform.ms_submit.label.text = processed_text[0][0]
return render_template('my-form.html', msform=msform, synform=synform)
elif request.method == 'POST' and synform.validate() and synform.syn_submit:
processed_text = model.most_similar([synform.syn_1.data])
return render_template('my-form.html', msform=msform, synform=synform)
return render_template('my-form.html', msform=msform, synform=synform)
class msForm(Form):
ms_1 = StringField(label='Eingabe_1', default = 'king', validators=[validators.DataRequired(message='This Field is required')])
ms_2 = StringField(label='Eingabe_2', default = 'man', validators=[validators.DataRequired(message='This Field is required')])
ms_3 = StringField(label='Eingabe_3', default = 'queen', validators=[validators.DataRequired(message='This Field is required')])
ms_submit = InlineSubmitField(label = '?')
class synForm(Form):
syn_1 = StringField(label='Eingabe', default = 'king', validators=[validators.DataRequired()])
syn_submit = InlineSubmitField('?')
I am assuming you have two separate <form>s on your HTML page, like this for brevity:
<form>{{ msform }}</form>
<form>{{ synform }}</form>
I believe you are saying this: upon completing and submitting one form, the values are lost from the other. Is that correct?
If so, that's the expected behavior, not from Flask, but from your browser. When you submit an HTML <form>, only the data from that form tag is sent to the server. Therefor, any data in other <form> tags is lost.
To submit all forms, render all the forms in the same <form> tag. Give each form a prefix to ensure the input names don't collide.
msform = msForm(request.form, prefix='ms')
<form method="post">
{{ msform }}
{{ synform }}
</form>

Django unable to read variable specified in form action

Can anyone help me with the below? I'm reading 'how to tango with django' project and stuck here.
I want a html form to revert back to a view function(add_page).
<h1>Add a Page</h1>
{% if category %}
<form id="category_form" method="post" action="/rango/category/{{category.slug}}/add_page/">
{% endif %}
This is my url map
urlpatterns = patterns('',
url(r'^$',views.index, name = 'index'),
url(r'^about/$',views.about, name = 'about'),
url(r'^category/(?P<category_name_slug>[\w\-]+)/$',views.category,name = 'category'),
url(r'^add_category/$', views.add_category, name = 'add_category'),
url(r'^category/(?P<category_name_slug>[\w\-]+)/add_page/$',views.add_page, name ='add_page'),
)
and my veiws.add_page function
def add_page(request,category_name_slug):
try:
cat = Category.objects.get(slug = category_name_slug)
except :
cat = None
if request.method == 'POST':
form = PageForm(request.POST)
if form.is_valid():
if cat:
page = form.save(commit = False)
page.category = cat
page.views = 0
page.save()
return redirect('/rango')
else:
print (form.errors)
else:
form = PageForm()
context_dict = {'form':form,'category':cat}
return render(request, 'rango/add_page.html',context_dict)
the submit button doesnt seem to be taking to post data to the requested url. Please let me know where i am going wrong?
If you want to redirect to the same page
you should probably call
return redirect('add_page', category_name_slug=page.category)
or maybe use something like
return redirect(request.build_absolute_uri())
in the POST section of your code.
request methods
redirect() options
EDIT: To clarify the 'return redirect('rango/')' is not pointing to anything in the url mapping in the question.

Categories

Resources