Update sql database using variables from Python Flask HTML list - python

I am trying to create a Website form (using Python Flask and WTForms and pyodbc for my database connection). I have a sql server database that is displaying the records to update in the html.
My goal is to update the "Unit price" and "discount" for each record in the database. The form I created requires an input in each of these fields from the user. Because that database data is displayed in a list, I'm not sure how to match up the data back to the database.
When I submit the form as is, only the first record is inserted into the database and This is what the form looks in my testing environment (please forgive the horrible formatting). The first column is for the "unit price" and the second is for the "discount" the only record inserted into the database is the first one and the element and element name are not mapped properly.
Is there a way to reference the columns in "elementdata"? (ex: elementdata.UniqueKey)
How can I create the connection between the data displayed in the html to the data inserted to the database?
EDIT: I watched a few of Corey Schafer's video's on youtube and found them helpful but they haven't quite solved my problem.(https://www.youtube.com/channel/UCCezIgC97PvUuR4_gbFUs5g)
I'm currently heading into the direction of creating my own list and then calling that list in the HTML. So instead of
elementdata = cur.execute("SELECT UniqueKey, Element, ElementName, Quantity FROM FakePricingData WHERE ClientName=clientname").fetchall()
I tried:
uniquekey = cur.execute("SELECT UniqueKey FROM FakePricingData WHERE ClientName=clientname").fetchall()
element = cur.execute("SELECT Element FROM FakePricingData WHERE ClientName=clientname").fetchall()
elementname = cur.execute("SELECT ElementName FROM FakePricingData WHERE ClientName=clientname").fetchall()
elementdata = [uniquekey, element, elementname]
However, when referencing elementdata[0], insead of getting just the uniquekey, I get the entire first row.
How can I get what I'm looking for? I would appreciate any help I can get!
#app.route("/VendorPricing/Example", methods=['GET', 'POST'])
#login_required
def vendorpricing():
clientname = "Example Credit Union"
elementdata = cur.execute("SELECT UniqueKey, Element, ElementName, Quantity FROM FakePricingData WHERE ClientName=clientname").fetchall()
form = VendorPricingForm()
if form.validate_on_submit():
for elementdata in elementdata:
loggedinuserid = current_user.get_id()
query = "INSERT INTO FakeQuoteData (UniqueKey, Element, ElementName, Quantity, UnitPrice, Discount, ClientName, SubmittedBy, LoggedInUserId) VALUES (?,?,?,?,?,?,?,?,?);"
query_values = [form.uniquekey.data, form.element.Element, 'NULL', '0', form.unitprice.data, form.discount.data, clientname, form.submittedby.data, loggedinuserid]
cur.execute(query, query_values)
cur.commit()
flash('Your pricing has been submitted!', 'success')
return redirect(url_for('welcome'))
return render_template('vendorpricing.html', form=form, clientname=clientname, elementdata=elementdata)
------ form ------
class VendorPricingForm(FlaskForm):
unitprice = IntegerField('Unit Price', validators=[DataRequired()])
discount = IntegerField('Discount')
uniquekey = StringField('Unique Key')
submittedby = StringField('Submitted By', validators=[DataRequired()])
submit = SubmitField('Submit')
{% extends "layout.html" %}
{% block content %}
<h2>{{ title }}</h2>
<body>
<form method="POST" action="">
{{ form.hidden_tag() }}
<div class="vendor-form-group">
{{ form.unitprice.label(class="form-control-label") }}
{{ form.discount.label(class="form-control-label") }}
{% for elementdata in elementdata %}
<p>
{{ elementdata }}
{{ form.unitprice }}
{{ form.discount }}
</p>
{% endfor %}
{{ form.submittedby.label(class="form-control-label") }}
<p>{{ form.submittedby }}</p>
{{ form.submit }}
</div>
</form>
</body>
{% endblock content %}

You can try InputRequired() instead of DataRequired()

Related

How do I Filter a viewlist based on a selection made on a createview form? Django

