How do I efficiency create big forms - FlaskForms - python-3.x

So I have an update form end point, which is very large, I've tried a few things to make it dynamically created to condense my code but I cant seem to figure it out.
#customers.route("/<int:customer_id>/update_customer", methods=['GET', 'POST'])
# not programmatic - needs work - I do not like this
def update_customer(customer_id):
post = Post.query.get_or_404(customer_id)
if post.author != current_user:
abort(403)
form = CustomerForm()
if form.validate_on_submit():
post.tpi_name = form.tpi_name.data
post.tpi_ref = form.tpi_ref.data
post.company_name = form.company_name.data
post.company_type = form.company_type.data
post.company_reg = form.company_reg.data
post.sic_code = form.sic_code.data
post.vat_number = form.vat_number.data
db.session.commit()
flash("That customer's record has been updated!", 'success')
return redirect(url_for('customers.view_customer', customer_id=post.id))
elif request.method == 'GET':
form.tpi_name.data = post.tpi_name
form.tpi_ref.data = post.tpi_ref
form.company_name.data = post.company_name
form.company_type.data = post.company_type
form.company_reg.data = post.company_reg
form.sic_code.data = post.sic_code
form.vat_number.data = post.vat_number
return render_template('customers/customers.html', form=form, username=current_user.username,image_file=current_user.image_file, email=current_user.email)
So say if we had 100+ fields in the form, how would I change this code so I don't have to explicitly state each field.

Based on pjcunningham's answer, and looking into wtform docs, I found this piece of code:
def edit_profile(request):
user = User.objects.get(pk=request.session['userid'])
form = EditProfileForm(request.POST, obj=user)
if request.POST and form.validate():
form.populate_obj(user)
user.save()
return redirect('/home')
return render_to_response('edit_profile.html', form=form)
Based on that, I guess you want to write this code:
if form.validate_on_submit():
form.populate_obj(post)
db.session.commit()
flash("That customer's record has been updated!", 'success')
return redirect(url_for('customers.view_customer', customer_id=post.id))
For the second part of your code (the "GET" branch), it depends on your intentions. You can populate your form with data from object when you create it:
form = CustomerForm(obj=post)

Related

when i update/edit data is duplicated instead of being updated in django

