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.
Related
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?
I have implemented a Dependent dropdown list within Django but when I try to submit the form I get the following error 'Select a valid choice. That choice is not one of the available choices.'
I have spent a while looking on the web for the answer and have tried a few with little avail.
From my understanding and reading, this is an error because I render the form with a queryset of none. Then I use ajax to fill in the options. Even though I have updated the dropdown list, the form validation is checking my submitted answer against a queryset of none - thus the error.
So i'm hoping someone can help me to update the choices the form will accepted on form submission.
views.py
# stage6 is where I render my view and check validation
def stage6(request):
form_deal = DealForm(request.POST or None, prefix='inst')
if form_deal.is_valid():
form_deal.save()
messages.success(request, 'Deal added successfully.')
form_deal = DealForm()
context = {
'dform': form_deal,
}
return render(request, 'stages/stage6/stage6.html', context)
# This is used for my ajax request
def load_offers(request):
property_process_id = request.GET.get('propertyprocess_link')
offers = Offer.objects.filter(propertyprocess_link=property_process_id).order_by('id')
return render(request, 'stages/stage6/offers_dropdown_list.html', {'offers': offers})
forms.py
class DealForm(forms.ModelForm):
deal_date = forms.CharField(
label='',
widget=forms.TextInput(attrs={'type': 'date'})
)
target_move_date = forms.CharField(
label='',
widget=forms.TextInput(attrs={'type': 'date'})
)
def __init__(self, *args, **kwargs):
super(DealForm, self).__init__(*args, **kwargs)
# filter the foreign keys shown
self.fields['propertyprocess_link'].queryset = PropertyProcess.objects.filter(sector="Sales")
# filter used for ajax request
self.fields['offer_accepted'].queryset = Offer.objects.none()
# add a "form-control" class to each form input
# for enabling bootstrap
for name in self.fields.keys():
self.fields[name].widget.attrs.update({
'class': 'form-control',
})
class Meta:
model = Deal
fields = ('propertyprocess_link',
'deal_date',
'price_agreed',
'target_move_date',
'offer_accepted'
)
models.py
class Deal(models.Model):
propertyprocess_link = models.ForeignKey(PropertyProcess,
on_delete=models.CASCADE)
deal_date = models.DateField()
price_agreed = models.IntegerField()
target_move_date = models.DateField()
offer_accepted = models.ForeignKey(Offer,
on_delete=models.CASCADE)
class Meta:
verbose_name_plural = "deals"
def __str__(self):
return '%s, %s' % (
self.propertyprocess_link.property_link.address_line_1,
self.propertyprocess_link.property_link.postcode
)
html
{% block content %}
<div class="container-fluid header-container">
<div class="row">
<div class="col-sm-9 col-md-7 col-lg-5 mx-auto">
<div class="card-au card-signin my-5">
<div class="card-body">
<form id="offers-form" data-offers-url="{% url 'ajax_load_offers' %}" class=" text-center text-white" method="post" novalidate>
{% csrf_token %}
{{ dform.non_field_errors }}
<div class="form-colour mt-2">
{{ dform.propertyprocess_link.errors }}
<label class="mb-0 mt-1">Property Being Offered On:</label>
{{ dform.propertyprocess_link }}
</div><div class="form-colour mt-2">
{{ dform.offer_accepted.errors }}
<label class="mb-0 mt-1">Offer Being Accepted:</label>
{{ dform.offer_accepted }}
</div>
<div class="form-colour mt-2">
{{ dform.price_agreed.errors }}
<label class="mb-0 mt-1">Price Agreed:</label>
{{ dform.price_agreed }}
</div>
<div class="form-colour mt-2">
{{ dform.deal_date.errors }}
<label class="mb-0 mt-1">Deal Date:</label>
{{ dform.deal_date }}
</div>
<div class="form-colour mt-2">
{{ dform.target_move_date.errors }}
<label class="mb-0 mt-1">Target Move Date:</label>
{{ dform.target_move_date }}
</div>
<div class="mb-3"></div>
{# hidden submit button to enable [enter] key #}
<div class="hidden-btn" style="margin-left: -9999px"><input class="hidden-btn" type="submit" value=""/></div>
<div class="text-center mt-2">
<input type="submit" class="login-btn btn-green btn btn-lg border-green text-uppercase py-3" value="Add Deal" />
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}
{% block postloadjs %}
{{ block.super }}
<script>
$("#id_inst-propertyprocess_link").change(function () {
var url = $("#offers-form").attr("data-offers-url"); // get the url of the `load_offers` view
var propertyID = $(this).val(); // get the selected Property Process ID from the HTML input
$.ajax({ // initialize an AJAX request
url: url, // set the url of the request (= localhost:8000/ajax/load-offers/)
data: {
'propertyprocess_link': propertyID // add the Property Process id to the GET parameters
},
success: function (data) { // `data` is the return of the `load-offers` view function
$("#id_inst-offer_accepted").html(data); // replace the contents of the offers input with the data that came from the server
}
});
});
</script>
{% endblock postloadjs %}
Thanks very much for any help anyone can give.
I am trying to create a form with an embedded table that the user can dynamically add and remove table rows while entering content into the cell inputs.
HTML
<form id="myForm" action="{{ url_for('hello_world') }}" method="POST">
<div class="form-row text-left">
<div class="col-1 text-left">
<input type="checkbox" id="skills" name="skills" value="Yes">
</div>
<div class = "col-11 text-left">
<h2>TECHNICAL SKILLS</h2>
</div>
</div><!--form-row-->
<div class="form-row">
<table id="myTable" name="skillsTable">
<tr>
<th>Category</th>
<th>Entries</th>
</tr>
</table>
</div><!--form-row-->
<br>
<button type="button" onclick="addSkill()">Add Row</button>
<button type="button" onclick="deleteSkill()">Delete row</button>
<hr>
<input type="submit" value="Submit" onclick="submit()" />
</form>
As you can see in the screenshot [![screenshot of the user form][1]][1] the name attribute is correctly being appended to added cell.
The goal is to have a way to get the table values dynamically created by the user over to the flask template where they can be displayed.
Javascript
<script>
var c1=0;
var c2=0;
function addSkill() {
var table = document.getElementById("myTable");
var row = table.insertRow(-1);
var cell1 = row.insertCell(0);
var cell2 = row.insertCell(1);
cell1.innerHTML = "<input type='text' value=' ' name=cell1_"+c1.toString()+"> ";
cell2.innerHTML = "<input type='text' value=' ' name=cell2_"+c2.toString()+"> ";
c1++;
c2++;
}
function deleteSkill() {
document.getElementById("myTable").deleteRow(-1);
}
</script>
I have tried setting the name attribute for each newly created cell using a counter, but this still does not show up rendered in the flask template:
flask
#app.route('/hello_world', methods=['GET', 'POST'])
def hello_world():
if request.method == 'POST':
result = {}
try:
skills = request.form['skills']
result['skills'] = skills
result['value'] = request.form['cell1_1']
except:
pass
return render_template("result.html",result = result)
result.html
{% if result.skills %}
<p>{{ result.value }}</p>
{% endif %}
In this example, I would expect to see "Language" show up on rendered after submitting the form if the checkbox is selected.
How can I refer to the table in the form from flask and loop through the <input> elements if they are dynamically created? Thx
[1]: https://i.stack.imgur.com/samhG.png
result.html
{% if result.skills %}
{% set skillsTable = result.skillsTable %}
<h2>TECHNICAL SKILLS</h2>
<table>
{% for skill in skillsTable %}
{% if loop.index|int % 2 == 0 %}
<tr><td>{{ skillsTable.pop(0) }}:</td><td>{{ skillsTable.pop(0) }}</td></tr>
{% else %}
<tr><td>{{ skillsTable.pop(0) }}:</td><td>{{ skillsTable.pop(0) }}</td></tr>
{% endif %}
{% endfor %}
{% endif %}
flask
#app.route('/hello_world', methods=['GET', 'POST'])
def hello_world():
if request.method == 'POST':
result = {}
try:
skills = request.form['skills']
result['skills'] = skills
result['skillsTable'] = []
form = request.form
for key, value in form.items():
if key.startswith("cell"):
result['skillsTable'].append(value)
except:
pass
return render_template("result.html",result = result)
So... I'm trying to dynamically fill option from a selectfield using data coming from a firestore database.
I initialise a new object from my Items class, I load my form named AjouterForm(), then I call my function getItemsDB() that populate the var fields (it's a list) using data from firestore db, and finally I try to populate the selectfield itemFields.
I can launch the program but when I go on "ajouter" I get an error :
ValueError: too many values to unpack (expected 2)
routes.py
#app.route("/ajouter", methods=['GET', 'POST'])
def ajouter():
try:
items = Items()
form = AjouterForm()
items.getItemsDB()
form.itemsFields.choices = items.fields
except ValueError:
flash("Problème", 'danger')
return render_template('ajouter.html', title='Ajouter', form=form)
ajouter.html
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Ajouter</legend>
<div class="row">
<div class="col">
<div class="form-group">
{{ form.type.label }}
{{ form.type }}
</div>
</div>
<div class="col">
<div class="form-group">
{{ form.customer.label }}
{{ form.customer }}
</div>
</div>
</div>
<div class="output">
<div id="INVOICE" class="hidden INVOICE">
<!--A compléter-->
</div>
<div id="ORDER" class="hidden ORDER">
{{ form.block.label }}
{{ form.block }}
</div>
<div id="SERVICE_REQUEST" class="hidden SERVICE_REQUEST">
<!--A compléter-->
</div>
<div class="output2">
<div id="ITEMS" class="hidden2 ITEMS">
{{ form.itemsFields.label }}
{{ form.itemsFields }}
</div>
<div id="PARTNERS">
</div>
<div id="fields">
</div>
</div>
</div>
</fieldset>
</form>
</div>
forms.py
class AjouterForm(FlaskForm):
type = SelectField("Type", choices=[("INVOICE","invoice"), ("ORDER","order"), ("SERVICE_REQUEST", "service_request")], id="type")
block = SelectField("Block", choices=[("ITEMS","items"), ("PARTNERS","partners"), ("fields","fields")], id="block")
itemsFields = SelectField("Champs", choices=[], coerce=list, id="itemsField")
tailleDocX = IntegerField("Taille X", validators=[DataRequired()])
tailleDocY = IntegerField("Taille Y", validators=[DataRequired()])
customer = SelectField("Customer", choices=[("mlt", "mlt")])
submit = SubmitField('Confirmer')
models.py
class Items(object):
def __init__(self):
self.fields = []
def getItemsDB(self):
doc_ref = db.collection("customizing").document("FORMS")
doc = doc_ref.get()
datas = doc.get("ORDER").get("ITEMS").get("fields")
for data in datas:
self.fields.append(data)
I'm kinda new to python, flask and firestore database and I can't manage to find what is causing this error, hope my english isn't to bad ! :-)
FINALLY I FIXED IT !
I found out that selectfields only accept tuple, I did this :
items = Items()
items.getItemsDB()
form.itemsFields.choices = []
for fields in items.fields:
form.itemsFields.choices += [(fields, fields)]
I have SQLite database which contains data regarding products such as Product Name, Description and Like which shows if the user likes or doesn't like the product.
A user search for different products which populate a table with the name, description and a checkbox which is clicked in if the value of Like is 1 in the database and unchecked if not.
I have implemented the button through (as seen in index.html)
<form method = "POST"> <input type="checkbox" name="like" {% if product.like == 1 %} checked {% else %} {% endif %}>
I now want the user to be able to check and uncheck the boxes in the table, and then press a button that updates the Like column in the database, how do I do this?
(I can't even access the value of the button in app.py. When trying print(requests.form['like']) on the line after name=requests.form['search'] it returns BadRequestKeyError: The browser sent a request that this server could not understand. KeyError: 'like')
app.py
...
product = []
#app.route('/', methods = ['POST'])
def index():
name = None
if request.method == 'POST':
name = request.form['search']
if name is not None or name != "":
query_product = Products.query.filter(Products.productname==name).first()
if (query_product is not None) and (query_product not in product):
company.append(query_company)
print(company, file = sys.stderr)
return render_template('index.html', companies = company)
Products class
class Products(db.Model):
index = db.Column(db.Integer(), primary_key = True)
productname = db.Column(db.String(), primary_key = False)
description = db.Column(db.String(), primary_key = False)
like = db.Column(db.Integer(), primary_key = False)
...more columns
index.html
<!-- /templates/index.html -->
{% extends 'base.html' %}
{% block content %}
<form method="POST">
<input type="text" placeholder="Search for Product.." name="search">
<button type="submit" name="submit">Search</button> <button type="submit" name="update" value = company.index style = margin-left:45%>Update</button>
<div class="product-container" style="overflow: auto; max-height: 80vh">
<div class="table-responsive">
<table class="table" id="products">
<thead>
<tr>
<th scope="col">Product Name</th>
<th scope="col">Description</th>
<th scope="col">Like</th>
</tr>
</thead>
<tbody>
{% for product in products %}
<tr {{company.index}}>
<th scope="row">{{ product.productname}}</th>
<td> {{ product.description }} </td>
<td><form method = "POST"> <input type="checkbox" name="like" {% if product.like == 1 %} checked {% else %} {% endif %}></form></td>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
I guess you need to do this with javascript and add another route to your backend which then updates the database.
maybe something like this, if it should happen automatically:
<input type="checkbox" onchange="updateLike('productId', this.checked)">
<script>
async function updateLike(productId, doesLike) {
let response = await fetch("http://localhost/products/productId/like", {
method:"POST",
headers: {"Content-Type":"application/json"},
body: JSON.stringify({
productId: productId,
like: doesLike
})
});
}
</script>
or you could add a button which sends the request to the server.
<input type="checkbox" name="like"/>
<button onclick="updateLike('productId', document.querySelector('input[name=like]').checked)">confirm</button>