WTForm used in two different views validate only using one view - python

I'm having a problem using a WTForm in two different views. One view creates a new item, using the form data. The other view display information for items already in the database and the user should be able to update the form data from there.
I can create and view the items in the database. The problem is updating the information when in in the display view. The steps I take are something like:
Create a item in /create. Before saving to database check that the same ID is not already in database. If exists show a flash and do not permit saving the item. Reloading /create
To display information about a existing item the route /display/<item> call the database, populate the form and show the information using the same form.
Desired behavior
To update information when in /display/<item>. After updating any field data and pressing the submit button, the form should save to the database and reload /display/<item>.
Actual behavior
After pressing the submit button the form takes me back to the /create route, and performs the ID validation.
I don't understand why, being in a different view /display/<item>, the form's submit button takes me to the /create view.
The code goes something like this:
This is a simple form
class ItemForm(FlaskForm):
id = IntegerField("id", validators=[DataRequired()])
name = StringField("Email", validators=[DataRequired()])
submit = SubmitField("Submit")
With this I create a new item in the database, checking that the same Id is not already in the database:
#app.route("/create", methods=["GET", "POST"])
def create():
form = ItemForm()
if form.validate_on_submit():
item = item = Item.query.filter(Item.id == form.id).first()
# Check the same id is not already created
if item is not None:
flash('item already in database')
return redirect(url_for("create"))
item = Item()
form.populate_obj(item)
db.session.add(item)
db.session.commit()
return redirect(url_for("display", item = form.id))
else:
return render_template("create.html", form=form)
And then after there is created items in the database i can call a display route. In this route I should be able to update the fields in the form and save it to the database. Whit out validating as the the same ID is already present in the database (I'm updating, no creating a new one).
#app.route("/display/<item>", methods=["GET", "POST"])
def display(item):
item = Item.query.filter(Item.id == item).first()
form = ItemForm(obj=item)
if form.validate_on_submit():
form.populate_obj(item)
db.session.add(item)
db.session.commit()
return redirect(url_for("display", item=form.id))
else:
return render_template("display.html", form=form)
The problem is that when in the /display/<item> route, the submit button takes me back to the /create route. Performing the validations in there. Of course the item is already created in the database so I get the flash and I'm redirected to /create, when it should reload /display/<item>.
Could someone point me in the right direction, please. Thank you all!

My mistake, but #the_gañañufla asked the right question. The problems of coding alone.
I had added an action to the HTML and i forgot to remove it.
I had
<form id="form_item" action="{{ url_for('create') }}" class="form" method=post>
After correct it I have:
<form id="form_product" action="" class="form" method=post>

Related

Django - Pass Object ID after redirect

Apologies if the question title is wrong but I am unsure of exactly what I need to do. I am building a scrum app where users can create user stories and add them to sprints. I want to have two separate ways of creating stories. If the user is creating a story from the backlog, the story that has been created will not be assigned to a sprint, but the user can also add a story from a sprint's detail screen. I want to make it so that if they add a story from a specific sprint's page, that story's sprint field is populated with that sprint.
The way I have tried to go about this is by creating two different functions in my views.py file and two separate templates. I am able to create the story without relating to a sprint as intended, but I cannot think of a way to get the id of the sprint where I navigated from to get to the new template.
For reference I have included my function for creating stories without relating to a sprint:
def create_story(response, id=None):
user = response.user
if response.method == "POST":
form = CreateNewUserStory(response.POST)
if form.is_valid():
name = form.cleaned_data["name"]
description = form.cleaned_data["description"]
status = form.cleaned_data["status"]
assignee = form.cleaned_data["assignee"]
estimate = form.cleaned_data["estimate"]
userstory = UserStory(name=name, description=description, status=status, assignee=assignee, estimate=estimate)
userstory.save()
response.user.userstories.add(userstory)
return HttpResponseRedirect("/backlog")
else:
form = CreateNewUserStory()
return render(response, "main/create_story.html", {"form": form})
If any more information or code is required please let me know, thank you!
You can define 2 URLs, one with an optional sprint ID:
urls.py:
path('story/<int:sprint_id>/add/', views.create_story, name='create_story_for_sprint'),
path('story/add/', views.create_story, name='create_story_for_backlog'),
views.py:
def create_story(request, sprint_id=None):
...
form = CreateNewUserStory(response.POST, initial={"sprint_id": sprint_id})
if form.is_valid():
...
sprint_id = form.cleaned_data["sprint_id"]
create_story.html:
<form action="..." method="POST">
<input type="hidden" name="sprint_id" value="{{ sprint_id|default_if_none:'' }}">
...
Then, in your sprint page, use
Add Story
and in your backlog page:
Add Story

Change a view varible file with the html file

I'm making an online store and I add to it the Stripe Checkout for payments, everything works correct, the problem is that the view function only manages one price. It looks like this:
def charge(request): # new
if request.method == 'POST':
charge = stripe.Charge.create(
amount=179900,
currency='mxn',
description='Bota Caballero',
source=request.POST['stripeToken']
)
return render(request, 'charge.html')
the amount says the price, In my html homepage I have a select tag, it defines the cost of the product
<select id="modelo" onchange="modelo()" class="select__option" required>
<option>Selecciona Modelo...</option>
<option>$1799</option>
<option>$1299</option>
<option>$1199</option>
</select>
I would like to change the amount variable of the view file depending on what is selected in the select tag. Any idea?
You would typically pass the value via your form and retrieve it with request.POST.get('amount', None) however..
You should never pass pricing from a client editable form.
As an example, if you POST a form with your selection to your view and then dynamically set that value to your amount, you set yourself up to some serious pricing issues.
The end user could simply modify the POST data and set whatever value they like.
I think the best solution here would be to:
Store the pricing you have set in your database
Pass the product identification or similar via POST
Retrieve pricing from database
Set dynamic pricing based on database response
I assume that your select tag is within a form. You can simply retrieve the value/option selected by your user and use it in your view function to set the Stripe charge.
<select id="modelo" name="modelo_price" onchange="modelo()" class="select__option" required>
<option>Selecciona Modelo...</option>
<option>$1799</option>
<option>$1299</option>
<option>$1199</option>
</select>
def charge(request): # new
if request.method == 'POST':
# Here you retrieve the value submitted by your user
charge_amount = int(request.form.get('modelo_price')) * 100
charge = stripe.Charge.create(
amount=charge_amount,
currency='mxn',
description='Bota Caballero',
source=request.POST['stripeToken']
)
return render(request, 'charge.html')

Multiple Forms on 1 Page Python FLASK

So I am loading all users from a database on a single page and I'm generating a password reset form for each user on the same page rather than having an individual page for each user.
My question is how can I click submit and apply the change for that specific user since I have multiple forms and submit buttons for each user via drop-down menu?
In my case the submit button is the "Reset Password" button.
I'm trying to call the form normally using
if request.method == "POST" and form.validate():
password = request.form['password']
but I'm getting exception error
name 'form' is not defined
I've been trying to solve this for a while but i'm getting pretty confused now as I've got multiple forms (one per user) on the same page.
NOTE : I'm not using WTForms for this task
Thanks
I'm not sure if this one has been answered but here is what i just figured out:
After your standard
if request.method == 'POST':
you can test for the existence of each form item within the request.form data. So add another if statement after the first
if 'my_form_element_name' in request.form:
print ('stuff')
If you have other types of form data such as files, you can do something like:
if request.method == 'POST':
if 'file_element_name' in request.files:
return stuff
elif 'my_form_element_name' in request.form:
return stuff
else: return stuff
else: return stuff
I have four forms in one html file and this method worked for me.

What's the best way to link views and pass data between them?

I have a basic diary application that has two views: Add and Edit view. When user hits 'Add' button on Add view, the data is saved into database and simple message is shown. When user hits 'Update' button on Edit View, the data is updated and a last update stamp is added.
So far these views run independently. The Edit View by default loads the last saved entry and allows for that to be updated.
I want to update it so that upon successful addition of new diary entry on the Add View, it transitions to the Edit View to allow the user to Edit and Update if they want to. How do I link the views together and pass the relevant data to Edit View to know which Entry (database object) to fetch for edit? Also I would like to be able to use the Edit View independently to fetch a specified diary entry for example on a GET. So the Edit View is agnostic to whomever called it, it just knows which diary entry to load.
The code for Add and Edit View as they are now, are displayed below:
def addEntry(request):
entryForm = None
if request.method == "POST":
entryForm = EntryForm(request.POST)
if entryForm.is_valid():
entryForm.save(commit = True)
request.method = "GET"
return entrySubmitted("Entry has been submitted")
else:
logger.info(entryForm.errors)
else:
# Set up view with new entry form
entryForm = EntryForm()
template = getEntryViewTemplate(entryForm)
return render(request, "DiaryEntry.html", template)
def editEntry(request):
# Get the latest entry from the database
entry = Entry.objects.last();
if request.method == 'GET':
entryForm = EntryForm(instance = entry)
else:
entryForm = EntryForm(request.POST, instance = entry)
if entryForm.is_valid():
entryForm.setLastUpdated(datetime.now())
entryForm.save(commit = True)
templateData = getEntryViewTemplate(entryForm)
return render(request, "EditEntry.html", templateData)
Thanks in Advance,
Francis
Look django documentation about Class Base View.
https://docs.djangoproject.com/en/1.10/topics/class-based-views/intro/#
You can create a class, with different method.
Get, Post(create object), Put(Update object)
And you have a lot of methods and attributes usefull.
You can link your class view to a specific model (Entry) for example.
https://docs.djangoproject.com/fr/1.10/ref/class-based-views/mixins-single-object/#django.views.generic.detail.SingleObjectMixin.get_queryset
How do I link the views together and pass the relevant data to Edit View to know which Entry (database object) to fetch for edit?
You need to pass ID Object to edit. Two solutions. In your Post data. Or by argument in your url.
http://your_web_site.com/entry/id_entry/
And you init your form with entry data
In your URL django you can link to url to your class base view.
url(r'^entry/$', views.EntryView.as_view(), name='create_entry'),
url(r'^entry/(?P<pk>[-\w/]+)$', views.EntryView.as_view(), name='update_entry'),
I hope this will help you

How to route form errors to another view using Flask, FlaskWTF

I'm trying to create two views using Flask. The first view show_entries displays a list of entries in a table. It also includes a form to create new entries.
The form gets submitted to a second view new_entry which accepts the POST method and is responsible for adding the new entry to the table using SQLAlchemy. new_entry then redirects back to show_entries.
My problem is that form.errors are not routed to show_entries, so the user never sees them. I'm not sure of the best way to go about this, or if I'm even on the right track with the way I've divided up the views.
Here's what I currently have:
def show_entries():
entryForm = EntryForm()
entries = g.user.entries
return render_template('show_entries.html',
entries=entries,
entryForm=entryForm)
def new_entry():
form = EntryForm()
if form.validate_on_submit():
newEntry = Entry(g.user, form.time.data)
db_session.add(newEntry)
db_session.commit()
flash('New entry was succesfully posted')
return redirect(url_for('show_entries'))
The normal pattern is to have /show_entries as a listing page with new_entry as the form. When you do a GET request to new_entry you get the form, then POST to it to add the entry. That way if there's an error you can just show it next to the form - all the data is available. If you split the views as you have then you'll need some way of moving the error data (and form data) from the new_entry view to the show_entries view.
Something more like (untested):
def show_entries():
entries = g.user.entries
return render_template('show_entries.html',
entries=entries)
def new_entry():
form = EntryForm()
if form.validate_on_submit():
newEntry = Entry(g.user, form.time.data)
db_session.add(newEntry)
db_session.commit()
flash('New entry was successfully posted')
return redirect(url_for('show_entries'))
return render_template('show_new_entry_form.html',
entryForm=form)

Categories