I have seen a large number of tutorials that show login forms with flask and flash-wtf but none where multiple select boxes are populated from database table values.
This is what I am trying to do:
A simple registration form:
First name
Last name
Address Line 1
Address Line 2
City
State Id (populated from states library query of Id,state)
Country Id (populated from countries library query of country, id)
Sample code or a link to a walk through would be greatly appreciated.
I tried to find a explanation for how to do this and couldn't find one. So I'm going to write one here. This is how I do things, there's probably better ways to go about it.
Source Code
You can download the full source code for this tutorial on my github account. I'm pretty much copying and pasting from the source code, but just in case github dies some day here we go.
Configuration
Need to configure our application and the database connection. In most cases
you probably want to load all of this from a configuration file.
In this tutorial we're just going to use a basic sqlalchemy test database.
app = Flask(__name__)
app.config['SECRET_KEY'] = 'Insert_random_string_here'
Set this configuration to True if you want to see all of the SQL generated.
app.config['SQLALCHEMY_ECHO'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
WTForms configuration strings
app.config['WTF_CSRF_ENABLED'] = True
CSRF tokens are important. Read more about them here,
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
app.config['WTF_CSRF_SECRET_KEY'] = 'Insert_random_string_here'
db = SQLAlchemy(app)
SQLALchemy Model
Next we need to create our model classes that will be used during the creation
of the database and also when we want to manipulate the database. This should
normally be it's own seperate file.
I'm importanting from here as if this was it's own seperate file.
Normally you'd have to import doing something like
from application import db
class RegisteredUser(db.Model):
"""
loads and pushes registered user data after they have signed up.
SQLalchemy ORM table object which is used to load, and push, data from the
server memory scope to, and from, the database scope.
"""
__tablename__ = "RegisteredUser"
#all of the columns in the database.
registered_id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(70))
last_name = db.Column(db.String(70))
address_line_one = db.Column(db.String(256))
address_line_two = db.Column(db.String(256))
city = db.Column(db.String(50))
"""
Now we're going to create all of the foreign keys for the RegisteredUser
table. The db.relationship section allows us to easily and automatically
join the other tables with registeredUser. The Join will only take place
if you attempt to access columns from the State or country table.
For more on Foreign keys using SQLAlchemy go to
"""
state_id = db.Column(
db.Integer,
db.ForeignKey('State.state_id'),
nullable=False)
#retrives the users name for display purposes.
state_by = db.relationship(
'State',
foreign_keys=[state_id],
backref=db.backref('State', lazy='dynamic'))
country_id = db.Column(
db.Integer,
db.ForeignKey('Country.country_id'),
nullable=False)
#retrives the users name for display purposes.
country_by = db.relationship(
'Country',
foreign_keys=[country_id],)
#this is the method and function style I've chosen when lines are too long
def __init__(
self,
first_name,
last_name,
address_line_one,
address_line_two,
city,
state_id,
country_id):
"""
Used to create a RegisteredUser object in the python server scope
We will be calling these init functions every time we use
RegisteredUser() as a 'function' call. It will create a SQLalchemy ORM
object for us.
"""
self.first_name = first_name
self.last_name = last_name
self.address_line_one = address_line_one
self.address_line_two = address_line_two
self.city = city
self.state_id = state_id
self.country_id = country_id
class State(db.Model): # pylint: disable-msg=R0903
"""
Holds State names for the database to load during the registration page.
SQLalchemy ORM table object which is used to load, and push, data from the
server memory scope to, and from, the database scope.
"""
__tablename__ = "State"
state_id = db.Column(db.Integer, primary_key=True)
state_name = db.Column(db.String(10), unique=True)
def __init__(self, state_name):
"""
Used to create a State object in the python server scope
"""
self.state_name = state_name
class Country(db.Model): # pylint: disable-msg=R0903
"""
Holds Country names for the database to load during the registration page.
SQLalchemy ORM table object which is used to load, and push, data from the
server memory scope to, and from, the database scope.
"""
__tablename__ = "Country"
country_id = db.Column(db.Integer, primary_key=True)
#longest country length is currently 163 letters
country_name = db.Column(db.String(256), unique=True)
def __init__(self, country_name):
"""
Used to create a Country object in the python server scope
"""
self.country_name = country_name
def create_example_data():
"""
Generates all of the demo data to be used later in the tutorial. This is
how we can use our ORM objects to push data to the database.
NOTE: create_example_data is called at the very bottom of the file.
"""
#Create a bunch of state models and add them to the current session.
#Note, this does not add rows to the database. We'll commit them later.
state_model = State(state_name="WA")
db.session.add(state_model)
state_model = State(state_name="AK")
db.session.add(state_model)
state_model = State(state_name="LA")
db.session.add(state_model)
#Normally I load this data from very large CVS or json files and run This
#sort of thing through a for loop.
country_model = Country("USA")
db.session.add(country_model)
country_model = Country("Some_Made_Up_Place")
db.session.add(country_model)
# Interesting Note: things will be commited in reverse order from when they
# were added.
try:
db.session.commit()
except IntegrityError as e:
print("attempted to push data to database. Not first run. continuing\
as normal")
WTForm
Now we're going to make our WTForms objects. These will have the data aquired
from the database placed on them, then we will pass them to our template files
where we will render them.
I'm importanting from here as if this was it's own seperate file.
import wtforms
import wtforms.validators as validators
from flask.ext.wtf import Form
class RegistrationForm(Form):
"""
This Form class contains all of the fileds that make up our registration
Form.
"""
#Get all of the text fields out of the way.
first_name_field = wtforms.TextField(
label="First Name",
validators=[validators.Length(max=70), validators.Required()])
last_name_field = wtforms.TextField(
label="Last Name",
validators=[validators.Length(max=70), validators.Required()])
address_line_one_field = wtforms.TextField(
label="Address",
validators=[validators.Length(max=256), validators.Required()])
address_line_two_field = wtforms.TextField(
label="Second Address",
validators=[validators.Length(max=256), ])
city_field = wtforms.TextField(
label="City",
validators=[validators.Length(max=50), validators.Required()])
# Now let's set all of our select fields.
state_select_field = wtforms.SelectField(label="State", coerce=int)
country_select_field = wtforms.SelectField(label="Country", coerce=int)
Views
import flask
def populate_form_choices(registration_form):
"""
Pulls choices from the database to populate our select fields.
"""
states = State.query.all()
countries = Country.query.all()
state_names = []
for state in states:
state_names.append(state.state_name)
#choices need to come in the form of a list comprised of enumerated lists
#example [('cpp', 'C++'), ('py', 'Python'), ('text', 'Plain Text')]
state_choices = list(enumerate(state_names))
country_names = []
for country in countries:
country_names.append(country.country_name)
country_choices = list(enumerate(country_names))
#now that we've built our choices, we need to set them.
registration_form.state_select_field.choices = state_choices
registration_form.country_select_field.choices = country_choices
#app.route('/', methods=['GET', 'POST'])
def demonstration():
"""
This will render a template that displays all of the form objects if it's
a Get request. If the use is attempting to Post then this view will push
the data to the database.
"""
#this parts a little hard to understand. flask-wtforms does an implicit
#call each time you create a form object. It attempts to see if there's a
#request.form object in this session and if there is it adds the data from
#the request to the form object.
registration_form = RegistrationForm()
#Before we attempt to validate our form data we have to set our select
#field choices. This is just something you need to do if you're going to
#use WTForms, even if it seems silly.
populate_form_choices(registration_form)
#This means that if we're not sending a post request then this if statement
#will always fail. So then we just move on to render the template normally.
if flask.request.method == 'POST' and registration_form.validate():
#If we're making a post request and we passed all the validators then
#create a registered user model and push that model to the database.
registered_user = RegisteredUser(
first_name=registration_form.data['first_name_field'],
last_name=registration_form.data['last_name_field'],
address_line_one=registration_form.data['address_line_one_field'],
address_line_two=registration_form.data['address_line_two_field'],
city=registration_form.data['city_field'],
state_id=registration_form.data['state_select_field'],
country_id=registration_form.data['country_select_field'],)
db.session.add(registered_user)
db.session.commit()
return flask.render_template(
template_name_or_list='success.html',
registration_form=registration_form,)
return flask.render_template(
template_name_or_list='registration.html',
registration_form=registration_form,)
runserver.py
Finally, this is for development purposes only. I normally have this in a
file called RunServer.py. For actually delivering your application you should
run behind a web server of some kind (Apache, Nginix, Heroku).
if __name__ == '__main__':
db.create_all()
create_example_data()
app.run(debug=True)
Templates
in macros.html
{% macro render_field(field) %}
<dt>{{ field.label }}
<dd>{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}
{% macro render_data(field) %}
<dt>{{ field.label }}
<dd>{{ field.data|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}
In registration.html
{% from "macros.html" import render_field %}
<form method=post action="/">
{{registration_form.hidden_tag()}}
<dl>
{{ render_field(registration_form.first_name_field) }}
{{ render_field(registration_form.last_name_field) }}
{{ render_field(registration_form.address_line_one_field) }}
{{ render_field(registration_form.address_line_two_field) }}
{{ render_field(registration_form.city_field) }}
{{ render_field(registration_form.state_select_field) }}
{{ render_field(registration_form.country_select_field) }}
</dl>
<p><input type=submit value=Register>
</form>
Finally, in success.html
{% from "macros.html" import render_data %}
<h1> This data was saved to the database! </h1>
<form method=post action="/">
{{registration_form.hidden_tag()}}
<dl>
{{ render_data(registration_form.first_name_field) }}
{{ render_data(registration_form.last_name_field) }}
{{ render_data(registration_form.address_line_one_field) }}
{{ render_data(registration_form.address_line_two_field) }}
{{ render_data(registration_form.city_field) }}
{{ render_data(registration_form.state_select_field) }}
{{ render_data(registration_form.country_select_field) }}
</dl>
<p><input type=submit value=Register>
</form>
Related
A user have logged in and submitted 2 projects. I need to have a list of submitted projects by this user.
In my models.py I have a Project table:
class Project(db.Model):
__tablename__ = 'project'
users = db.relationship(User)
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) # means users.id is a ForeignKey for user_id
date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
title = db.Column(db.String(140), nullable=False) # Project TITLE
text = db.Column(db.Text, nullable=False) # JUST A TEXT OF THE PROJECT
def __init__ (self,title,text,user_id):
self.title = title
self.text = text
self.user_id = user_id
def __repr__(self):
return f"Project ID: {self.id} --Date: {self.date} -- {self.title}"
This is the route that I use to submit a project:
#app.route('/create_project', methods = ['GET','POST'])
#login_required
def create_project():
form = ProjectForm()
if form.validate_on_submit():
print(current_user)
project = Project(title = form.title.data,
text = form.text.data,
user_id = current_user.id
)
db.session.add(project)
db.session.commit
flash('Project created')
return redirect(url_for('welcome_user'))
return render_template('create_project.html', form=form)
When I submit a project, the code works well and I have a flash message that project is submitted. This means the data enters into a database.
However when I want to see the projects created by the same user:
# User's list of PROJECTS:
#app.route("/<first_name>")
#login_required # to make sure that user must be logged in to see that view
def user_posts(first_name):
#requesting a page
page = request.args.get('page',1,type=int)
user = User.query.filter_by(first_name=first_name).first_or_404()
# filtering the blog posts by username
projects = Project.query.filter_by(author=user).order_by(Project.date.desc()).paginate(page=page, per_page=5) # comes from backref='author' on models.py
return render_template('user_projects.html', projects=projects, user = user)
in user_projects.html I have the following:
{% extends "base.html" %}
{% block content %}
<div class="container">
<div class="jumbotron">
<div align="center">
<h1>Welcome to the page for {{user.first_name}}</h1>
</div>
</div>
USER PROJECTS <br>
project ID
<hr>
{{projects}}
<hr>
USER
<hr>
{{user}}
<hr>
{% for post in projects.items %}
<h2> {{post.title}} </h2>
<p class="text-muted">Published On: {{post.date.strftime("%Y-%m-%d")}}</p>
<br>
<p>{{post.text}}</p>
<br>
{% endfor %}
</div>
{% endblock %}
While I do have a user information inside this template, I can access name, surname etc., I have nothing inside a projects object, however I have <flask_sqlalchemy.Pagination object at 0x0000024F5E334CD0> on a screen - meaning that at least the query worked well and there is something in it.
I wonder how can I verify the content of the given table - when I create a test route:
#app.route('/test')
def test():
test = Project.query.all()
return render_template('test.html', test=test)
I see nothing inside a test object. Why?
I think you forgot the brackets behind the commit command within your route to save. The function is therefore not called and the new project is not stored in the database. The table is empty.
#app.route('/create_project', methods = ['GET','POST'])
#login_required
def create_project():
form = ProjectForm()
if form.validate_on_submit():
project = Project(
title = form.title.data,
text = form.text.data,
user_id = current_user.id
)
db.session.add(project)
db.session.commit() # <-- The brackets were missing here!
flash('Project created')
return redirect(url_for('welcome_user'))
return render_template('create_project.html', form=form)
Your queries appear correct.
Recently I've decide to create form creation form in Flask web app. After searching form creation found Formfield, FieldList classes in flask wtf forms and I can create the form with these classes. but it doesn't provide that I want to.
First- I am going to create a form creation form which will be help me to create form and fields on management interface.
Second- I want to be able to add the fields, not the same type of field, all different kind, such as (booleanField, StringField, IntegerField, DateTimeField etc.) because, in the form there could be different type of fields for specific reason.
Third- I want to retreive this form whenever I want to use in my view
On the DB models side;
class Form(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.StringField)
fields = db.relationship('FormFields', backref='forms', lazy=True)
class FormFields(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.StringField)
field_type = db.Column(db.StringField)
form_id = db.Column(db.Integer, db.ForeignKey('forms.id'), nullable=False)
And othr tables for StrinField, BooleanField, TextField, etc. etc. should be connected this field model, and when I save the data over this created form, these data should be saved int the correct tables
The reason I am searching this, because I don't want to hardcode the Forms and fields in the code, when I need to new form or field I don't want to update code itself, it should be dynamically updated on the database.
And I want to use sqlalchemy based form creation from management page. And this will help to create anytime new form and relate the fields to the form. And on the internet still I didn't find the these style form creation for Flask, almost all of them creating dynamic for with same type of fields
Any ideas?
Last a couple of weeks I was search how to create dynamically flask form based on models
And #nick-shebanov has been redirect me to another approach EAV impelemntation, which is really diffucult to implement. I've tried :)
And decide to create form based on dictionary, and intend to populate the related attributes from the model and pass it to form as dictionary.
What I've done so far;
# app.py file
from flask_wtf import Form
from flask import Flask, render_template, request, flash
from flask_wtf import FlaskForm
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from wtforms import TextField, IntegerField, HiddenField, StringField, TextAreaField, SubmitField, RadioField,SelectField
from wtforms import validators, ValidationError
app = Flask(__name__)
app.secret_key = 'secret123'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///sqlite.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
migrate = Migrate(app, db)
# form class with static fields
class DynamicForm(FlaskForm):
form_type = HiddenField(default='FormType', render_kw={ 'type':'hidden' })
# name = StringField()
#app.route('/', methods=['GET', 'POST'])
def index():
fields = {
'username': 'Username',
'first_name': 'Fisrt Name',
'last_name': 'Last Name',
'email': 'Email',
'mobile_phone': 'Mobile Phone'
}
for key, value in fields.items():
setattr(DynamicForm, key, StringField(value))
form = DynamicForm()
if request.method == 'POST':
# print(dir(request.form))
# print(request.form)
dict = request.form.to_dict()
# print(dict.keys())
print(request.form.to_dict())
return render_template('index.html', form=form)
if __name__ == '__main__':
app.run(debug = True)
# index.html template
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<form method="POST">
{{ form.csrf_token }}
{{ form.form_type }}
{% for field in form if field.name != 'csrf_token' %}
{% if field.name != 'form_type' %}
<div>
{{ field.label() }}
{{ field() }}
{% for error in field.errors %}
<div class="error">{{ error }}</div>
{% endfor %}
</div>
{% endif %}
{% endfor %}
<input type="submit" value="Go">
</form>
</body>
</html>
Now I can see my all fields has been rendered including hidden field. When I fill the form and post the data, I can capture it.
But still I didn't achieve to implement save the captured data into database like vertical DB modelling style yet
Here is my simple approach of DB modelling
see image here
Is there suggestions?
basically I want to do something quite simple: I want to create a form for deleting entries in a database.
the template is creating a html table with all entries without any trouble. My problem now is: how to convert this to a form with a link in every row.
Of course I could do the manual way of writing html code with a link. But is there a more "flaskish" way? I'm already using wtforms and sqlalchemy
My route:
#app.route('/admin', methods=['GET', 'POST'])
#htpasswd.required
def admin(user):
orders = Order.query.all()
return render_template(
'admin.html', title="AdminPanel", orders=orders
)
The model:
class Order(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(120), index=True, unique=True)
The template:
{% for order in orders %}
<tr>
<td>{{order.email}}</td>
<td><i class="fa fa-trash" aria-hidden="true"></i></td>
</tr>
{% endfor %}
You should use a different route for deletion and not the same one you are using to render the template. Also you do not need a form for the deletion task. You can use get parameters for that and links like you have tried
add this to your routes:
from flask import redirect, url_for
from app import db # you should import your db or db session instance so you can commit the deletion change this line to wherever your db or db session instance is
#app.route('/delete/<order_id>', methods=['GET', 'POST'])
#htpasswd.required
def delete(order_id):
orders = Order.query.filter(Order.id == order_id).delete()
db.commit()
return redirect(url_for('admin'))
Basically you will perform a delete and then redirect back to the admin route with the above code
Your template file should be changed to:
{% for order in orders %}
<tr>
<td>{{order.email}}</td>
<td><i class="fa fa-trash" aria-hidden="true"></i></td>
</tr>
{% endfor %}
I'm in the learning stages of django. I just dived into a project to learn the framework and am having a series of questions throughout the process.
I basically want to have individual pages for users who create a task list and have them post tasks to their own page.
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class UserProfile(models.Model):
# This line is required. Links UserProfile to a User model instance.
user = models.OneToOneField(User)
# The additional attributes we wish to include.
website = models.URLField(blank = True)
# Override the __unicode__() method to return out something meaningful!
def __unicode__(self):
return self.user.username
class TaskItem(models.Model):
taskn = models.CharField(max_length = 400)
usern = models.ForeignKey(User)
In my template, if the user has their task entered, how do I call it to render onto the page?
My View:
def profile_page(request, username):
user = User.objects.get(username=username)
taskitems = user.taskn_set.all()
return render_to_response('profile.html', {}, context)
Current issue:
'User' object has no attribute 'taskn_set'
{{ request.user.taskitem_set.all }} would give you all the related task items. Now, to display it in your template:
{% for task_item in user.taskitem_set.all %}
{{ task_item.task_n }}
{% endfor %}
would display the list of tasks.
Here is the documentation on reverse-queries on foreign key (related_name) Also, read this
you would do something like this:
{% for task in user.taskitem_set.all %}
{{ task.task_n }}
{% endfor %}
This will fetch all TaskItem instances related to your user. (notice the extra database query)
While i don't know how your view works, i will assume that you are making the right checks to make sure that every user can only see his own tasks.
One performance trick you will find most useful is to use prefetch_related('taskitem_set'), this will prefetch the TaskItem instances as long as your UserProfile instance with one query:
user = User.objects.filter(id=user_id).prefetch_related('taskitem_set')
You can tune the code to match your preferences.
Hope this helps!
So I am working on a small Django project, which for the moment doesn't require optimization. But to prepare for the future, I'd like to know a bit more about the three approaches.
For instances, as part of the models, I have User and UserProfile, Transaction.
class User(models.Model):
name = ...
email = ...
class UserProfile(models.Model):
user = models.ForeignKey(User, related_name='profile')
photo = models.URLField(...)
...
class Transaction(models.Model):
giver = models.ForeignKey(User, related_name="transactions_as_giver")
receiver = models.ForeignKey(User, related_name='transactions_as_receiver')
...
I frequently need to do something like "return transactions that the request.user is giver or receiver", or "return the profile photo of a user". I have several choices, for instance to get a list of pending transactions and photos of both parties, I can do it at views.py level:
1.
#views.py
transactions = Transaction.objects.filter(Q(giver=request.user)|Q(receiver=request.user))
for transaction in transactions:
giver_photo = transactions.giver.profile.all()[0].photo
# or first query UserProfile by
# giver_profile = UserProfile.objects.get(user=transaction.giver),
# then giver_photo = giver_profile.photo
#
# Then same thing for receiver_photo
transaction['giver_photo'] = giver_photo
...
Or I can do it more on template level:
# some template
<!-- First receive transactions from views.py without photo data -->
{% for t in transactions %}
{{t.giver.profile.all.0.photo}}, ...
{% endfor %}
Or I can move some or even all of the above stuffs into filters.py
# some template
{{ for t in request.user|pending_transactions }}
{{ t.giver|photo }} {{ t.receiver|photo }}
{{ endfor }}
where photo and pending_transactions are roughly the same code in original views.py but moved to a filter.
So I wonder is there a best practice/guide line on how to choose which approach?
From Django documentation, lower level is faster, and therefore 2. 3. should be slower than 1; but how about comparing the 2. and 3.?
In getting a user photo, which of the two should be recommended, transactions.giver.profile.all()[0].photo OR profile = UserProfile.objects.get(...) --> photo = profile.photo?
Move this logic into models and managers. Views and templates must be as short as possible.
class User(models.Model):
...
def transactions(self):
return Transaction.objects.filter(Q(giver=self)|Q(receiver=self))
def photo(self):
return self.profile.all().first().photo
So the template will be:
{% for t in request.user.transactions %}
{{ t.giver.photo }} {{ t.receiver.photo }}
{% endfor %}
My experience says that business logic in model as much easier to test, support and reuse than in the views/templates.