data is duplicated instead of being updated in django, please help me to overcome this i also tried update method but issues i have faced is image not displayed, therefore i is used save method which will save and make copy of anthor object which i dont want. it should update the same object.
views.py
def EuAdmin(request, pk):
pi = EmailDb.objects.get(id=pk)
if request.method == 'POST':
institution = request.POST.get('institution', '')
fullname = request.POST.get('fullname', '')
email = request.POST.get('email', '')
contact = request.POST.get('contact', '')
position = request.POST.get('position', '')
uploadd = request.FILES.get('upload', '')
sdata = EmailDb(institution=institution, fullname=fullname, contact=contact, email=email, position=position, uploaddata=uploadd)
sdata.save()
return HttpResponseRedirect("/eadmin")
return render(request, 'NEC/eupdate.html', {'pi': pi})
models.py
class EmailDb(models.Model):
institution = models.CharField(max_length=300, blank=True, null=True)
fullname = models.CharField(max_length=50, blank=True, null=True)
contact = models.IntegerField()
email = models.CharField(max_length=300, blank=True, null=True)
position = models.CharField(max_length=100, blank=True, null=True)
uploaddata = models.FileField(upload_to='appointment_letter')
def __str__(self):
return self.fullname
That's because you create a new EmailDb object. You can edit the one with:
from django.shortcuts import get_object_or_404
def EuAdmin(request, pk):
pi = get_object_or_404(EmailDb, pk=pk)
if request.method == 'POST':
institution = request.POST.get('institution', '')
fullname = request.POST.get('fullname', '')
email = request.POST.get('email', '')
contact = request.POST.get('contact', '')
position = request.POST.get('position', '')
uploaded = request.FILES.get('upload', '')
pi.institution = institution
pi.fullname = fullname
pi.contact = contact
pi.email = email
pi.position = position
pi.uploaddata = uploaded
pi.save()
return HttpResponseRedirect('/eadmin')
return render(request, 'NEC/eupdate.html', {'pi': pi})
Note: It is better to use a Form [Django-doc]
than to perform manual validation and cleaning of the data. A Form will not
only simplify rendering a form in HTML, but it also makes it more convenient
to validate the input, and clean the data to a more convenient type.
Note: Models normally have no Db suffix. A model is not a table, it is stored in a relational database as a table, but even then it has extra logic like validators, managers, etc.
Note: Please use an EmailField [Django-doc] instead of a CharField [Django-doc] to store an email address, this can do validation to check if the entered data is indeed an email address.
Note: It is often better to use get_object_or_404(…) [Django-doc],
then to use .get(…) [Django-doc] directly. In case the object does not exists,
for example because the user altered the URL themselves, the get_object_or_404(…) will result in returning a HTTP 404 Not Found response, whereas using
.get(…) will result in a HTTP 500 Server Error.
sdata = EmailDb(institution=institution, fullname=fullname, contact=contact, email=email, position=position, uploaddata=uploadd)
sdata.save()
This performs an INSERT SQL statement.
Since you already have object just set the value and call save() as
def EuAdmin(request, pk):
pi = EmailDb.objects.get(id=pk)
if request.method == 'POST':
institution = request.POST.get('institution', '')
fullname = request.POST.get('fullname', '')
email = request.POST.get('email', '')
contact = request.POST.get('contact', '')
position = request.POST.get('position', '')
uploaded = request.FILES.get('upload', '')
pi.institution = institution
pi.fullname = fullname
pi.contact = contact
pi.email = email
pi.position = position
pi.uploaddata = uploaded
pi.save()
return HttpResponseRedirect("/eadmin")
return render(request, 'NEC/eupdate.html', {'pi': pi})

Flask Assign different ID to different website visitor at the same time and write their input information into json file

I am using FLASK to create a series of websites for doing a survey, I want to run the server once and invite multiple participants to help me fill the form on the webpage.
1.
I am aiming for assigning a unique ID (currently using python UUID) to different visitors and save their input info into different JSON files. (eg,. A's id is 00001, after he submiting the form, the info will be saved in the file named 00001.json, B's info will be saved in 00002.json). For now, I only create a UUID when the server start running, every visitor's information will be rewritten in the same file, (what I want is n visitors will produce n files).
2.
I want to show different survey questions to different visitors.
I am quite confused about if it is related to Multi-thread? Session? Cookie? Flask-login.
Hope someone can guide me on this. Thanks a lot!
assignmentID = str(uuid.uuid1())
jsonFileName = "upload/" + assignmentID + ".json"
jsonSurvey = "upload/" + assignmentID + "_survey.json"
#it ideally should randomly select different info/question no. to different user
Idx = random.sample(range(0,10), 5)
#app.route("/")
def index():
return render_template("surveyPage.html", data=Idx)
# write input information from webpage to JSON files, each visitor ideally should have their own JSON file.
#app.route("/record")
def recordData():
if request.method == 'POST':
print("READING FORM")
with open(jsonSurvey, 'w') as f:
json.dump(request.form, f)
f.close()
if __name__ == "__main__":
app.config['JSON_SORT_KEYS'] = False
app.run(debug = True)
# app.config["TEMPLATES_AUTO_RELOAD"] = True
You are defining assignmentID, jsonFileName, jsonSurvey and Idx outside of the request handlers which means they will be set once: when the server starts. jsonSurvey will therefore have the same value for each request which means that every new survey you store will overwrite the previous one.
The solution is to move the definition into the request handlers so that you genereate new values on each request.
#app.route("/")
def index():
Idx = random.sample(range(0,10), 5)
return render_template("surveyPage.html", data=Idx)
#app.route("/record")
def recordData():
if request.method == 'POST':
assignmentID = str(uuid.uuid1())
jsonFileName = "upload/" + assignmentID + ".json"
jsonSurvey = "upload/" + assignmentID + "_survey.json"
print("READING FORM")
with open(jsonSurvey, 'w') as f:
json.dump(request.form, f)
f.close()