I am learning how to use Python & Django. I don't have much experience, and this has been a huge learning process for me. Please forgive any ignorance in my question.
I am designing a Django app and have a few relational tables created. My question involves only my first 2 models/forms/views.
Here are my models
`
class CustomerName(models.Model):
#Fields
company = models.CharField(max_length = 60, help_text = "Enter Customer Name",unique=True)
def __str__(self):
return self.company
class ProgramName(models.Model):
#Fields
customer = models.ForeignKey("CustomerName",on_delete=models.CASCADE)
program = models.CharField(max_length = 60, help_text = "Enter Program Name")
def __str__(self):
return self.program
`
Here are my Views (Ive left out the customer add view since that works and I don't think its relevant)
`
class ProgramAddView(CreateView, ListView):
template_name = 'app/ProgramAdd.html'
form_class = Programform
model = ProgramName
success_url = reverse_lazy('Part Number Add')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["qs_json"] = json.dumps(list(ProgramName.objects.values()))
return context
`
Here is the form
`
class Programform(forms.ModelForm):
class Meta:
model = ProgramName
fields = "all"
class Customerform(forms.ModelForm):
class Meta:
model = CustomerName
fields = ('company',)
`
Here is the HTML app/ProgramAdd.html
`
{% extends 'base.html' %}
{% block content %}
<h2>Program</h2>
<form method="post" class = 'formarea'>
<div id = 'formarea' class = 'formdiv'>
{{ form.media.js }}
<table>
{{ form.as_table }}
</table>
{% csrf_token %}
<button type="submit">Save</button>
Nevermind
</div>
</form>
<h2>Program List</h2>
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Program</th>
<th scope="col">Customer</th>
</tr>
</thead>
</table>
<ul>
<div id="box">
<!-- Iterate over object_list -->
{% for item in object_list %}
<!-- Display Objects -->
{{ item }}
</div>
<hr/>
<!-- If object_list is empty -->
{% empty %}
<li>No objects yet.</li>
{% endfor %}
</ul>
{% endblock %}
{% block scripts %}
<script>
const data = '{{qs_json}}'
console.log(data)
const rdata = JSON.parse(data.replace(/"/g, '"'))
console.log(rdata)
console.log("WHAT!?")
const input = document.getElementById('id_customer')
console.log(input.selected)
let filteredArr = []
input.addEventListener('click', (e)=>{
console.log(input.value)
box.innerHTML = ""
filteredArr = rdata.filter(programname=> programname['program'].includes(e.target.value))
console.log(filteredArr)
if (filteredArr.length > 0){
filteredArr.map(programname=>{
box.innerHTML += `<b>${programname['program']}</b><br>`
box.innerHTML += `<b>${programname['customer_id']}</b><br>`
})
} else {
box.innerHTML = "<b>No results found...</b>"
}
})
</script>
{% endblock scripts %}
`
Here is how the page looks
Now ideally, you would be filling out the form to add a program to the customer in the database. I wanted to display existing Customers + programs already in the database down below the form.
When I make a choice though, this always happens
enter image description here
There are 2 questions/issues I have so far that I've yet to find a solution for.
Question A. I need to filter my list by Customer name instead of program name but any time I reference the customer column (Example down below) I am told I cant use .includes as its not an array?
*This returns the following error: programname.customer_id.includes is not a function
filteredArr = rdata.filter(programname=> programname['customer_id'].includes(e.target.value))
This confuses me as console.log(rdata) returns this table
0
:
{id: 1, customer_id: 1, program: 'Program A'}
1
:
{id: 2, customer_id: 2, program: 'Program A'}
2
:
{id: 3, customer_id: 2, program: 'Program B'}
which I would assume means I can filter by customer_id instead?
I also ran console.log(input.value) and it returns either 1 or 2 or 3 based on my customer selection? so that would mean the filter should be basing its test on the value of the proper drop down box.
Question B:
My second question, how do I display customer as its name rather than ID number?
I appreciate any feedback or guidance. Thank you for your time.
For question A:
I tried swapping out
filteredArr = rdata.filter(programname=> programname['program'].includes(e.target.value))
to both
filteredArr = rdata.filter(programname=> programname['customer_id'].includes(e.target.value))
as well as
filteredArr = rdata.filter(customername=> customername['company'].includes(e.target.value))
But I either get undefined or programname.customer_id.includes is not a function
I have also extensively googled into the issue and could not find anything that did not involve my specific request of a create view + a list view working together.
For question B:
Instead of
`
{% for item in object_list %}
<!-- Display Objects -->
{{ item }}
I tried using
{% for object in object_list %}
<!-- Display Objects -->
{{ object.company }}
{{ object.program }}
`
Which seems to show the desired result but this does not work with the filter at all. So I cannot proceed with this option.
After messing around with it longer, I was able to achieve near my goal using
filteredArr = rdata.filter(programname=> programname.customer_id == input.value)

Django 3 - Passed variable in views.py not accessible in web form like other vars

Newbie to Django here - I have a list of DB entries that I want to be able to cycle through one at a time and currently I am working on loading the first object by its ID. The form renders fine, but other vars aren't accessible as expected - am I missing something in the code?
views.py
def ticket_edit(request):
updateform = TicketForm(request.POST)
if request.method == 'POST':
if updateform.is_valid():
ticket = Ticket.objects.update(
ticketID=updateform.cleaned_data['ticketID']
)
ticket.save()
return HttpResponseRedirect(".")
else:
print("FORM NOT YET VALID: Awaiting form.is_valid()")
updateform = TicketForm()
#--Get list of ticketIDs---------------
def ticketID_listGet():
tickets = Ticket.objects.all()
ticketIDList = []
for ticket in tickets:
aTicketID = ticket.appTicketID
ticketIDList.append(aTicketID)
return ticketIDList
tList = ticketID_listGet()
tFirst = str(tList[0])
print('TicketIDs: ', tList, tFirst)
return render(request,'authentication/ticket-edit.html',{
"has_error": False,
"updateform": updateform,
"tFirst": tFirst
})
ticket-edit.html
<div class="container" style="border-style:none">
<form action="." method="POST" id="ticket-update-form" style="border-style:none">
{% csrf_token %}
<div class="row">
<div class="col-md-12">
<div class="form-control" style="border-style:none">
{{ updateform.ticketID.label_tag }}
{{ updateform.ticketID }}
{{ tFirst }}
</div>
</div>
</div>
I am not getting errors when using {{ tFirst }}, it's just being ignored as if unrecognized. Ultimately I am trying to determine how to retrieve the existing ticketIDs and the first ticketID as vars and then cycle through them with buttons that set the value in the web form's {{ updateform.ticketID }) widget - am I making this more difficult than necessary?

Unable to update record with Flask, MySQL

I'm working on a music database app with Flask, and I have a page where I can insert a record into the database that works how it should. Yesterday, I built a page where you can edit the values of the record. For the route, I copied the code from another, more simple app I made and re-wrote it for this app. When I visit the edit page, it fills the text boxes with the current values for the record...but when I change any of the items and submit it, nothing happens. It renders the page that I specified in the route after submit, but when I query the table nothing has changed.
Here's the route:
#app.route('/edit_album/<string:catno>/', methods=['GET', 'POST'])
def edit_album(catno):
cur = mysql.connection.cursor()
# Get article by catno
result = cur.execute("SELECT * FROM albums WHERE catno = %s", [catno])
album = cur.fetchone()
form = AlbumForm()
form.artist.data = album['artist']
form.title.data = album['title']
form.year.data = album['year']
form.rlabel.data = album['label']
form.genre.data = album['genre']
if request.method == 'POST':
# album art
#cover =
catno = album['catno']
artist = form.artist.data
title = form.title.data
year = form.year.data
rlabel = form.rlabel.data
genre = form.genre.data
# format (lp or tape)
# Create Cursor
cur = mysql.connection.cursor()
# Execute cursor
cur.execute("UPDATE albums SET artist=%s, title=%s, year=%s, label=%s, genre=%s WHERE catno=%s", (artist, title, year, rlabel, genre, catno))
# Commit to DB
mysql.connection.commit()
# Close DB connection
cur.close()
return redirect(url_for('view_album', catno=catno))
return render_template('edit_album.html', album=album, form=form)
And here's the actual edit page:
{% extends 'layout.html' %}
{% block body %}
<div class="container">
<div class="col-md-12 text-center border mt-3">
<h1 class="text-white">{{album.artist}} :: {{album.title}}</h1>
</div>
<div class="row mt-3">
<div class="col-sm-4 col-md-4 text-center">
{% if album.albumArt == None %}
<img src="/static/album_art/not_available.png" height="300" width="300">
<a class="btn btn-primary mt-3">Upload Cover</a>
{% endif %}
</div>
<div class="col-sm-8 col-md-8">
<table class="table table-light table-striped">
<tr>
<td>Artist: {{album.artist}}</td>
</tr>
<tr>
<td>Album: {{album.title}}</td>
</tr>
<tr>
<td>Catalog No: {{album.catno}}</td>
</tr>
<tr>
<td>Record Label: {{album.label}}</td>
</tr>
<tr>
<td>Year Released: {{album.year}}</td>
</tr>
<tr>
<td>Genre: {{album.genre}}</td>
</tr>
</table>
</div>
</div>
<div class="card text-center mt-3">
<div class="card-header text-center bg-primary">
<p>EDIT ALBUM</p>
</div>
<form method="POST" action="{{ url_for('edit_album', catno=album.catno) }}" class="card-footer text-center">
<div class="row">
{{ form.csrf_token}}
<div class="col-sm-4">
{{ form.artist.label }}<br>
{{ form.artist }}<br>
</div>
<div class="col-sm-4">
{{ form.title.label }}<br>
{{ form.title }}<br>
</div>
<div class="col-sm-4">
{{ form.year.label }}<br>
{{ form.year }}<br>
</div>
</div>
<div class="row">
<div class="col-sm-4">
{{ form.rlabel.label }}<br>
{{ form.rlabel }}<br>
</div>
<div class="col-sm-4">
{{ form.genre.label }}<br>
{{ form.genre }}
</div>
</div>
<p><input class="btn btn-primary mt-3" type="submit" value="Submit">
</form>
</div>
</div>
{% endblock %}
The only thing I really got from searching last night, is that I may have two connections to the DB open, but I don't since I just have the one connection at the beginning of the script. It wouldn't be an issue with too many cursors, would it?
Otherwise, this is the first app I've used the Flask-WTF module for the forms, so could it be something wrong I'm doing with that? Here's that class if there's any questions:
# Form for adding record to database
class AlbumForm(FlaskForm):
# Album Art - figure out image uploads
cover = FileField('Upload Cover Art')
catno = StringField('Catalog Number')
artist = StringField('Artist')
title = StringField("Album Title")
year = StringField('Year Released')
rlabel = StringField('Record Label')
genre = StringField('Genre')
The app doesn't throw any errors, so I'm not sure what's going on, or if I'm just overlooking something.
Seems like you are overwriting your form on post, because on both get and post you are fetching an album entry and filing a form with it's data. It should work if you structure it like this:
def edit_album(catno):
cur = mysql.connection.cursor()
if request.method == 'POST':
form = AlbumForm() # WTForms will extract data from the request's body by itself
# other code from a if request.method == 'POST' block
elif request.method == 'GET':
cur.execute("...")
album = cur.fetchone()
form = AlbumForm()
form.artist.data = album['artist']
form.title.data = album['title']
form.year.data = album['year']
form.rlabel.data = album['label']
form.genre.data = album['genre']
return render_template...
P.S. A good but too high-level and magic-y (and hard to understand as a result) example is given in the WTForms docs: wtform has formdata and obj arguments; each time wtform instance is created (form = AlbumForm) it tries to extract data from a request to populate it's fields. If it fails (and it would on a get-request because no form-data exist) it will get data from a second source -- the obj argument, which has your current db-entry value. But on post wtform successfully retrieves data from a post-request-formdata which then populates db-entry which is then saved.
The solution was this:
The value of the variables that are used to update the database were supposed to be like this:
catno = album['catno']
artist = request.form['artist']
title = request.form['title']
year = request.form['year']
rlabel = request.form['rlabel']
genre = request.form['genre']
And not:
catno = album['catno']
artist = form.artist.data
title = form.title.data
year = form.year.data
rlabel = form.rlabel.data
genre = form.genre.data
Because in the latter method, I was just passing the same data into the variables that was loaded into the form when the page was opened, instead of the updated values in the text boxes.

append_entry() is not working after appending first time. I am using Fieldlist with flask-wtf, jinja & Python 3.4

I am facing issue while appending fieldlist through append_entry in flask-wtf.
I am able to add one row of fields from fieldlist but after adding first row, i am not able to add second row.
No errors are shown.
In first attempt a new row is added of field list items and in server log "Data is King" and "True" is printed (I have included this to see if add button sends data and loop actually goes through).
In second attemt new row is not added but page reloads and in server log "Data is King" and "True" is printed (This means loop is executed but append_entry command does not work).
I found same question asked 4 years ago, but solution given in that question's accepted answer does not solve the problem.
Below is the link.
https://stackoverflow.com/questions/8637831/cant-append-entry-fieldlist-in-flask-wtf-more-than-one#=
I have tried all the combinations but nothing solves the problem (am upto this problem for more than 48hrs now).
Can someone help please.
form.py Data
class FormCont(FlaskForm):
description = StringField('Description',validators=[DataRequired()])
start_time = FloatField('Start Time',validators=[DataRequired()])
class AddItemCont(FlaskForm):
a = FloatField(validators=[DataRequired()])
b = FloatField(validators=[DataRequired()])
c = FloatField(validators=[DataRequired()])
item_cont = FieldList(FormField(FormCont), min_entries=0, max_entries=100)
add_item_cont = SubmitField("Add")
remove_item_cont = SubmitField("Remove")
submit = SubmitField()
view.py data
#additem.route('/additem/', methods = ['GET','POST'])
def additemcont():
form = AddItemCont(request.form)
if form.add_item_cont.data:
form.item_cont.append_entry()
print ("Data is King")
print (form.add_item_cont.data)
return render_template('entry.html',form=form)
entry.html data
<form action="{{url_for('additemcont')}}" method='POST' name='AddItemCont' class="form-horizontal">
{{ form.hidden_tag() }}
<div class = "row">
<div class="col-md-1" >
<form action="" method="POST" class="form-horizontal"><button type="submit" class="btn btn-success btn-xs" name="add_item_cont" value="add_item_cont"><span class="glyphicon glyphicon-plus" aria-hidden="true"></button>
</form>
</div>
</div>
<div class = "row"><div class="col-md-12" >
{% for x in form.item_cont %}
{% for i in x %}
{{ i (class="form-control") }}
{% endfor %}
{% endfor %}
</div></div>
Have you tried initializing the form and passing it to append_entry?
#additem.route('/additem/', methods = ['GET','POST'])
def additemcont():
form = AddItemCont(request.form)
if form.add_item_cont.data:
### here
form_cont = FormCont()
form.item_cont.append_entry(form_cont)
####
print ("Data is King")
print (form.add_item_cont.data)
return render_template('entry.html',form=form)

SelectMultipleField default value is not being selected on HTML

c.ingredientsI am creating a Flask sample app for learning purposes and I have a Form called CocktailForm that contains a SelectMultipleField. This form should be used for creating new cocktails objects as well as for updating them:
class CocktailForm(Form):
name = StringField('What is the coktail\'s name?', validators=[Required()])
ingredients = SelectMultipleField('Ingredients',
coerce=int,
)
submit = SubmitField('Submit')
For editing I want to load the same form with the cocktail data. It works for name, and for loading all choices but I also want to select those choices that are ingredients of that cocktail:
#main.route('/edit/<int:id>', methods=['GET', 'POST'])
def edit(id):
try:
c = Cocktail.query.filter(Cocktail.id == id).one()
form = CocktailForm()
form.ingredients.choices = [(i.id, i.name) for i in Ingredient.query.all()]
if form.validate_on_submit():
c.name = form.name.data
cocktail_ingredients = Ingredient.query.filter(Ingredient.id.in_(form.ingredients.data)).all()
c.ingredients.extend(cocktail_ingredients)
db.session.commit()
else:
form.name.data = c.name
form.ingredients.default = [(i.id, i.name) for i in c.ingredients]
return render_template('new.html', form=form)
except Exception, e:
print e
return redirect(url_for('.index'))
I get unexpected results since on the HTML those choices are not displayed but when I submit the form it seems like those choices are always selected even if you select a new ones.
The template is quite simple:
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Cocktails{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>New Cocktail</h1>
{{ wtf.quick_form(form) }}
</div>
{% endblock %}
This is what you can see in in the browser. I am editing Gintonic cocktail which is composed of gin and tonic. However they are not displayed as selected:
Browser
Thanks in advance, any tip will be appreciated
The line form.ingredients.default = [(i.id, i.name) for i in Ingredient.query.all()] does not set the selected values. You want to change it to be form.ingredients.data = [i.id for i in c.ingredients].
You should have a redirect/render_template in the validate block, but that is optional.

Categories

Resources