I want to preload form data in wtforms with data from my database without knowing the column names.
this works so long as I know the column name I want.
form.column.data=User.query.get(1).first().column
what I want to do is go over all the columns like so:
for attr, value in User.query.get(1).__dict__.iteritems():
form.attr.data = value
doing so gives me the error:
AttributeError: 'EditUsersForm' object has no attribute 'attr'
Here is a snippet of the code I'm trying to get to work specifically.
forms.py
class EditUsersForm(ModelForm):
class Meta:
model=User
exclude=['password_hash']
models.py
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(32), index=True, unique=True)
password_hash = db.Column(db.String(128))
email = db.Column(db.String(120), index=True, unique=True)
posts = db.relationship('Post', backref='author', lazy='dynamic')
#mobilitys = db.relationship('Mobility', backref='username', lazy='dynamic')
about_me = db.Column(db.String(140))
last_seen = db.Column(db.DateTime)
def __repr__(self): # pragma: no cover
return '<user> %r' % (self.username)
views.py
#app.route('/manpower_edit_users', methods=['GET', 'POST'])
#login_required
def manpower_edit_users():
form=QueryAllUsers(request.form)
forms=EditUsersForm(request.form)
'''
if forms.validate():
print("validate")
print("forms.validate=",forms.validate)
flash("User added")
'''
if request.method == "POST":
for attr, value in User.query.get(user_list).__dict__.iteritems():
forms.attr.data=value
elif request.method == "GET":
for attr, value in User.query.get(1).__dict__.iteritems():
forms.attr.data=value
return render_template('manpower_edit_users.html',title='Manpower Edit User', form=form, forms=forms)
edit_user_form = EditUsersForm(obj = User.query.get(1))
You can populate a form from data. The keyword argument obj in documentation of WTForms said:
obj – If formdata has no data for a field, the form will try to get it from the passed object.
Related
I'm trying to get the id from an object that is trapped in a self-referential relationship. I believe my definition of the relationship may be limiting its function, or perhaps I'm projecting. I've tried every related SO solution I could find to no avail. Mainly, the form.parent_id.data.parent_id pattern I've seen on several solutions throws AttributeError: 'NoneType' object has no attribute 'parent_id'. I'm aware that sqlalchemy.exc.InterfaceError: <unprintable InterfaceError object> results from form.parent_id.data because QuerySelectField returns an object, not an id. I get the same error when setting get_pk=lambda x: x.parent_id in the form object.
routes.py
def edit_user(id):
form = UserForm()
user = User.query.get_or_404(id)
selected_date_ids = []
upcoming_dates = TestDate.query.order_by(TestDate.date).filter(TestDate.status != 'past')
tests = sorted(set(TestDate.test for TestDate in TestDate.query.all()), reverse=True)
if form.validate_on_submit():
if request.method == "POST":
...
user.tutor_id=form.tutor_id.data
user.parent_id=form.parent_id.data
models.py
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
...
tutor_id = db.Column(db.Integer, db.ForeignKey('user.id'))
students = db.relationship('User', backref=db.backref('tutor'),
foreign_keys=[tutor_id], remote_side=[id])
parent_id = db.Column(db.Integer, db.ForeignKey('user.id'))
children = db.relationship('User', backref=db.backref('parent'),
foreign_keys=[parent_id], remote_side=[id])
forms.py
def get_tutors():
return User.query.filter_by(role='tutor')
def get_parents():
return User.query.filter_by(role='parent')
def full_name(User):
return User.first_name + " " + User.last_name
class UserForm(FlaskForm):
...
tutor_id = QuerySelectField('Tutor', default=1, query_factory=get_tutors, get_label=full_name, allow_blank=True)
parent_id = QuerySelectField('Parent', query_factory=get_parents, get_label=full_name, allow_blank=True)
is_admin = BooleanField('Admin')
submit = SubmitField('Save')
I ended up switching from QuerySelectField to SelectField using this answer to populate dynamic values, and everything became much simpler. Still not sure about my db.relationships, but they seem to be getting the job done.
I have created ContactForm as a quick WTForm within my HTML templates. When I go through my application and try to use the contact form to add the name and email values in the ContactForm to my User class it doesn't work. When I query User class in DB I just get empty brackets []. Somebody, please help me!
class ContactForm(FlaskForm):
name = StringField('Name', [InputRequired()])
email = StringField('Email Address', [InputRequired()])
submit = SubmitField('Sign Up')
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, unique=False, nullable=False)
email = db.Column(db.String, unique=False, nullable=False)
def __init__(self, name, email):
self.name = name
self.email = email
def __repr__(self, name, email):
return f"{name},{email}"
#app.route('/contact', methods=['GET'])
def contact():
form = ContactForm()
if form.validate_on_submit():
name = form.name.data
email = form.email.data
user = User(name, email)
db.session.add(user)
db.session.commit()
flash('Success')
return redirect(url_for('home'))
else:
return render_template('contact.html', form=form)
return render_template('contact.html', form=form)
Please try with methods POST in your route.
I've created a form which takes user's name and their email address. I get this data from the form and put it into a sqlite3 database in the following way:
#app.route('/my_form', methods=["GET", "POST"])
def form_data():
if request.method == "POST":
user_name = request.form["name"]
new_user = form_database(name=user_name)
user_email = request.form["email"]
new_user_email = form_database(email=user_email)
try:
db.session.add(new_user)
db.session.add(new_user_email)
db.session.commit()
return redirect("/my_form")
Current result: each data entry gets recorded into a new row:
1|Jack||||||||||
2||svisto#hotmail.com|||||||||
Desirable result: each data entry gets recorded into the same row:
1|Jack|svisto#hotmail.com|||||||||
Question: How can I change the code such that I get the desirable result?
Lets say you have a User class in your model like this:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer,
primary_key=True)
username = db.Column(db.String(32),
index=False,
unique=True,
nullable=False)
email = db.Column(db.String(64),
index=True,
unique=True,
nullable=False)
Then you can do this in your try block
try:
new_user = User(user_name=user_name,
email=email)
db.session.add(new_user)
db.session.commit()
Solution I found:
I fused:
new_user = form_database(name=user_name) and new_user_email = form_database(email=user_email)
together such that the code looks like this:
#app.route('/my_form', methods=["GET", "POST"])
def form_data():
if request.method == "POST":
user_name = request.form["name"]
user_email = request.form["email"]
new_user_details = form_database(name=user_name, email=user_email)#assigns 2 form inputs for both columns in the database model to the same variable
try:
db.session.add(new_user_details)#adds that variable to the database as one entry, hence in one row but different columns
db.session.commit()
I have two tables, named projects and actions and every project contain several action
class Projet(db.Model):
__tablename__ = 'projets'
id = db.Column(db.Integer, primary_key=True)
nom_projet = db.Column(db.String(100))
description_projet = db.Column(db.String(800))
date_affectation = db.Column(db.DateTime, nullable = False)
statut_projet = db.Column(db.String(100))
admin_id = db.Column(db.Integer, db.ForeignKey('admins.id'))
actions = db.relationship('Action', backref='projet',
lazy='dynamic')
def __repr__(self):
return '<Projet: {}>'.format(self.id)
class Action(db.Model):
__tablename__ = 'actions'
id = db.Column(db.Integer, primary_key=True)
projet_id = db.Column(db.Integer, db.ForeignKey('projets.id'))
description = db.Column(db.String(1000))
statut_action = db.Column(db.String(100))
date_action = db.Column(db.DateTime, nullable = False)
date_execution = db.Column(db.DateTime, nullable = True)
def __repr__(self):
return '<Action: {}>'.format(self.id)
my problem is, I need to create a new action based on an existing project as shown in image,
I need to click on add button and he must redirect me to action form with the name of project auto-selected, and I entre the action details.
this is my first code to add action:
#auth.route('/action/add', methods=['GET', 'POST'])
#login_required
def add_action():
form = ActionForm()
if form.validate_on_submit():
action = Action(
projet = form.projet.data,
description = form.description.data,
statut_action = form.statut_action.data,
date_action = form.date_action.data,
date_execution = form.date_execution.data
)
try:
db.session.add(action)
db.session.commit()
flash('You have successfully added a new action.')
except:
flash('Error: action name already exists.')
return redirect(url_for('auth.list_projets'))
return render_template('admin/actions/action.html', action="Add", form=form,
title="ADD ACTION")
Steps:
Update the URL to include project_id as path param: ex: /project/1/actions/add is meant to load a page with new action form for project with id 1
Update the links to add new action in the previous page(as shown in the screenshot) as per step 1
Remove project field from ActionForm as it is handled using path param
Update "new action form" page to show product name coming in product_name variable
Try,
#auth.route('/project/<project_id>/action/add', methods=['GET', 'POST'])
#login_required
def add_action(project_id):
form = ActionForm()
project = Project.query.get(project_id)
if not project:
flash('Error: invalid project')
abort(404)
if form.validate_on_submit():
action = Action(
project = project,
description = form.description.data,
statut_action = form.statut_action.data,
date_action = form.date_action.data,
date_execution = form.date_execution.data
)
try:
db.session.add(action)
db.session.commit()
flash('You have successfully added a new action.')
except:
flash('Error: action name already exists.')
return redirect(url_for('auth.list_projets'))
return render_template('admin/actions/action.html', action="Add", form=form,
title="ADD ACTION", project_name=project.name)
Using
Flask
Flask-sqlalchemy
Sqlalchemy
Jquery
Datatables (jquery plugin)
Jeditable (jquery plugin)
Consider this user class ( straight out of flask-sqlalchemy docs):
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
def __init__(self, username, email):
self.username = username
self.email = email
def __repr__(self):
return '<User %r>' % self.username
The datatables makes an ajax request and populates the table. Each td then is made editable in place with jeditable. As soon as a td is modified, jeditable makes a POST request to localhost/modify containing:
The row id(the same id from the user class)
The new modified value
The column of the table that was altered ( for the sake of argument let's assume that there are three columns id/username/email) (int)
Now, i'd like that in the function that handles localhost/modify i take the row id, make a user object and query the db for that specific row, see what property needs to be modified, modify it with the new value and commit to the db. Here's what i got so far:
#app.route('/modify', methods=['GET', 'POST'])
def modify()
if request.method == 'POST' :
user = user.query.get(form.row_id)
if form.column_id == int(0):
user.id = form.value
elif form.column_id == int(1):
user.username = form.value
elif form.column_id == int(2):
user.email = form.value
else:
pass
db.session.commit()
return 'ok'
This way, yes it does work but theremust be amore beautiful approach. This one doesn't seem very...pythonic
Mihai
Use a map of column ID to attribute name.
colmap = {
0: 'id',
1: 'username',
2: 'email',
}
#app.route('/modify', methods=['GET', 'POST'])
def modify()
if request.method == 'POST' :
user = user.query.get(form.row_id)
try:
setattr(user, colmap[form.column_id], form.value)
except KeyError:
pass
db.session.commit()
return 'ok'