results of sqlite query not displayed in flask web app

I'm attempting to learn flask, so decided to follow this tutorial:
https://www.blog.pythonlibrary.org/2017/12/14/flask-101-adding-editing-and-displaying-data/
I just updated my main function with the below:
#app.route('/results')
def search_results(search):
results = []
search_string = search.data['search']
if search.data['search'] == '':
qry = db_session.query(Album)
results = qry.all()
if not results:
flash('No results found!')
return redirect('/')
else:
# display results
table = Results(results)
table.border = True
return render_template('results.html', table=table)
but when I add an album to the DB and try to query it back using search option it says no results. The DB file was created correctly and I have exactly the same code as in the tutorial up to this point.
The only change I made was adding from tables import Results. Full main.py below. Can you please give me some guidance about where to look for the culprit? Like I said, just learning, so any suggestions re resources in a friendly laid out way would be much appreciated (beginner programmer).
from app import app
from db_setup import init_db, db_session
from forms import MusicSearchForm, AlbumForm
from flask import flash, render_template, request, redirect
from models import Album, Artist
from tables import Results
init_db()
def save_changes(album, form, new=False):
"""
Save the changes to the database
"""
# Get data from form and assign it to the correct attributes
# of the SQLAlchemy table object
artist = Artist()
artist.name = form.artist.data
album.artist = artist
album.title = form.title.data
album.release_date = form.release_date.data
album.publisher = form.publisher.data
album.media_type = form.media_type.data
if new:
# Add the new album to the database
db_session.add(album)
# commit the data to the database
db_session.commit()
#app.route('/', methods=['GET', 'POST'])
def index():
search = MusicSearchForm(request.form)
if request.method == 'POST':
return search_results(search)
return render_template('index.html', form=search)
#app.route('/results')
def search_results(search):
results = []
search_string = search.data['search']
if search.data['search'] == '':
qry = db_session.query(Album)
results = qry.all()
if not results:
flash('No results found!')
return redirect('/')
else:
# display results
table = Results(results)
table.border = True
return render_template('results.html', table=table)
#app.route('/new_album', methods=['GET', 'POST'])
def new_album():
"""
Add a new album
"""
form = AlbumForm(request.form)
if request.method == 'POST' and form.validate():
# save the album
album = Album()
save_changes(album, form, new=True)
flash('Album created successfully!')
return redirect('/')
return render_template('new_album.html', form=form)
if __name__ == '__main__':
app.run()
No doubt you have already peppered your source code with print() statements and found nothing illuminating. Cached rows in the DB model might be the aspect that is hard to understand, here, and logging sqlite calls would shed light on that.
Use this:
import logging
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
It's noisy, but it will show when rows hit the backend DB and when they are retrieved.
Get in the habit of repeatedly issuing debug queries like this, so you know for sure what has been persisted:
$ echo 'select * from album;' | sqlite3 music.db
For repeatable testing, it can be convenient to copy the database file to a backup location, and then cp that frozen snapshot on top of the active file before each test run. It's important that the running flask app be restarted after such copying. Setting FLASK_DEBUG=1 can help with that.
Also, jeverling suggests using SQLAlchemyDebugPanel.

Flask Sample webpage that request input from user performs no action

I trying to learn how to code in Flask and am building a small portal that accepts inputs from the user (User could select among various check boxes). Based on the input I am trying to display the selected columns by means of an excel file. Given below what I have done thus far and I am not sure how to take this forward.
#app.route('/index', methods=['GET','POST'])
def user_input():
form = SampleForm()
if form.validate_on_submit():
Username = form.username_field.data
Age = form.age_field.data
output = user_input(Username,Age)
return render_template('index', form=form)
I have managed to build the above code by reading through various blogs and posts but this does nothing. Could anyone guide me on where am I going wrong with the above sample piece of code. Thanks
Class.py
class test(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(100), nullable=False)
age = db.Column(db.String(100), nullable=False)
Python function:
def function(*field_names):
cursor = conn.cursor()
dwh_cursor.execute('select {} from enrolments'.format(', '.join(str(field) for field in field_names)))
print(field_names)
output_file = dwh_cursor.fetchall()
Does this help? Might need some tweaking to fit your needs.
#app.route('/index', methods=['GET','POST'])
def user_input():
form = SampleForm()
if form.validate_on_submit():
newform = User(
username = form.username_field.data,
age = form.age_field.data,
)
db.session.add(newform)
db.session.commit()
return redirect(url_for('user_input'))
return render_template('user_input', form = form)

how to restrict other user from updating an db object in Flask?

I am new to flask and building an ticket assigner application. Generator end point will assign always the oldest ticket(status=q) from the system to the person logged in. Then either he will resolve(post method = s) the ticket or can update its status to pending (post method = p).
#main.route('/ticket/generator', methods=['GET', 'POST'])
#login_required
def generate_ticket():
ticket = Ticket.query.filter_by(status='q').order_by(Ticket.create_date).first()
form = GenerateTicketForm(obj=ticket)
if form.validate_on_submit():
ticket.ticket_id = form.ticket_id.data
ticket.status = form.status.data
db.session.add(ticket)
db.session.commit()
flash('Ticket Status Update Successfully.')
return redirect(url_for('main.generate_ticket'))
return render_template('generate_ticket.html', form=form)
I want to change the status when the ticket already assigned to someone, so that other do not get the same ticket. So I have created a class method to change the status when getting the oldest ticket:
#classmethod
def activate_tkt_flag(cls, ticket_id_, create_date_):
ticket_db_obj = cls(ticket_id=ticket_id_, create_date=create_date_)
ticket_in_memory = Ticket.query.get(ticket_db_obj.ticket_id)
ticket_in_memory.status = 'a'
db.session.commit()
return ticket_in_memory
If call that after the ticket object in the first query it rewrite the loop some how and assign a new ticket and forget about the previous one the it can not rewrite the the ticket any more and I am getting that error: Key (ticket_id)=(T5) already exists UPDATE ticket SET ticket_id=%(ticket_id)s, status=%(status)s WHERE ticket.ticket_id = %(ticket_ticket_id)s'] [parameters: {'ticket_id': 'T5', 'status': 's', 'ticket_ticket_id': 'T1'}]. If some one has better idea how to do it will be grateful, I am kind of stuck here. Here is the form looks like.
Finally I was able to solve it. I just create one end point which execute first and update the ticket status and pass the ticket id to the next end point. So the last end point show the ticket with updated status to render template html.
#main.route('/ticket/generator/<ticket_id>', methods=['GET', 'POST'])
#login_required
def generate_ticket(ticket_id):
ticket = Ticket.query.get(ticket_id)
form = GenerateTicketForm(obj=ticket)
if form.validate_on_submit():
ticket.ticket_id = form.ticket_id.data
ticket.status = form.status.data
db.session.add(ticket)
db.session.commit()
# flash('Ticket Status Update Successfully.')
return redirect(url_for('main.display_status', ticket_id=ticket.ticket_id))
return render_template('generate_ticket.html', form=form)
#main.route('/ticket/assign')
def assign_ticket():
ticket = Ticket.query.filter_by(status='q').order_by(Ticket.create_date).first_or_404()
ticket.status = 'a'
db.session.commit()
return redirect(url_for('main.generate_ticket', ticket_id=ticket.ticket_id))
The assign end point execute first then the ticket id handed over to generator end point.

Resources