I am trying to create a form using wtform datetime that accepts a date with a date picker.
form.py
class RegistrationForm(FlaskForm):
eventname = StringField('Event Name', validators=[DataRequired()])
eventstart = DateField(
'Enter start date and time', id='datepick', validators=[DataRequired()]
)
This renders the date and time picker correctly. However, I am not understanding how to get it into the correct format using sqlalchemy and sqlite. It doesn't seem to be validating on submit. When I add a print(events), the output is {}.
view.py
#events_blueprint.route('/add', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
print(form.errors)
if form.validate_on_submit():
event = Events(
eventname=form.eventname.data,
eventstart=form.eventstart.data,
# eventstop=form.eventstop.data,
)
db.session.add(event)
db.session.commit()
flash("Thank you for registering your event.")
return render_template('add.html', form=form)
class Events(db.Model):
__tablename__ = 'events'
id = db.Column(db.Integer, primary_key=True)
eventname = db.Column(db.Text(64), unique=True, index=True)
eventstart = db.Column(db.Text, nullable=False)
# This is a one-to-many connection with Eventdetails database
details = db.relationship('Eventdetails', backref='event', lazy='dynamic')
def __init__(
self, eventname, eventstart
):
self.eventname = eventname
self.eventstart = eventstart
def __repr__(self):
return f"{self.eventname}"
Any help pointing me in the right direction would be greatly appreciated. Ultimately, I would like to factor in the timezone and event end time so I have a time range.
Related
I am learning python and trying to create a drop-down in my form with the data from another table. here is my code
Models.py
class empmasterModel(db.Model):
__tablename__ = "empmaster"
Empnum = db.Column(db.Integer, primary_key=True)
Employee_Number = db.Column(db.Integer(),primary_key=True,unique = True)
Employee_Name = db.Column(db.String())
Employee_Type = db.Column(db.String())
Coreid = db.Column(db.String())
EmailId = db.Column(db.String())
def __init__(self, Employee_Number,Employee_Name,Employee_Type,Coreid,EmailId):
self.Employee_Number = Employee_Number
self.Employee_Name = Employee_Name
self.Employee_Type = Employee_Type
self.Coreid = Coreid
self.EmailId = EmailId
def __repr__(self):
return f"{self.Employee_Number}:{self.Employee_Name}:{self.Employee_Type}:{self.Coreid}:{self.EmailId}"
Above is my models.py code with class empmaster from which DB table I need the employee number and employee name.
app.py
#app.route('/component/add', methods=['GET', 'POST'])
def componentadd():
error_msg = ''
success_msg = ''
if request.method == 'GET':
empmaster_data = empmasterModel.query.all()
print(empmaster_data , "dbbbbbbbbbbbbb")
return render_template('component/add.html', empmaster_data=empmaster_data)
Above is the app.py code where I am trying to fetch data from empmaster table. But here I am getting whole table data but not two-column data. I tried two column names in brackets after the filter too but it did not work. I searched for many solutions but was not getting desired results. Can somebody help me?
If you have session management, you can do something like:
session.query(
empmasterModel.Employee_Number.label("Employee_Number"),
empmasterModel.Employee_Name.label("Employee_Name")
).all()
If you use Flask with no session management, you can get the session from the SQLAlchemy instance.
db = SQLAlchemy() # Usually in you app.py
And then:
db.session.query(
empmasterModel.Employee_Number.label("Employee_Number"),
empmasterModel.Employee_Name.label("Employee_Name")
).all()
This issue has confused me for a long time and I searched for a few days but still cannot get it resolved, including this, this, and this.
Below code returns a query object and it shows correctly in the select field.
But when submitting to database, the errors occurred.
# Query the user with Role.id == 4 as reviewer
def reviewer_choices():
return User.query.join(User.roles).filter(Role.id == 4)
# Build a select field
class ProjectView(sqla.ModelView):
form_extra_fields = {
'reviewer': sqla.fields.QuerySelectField(
query_factory=reviewer_choices,
)}
I tried to define __repr__ and __str__ in order to convert it to string but in vain, is there any other way to convert the query object to string? Thanks in advance.
1. __repr__:
The error returns:
sqlalchemy.exc.InterfaceError
InterfaceError:
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(255))
# ...
# ...
def __repr__(self):
return self.first_name
2. __str__:
The error returns:
sqlalchemy.exc.InterfaceError InterfaceError: (raised as a result of
Query-invoked autoflush; consider using a session.no_autoflush block
if this flush is occurring prematurely) (sqlite3.InterfaceError) Error
binding parameter 8 - probably unsupported type. [SQL: u'INSERT INTO
project
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(255))
# ...
# ...
def __str__(self):
return self.first_name
I currently used:
In the Project class
class Project(db.Model):
# ...
reviewer = db.Column(db.Unicode(128))
# ...
In the Project table
CREATE TABLE `project` (
# ...
`reviewer1` TEXT,
# ...
Assuming that having reviewer as string field in your Project model is intentional (as opposed to being a relationship).
A QuerySelectField data property stores an ORM instance, which in your case is an instance of a User model, whilst your reviewer field is a string, hence the error message.
You can create an inherited QuerySelectField class and override its populate_obj method to convert the selected User instance to a string of your choice, e.g.
class ProjectQuerySelectField(QuerySelectField):
def populate_obj(self, obj, name):
# obj is the current model being edited/created
# name is the field name - 'reviewer' in this instance
# self.data is the user instance selected in the form
setattr(obj, name, str(self.data))
Note the use of the str function to get the string representation of the selected user instance.
See self-contained sample app below. Navigate to http://127.0.0.1:5000/admin/project/ to see how a selected user gets converted to a string.
from flask import Flask
from flask_admin.contrib.sqla import ModelView
from flask_admin.contrib.sqla.fields import QuerySelectField
from flask_security import Security, SQLAlchemyUserDatastore, RoleMixin, UserMixin
from flask_sqlalchemy import SQLAlchemy
from flask_admin import Admin
# Create application
app = Flask(__name__)
# Create dummy secrey key so we can use sessions
app.config['SECRET_KEY'] = '123456790'
# Create in-memory database
app.config['DATABASE_FILE'] = 'sample_db.sqlite'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + app.config['DATABASE_FILE']
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
# Flask views
#app.route('/')
def index():
return 'Click me to get to Admin!'
# Define models
roles_users = db.Table('roles_users',
db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
db.Column('role_id', db.Integer(), db.ForeignKey('role.id')))
class Role(db.Model, RoleMixin):
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(255), unique=True)
description = db.Column(db.String(255))
def __str__(self):
return unicode(self).encode('utf-8')
def __unicode__(self):
return self.name
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(255))
last_name = db.Column(db.String(255))
email = db.Column(db.String(254), unique=True)
roles = db.relationship('Role', secondary=roles_users, backref=db.backref('users', lazy='dynamic'))
def __str__(self):
return unicode(self).encode('utf-8')
def __unicode__(self):
return ', '.join(filter(None, [self.first_name, self.last_name, self.email]))
class Project(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255))
reviewer = db.Column(db.Unicode(128))
def __str__(self):
return unicode(self).encode('utf-8')
def __unicode__(self):
return self.name
# Setup Flask-Security
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)
class UserView(ModelView):
column_list = ['first_name', 'last_name', 'email', 'roles']
form_columns = ['first_name', 'last_name', 'email', 'roles']
class RoleView(ModelView):
form_columns = ['name', 'description']
def reviewer_choices():
# return User.query.join(User.roles).filter(Role.id == 4)
return User.query.join(User.roles).filter(Role.name == u'Reviewer')
class ProjectQuerySelectField(QuerySelectField):
def populate_obj(self, obj, name):
setattr(obj, name, str(self.data))
class ProjectView(ModelView):
form_extra_fields = {
'reviewer': ProjectQuerySelectField(
query_factory=reviewer_choices,
)}
admin = Admin(app, template_mode="bootstrap3")
admin.add_view(UserView(User, db.session))
admin.add_view(RoleView(Role, db.session))
admin.add_view(ProjectView(Project, db.session))
def build_sample_db():
db.drop_all()
db.create_all()
# Reviewer will have id : 4 and will have index 3 in _roles list
_roles = []
for _name in ['Admin', 'Editor', 'Reader', 'Reviewer']:
_role = Role(name=_name)
_roles.append(_role)
db.session.add_all(_roles)
# get the "Reviewer" Role
_reviewer_role = _roles[3]
# Give Paul and Serge "Reviewer" role
_user_1 = User(first_name="Paul", last_name="Cunningham", email="paul#example.com", roles=[_reviewer_role])
_user_2 = User(first_name="Luke", last_name="Brown", email="luke#example.com")
_user_3 = User(first_name="Serge", last_name="Koval", email="serge#example.com", roles=[_reviewer_role])
db.session.add_all([_user_1, _user_2, _user_3])
db.session.commit()
if __name__ == '__main__':
build_sample_db()
app.run(port=5000, debug=True)
I am currently trying to insert items into my database. I am using SQLlite and SQLAlchemy with Flask but there seems to be an issue. Whenever I try to insert items manually from the cmd, I receive an error.
This session's transaction has been rolled back due to a previous
exception during flush.
I have implemented an one to many relationship in my database but something seems to keep messing up. Here is my Python code:
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_bootstrap import Bootstrap
main = Flask(__name__)
db = SQLAlchemy(main)
main.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://YYYYYYY:XXXXXXX#localhost/address'
main.config['SECRET_KEY'] = 'something-secret'
Bootstrap(main)
class Organisation(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(80), unique=True)
email = db.Column(db.String(40), unique=True)
number = db.Column(db.String(40), unique=True)
employees = db.relationship('Person', backref='employer', lazy='dynamic')
def __init__(self, title, email, number):
self.title = title
self.email = email
self.number = number
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(60), unique=False)
email = db.Column(db.String(40), unique=True)
mobile = db.Column(db.String(40), unique=True)
employer_id = db.Column(db.Integer, db.ForeignKey('organisation.id'))
def __init__(self, name, email, mobile, employer_id):
self.name = name
self.email = email
self.mobile = mobile
self.employer_id = employer_id
#main.route('/', methods=['GET'])
def index():
result = Person.query.all()
org_result = Organisation.query.all()
return render_template("index.html", result=result, org_result=org_result)
#main.route('/additems', methods=['GET'])
def additems():
return render_template('add.html')
#main.route('/add', methods=['GET', 'POST'])
def add():
person = Person(request.form['name'], request.form['email'], request.form['mobile'])
db.session.add(person)
db.session.commit()
if __name__ == "__main__":
main.run(debug=True)
If I have to honest, I think that my issue is somewhere in the init functions. I have tried changing them in several ways:
1.Adding employees as self.employees = employees and trying directly to input an Organisation as:
organisation_one=Organisation(title="XX",email="xx#mail.com",number="3838",employees=person_one) but it fired back an error even before I could submit person_one
2.I have tried referencing the employer_id in the Person __init__ file and when I try to add the organisation id, I recive an error "can't adapt type".
What am I doing wrong with the one to many database model? Can someone help me out?
Your database models require a __tablename__ attribute like this: This tells it what the actual table name is in the database. Otherwise SQLAlchemy doesn't know how to write the SQL for you.
class Organisation(db.Model):
__tablename__ = 'organisation'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(80), unique=True)
email = db.Column(db.String(40), unique=True)
number = db.Column(db.String(40), unique=True)
employees = db.relationship('Person', backref='employer', lazy='dynamic')
def __init__(self, title, email, number):
self.title = title
self.email = email
self.number = number
You must also reference this table name in the backref for your Person model:
db.ForeignKey('organisation.id')) # assuming "organisation" is the table name
Also, your /add route is incomplete and will result in an error:
#main.route('/add', methods=['GET', 'POST'])
def add():
person = Person(request.form['name'], request.form['email'], request.form['mobile'])
db.session.add(person)
db.session.commit()
# e.g. add some instruction here on what to do...
flash('Person %s <%s>added!' % (request.form['name'], request.form['email']))
return redirect(url_for('main.additems'))
I have been searching the internet for a while now in order to solve this issue.
I am working on a proof of concept, a simple input output data entry application.
However, the Form I am using, won't output my NoneType values as I am trying to use filters in order to do so, as illustrated in this stackoverflow thread : Get None from a Fields data in instead of an empty string
However the results are not as expected, as I still only receive Strings.
My Form
class MyForm(Form):
event_date = DateField('Event Date:', format='%Y-%m-%d', validators=[DataRequired()], filters=[lambda x: x or None])
person = StringField('Person', filters=[lambda x: x or None])
office = QuerySelectField(query_factory=get_all_offices, allow_blank=True,
filters=[lambda x: x or None])
event_type = QuerySelectField(query_factory=get_all_event_types, allow_blank=True, filters=[lambda x: x or None])
submit = SubmitField('Search')
Render
#app.route('/myform', methods=['GET', 'POST'])
def myform():
if request.method == 'GET':
form = MyForm()
return render_template('my_form.html', form=form)
else:
print(request.form)
context = {}
event_date = request.form['event_date']
office = request.form['office']
person = request.form['person']
event_type = request.form['event_type']
print(person)
print(event_date)
print(type(office))
print(event_type)
The Input
The Output
As you can see, office, and event type are coming through as a String Object, with a value of "__None".
Any ideas if it is possible to have the form handle the translation of '' or '__None' to the builtin NoneType ?
Edit: Schema for person / office
class Person(Base):
__tablename__ = 'people'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(64), nullable=False)
political_party = Column(String(64), nullable=False)
ap_candidate_id = Column(String(64), nullable=False)
def __repr__(self):
return self.name
class Office(Base):
__tablename__ = 'offices'
id = Column(Integer, primary_key=True, autoincrement=True)
desc = Column(String(64), nullable=False)
def __repr__(self):
return self.desc
class Event(Base):
__tablename__ = 'events'
id = Column(Integer, primary_key=True, autoincrement=True)
event_date = Column(DateTime, nullable=False)
office_id = Column(Integer, ForeignKey(Office.id))
office = relationship('Office', backref='events')
winner_id = Column(Integer, ForeignKey(Person.id))
winner = relationship('Person', foreign_keys=winner_id, backref='events_won')
loser_id = Column(Integer, ForeignKey(Person.id))
loser = relationship('Person', foreign_keys=loser_id, backref='events_lost')
event_type_id = Column(Integer, ForeignKey(EventType.id))
event_type = relationship('EventType', backref='events')
state_id = Column(Integer, ForeignKey(State.id))
state = relationship('State', backref='state_events')
def __repr__(self):
return '{0} - {1}'.format(self.office, self.event_date)
You are doing
print request.form['event_date']
so basically you are just printing data posted by the browser. You are not even using the MyForm class you created. You need to pass your request.form data to your MyForm class so that it may parse it. And later use your MyForm class instance.
You need to do something like
def myform():
if request.method == 'GET':
form = MyForm()
return render_template('my_form.html', form=form)
else:
form = MyForm(request.form)
if form.validate():
print form.event_date.data
print form.office.data
print form.person.data
print form.event_type.data
For more details, please go through docs about how to handle forms in views.
Your filters need to take into account that the value could be the string value "__None", currently your filters are just checking whether the value is absent or not. Something like the following should work (untested).
person = StringField('Person', filters=[lambda x: x or None, lambda x: None if x == '__NONE' else x])
I'm trying out Flask but I'm having the error sqlalchemy.exc.InterfaceError: <unprintable InterfaceError object> while submitting a wtforms. The model class is:
class Post(db.Model):
__tablename__ = 'blog_posts'
id = db.Column(db.Integer, unique=True, primary_key=True)
title = db.Column(db.String(50), unique=False)
content = db.Column(db.Text, unique=False)
user_id = db.Column(db.String, db.ForeignKey('users.username'))
#staticmethod
def post_new_entry(title, content, user_id):
""" Post new entry to database """
new_post = Post(title=title, content=content, user_id=user_id)
db.session.add(new_post)
db.session.commit()
return new_post
def __repr__(self):
return 'PostID {}: {} by {}'.format(self.id, self.title, self.user_id)
For my Form, I have the following:
class PostForm(Form):
title = StringField('Title', validators=[DataRequired(), Length(10, 65)])
post_content = TextAreaField('Content', validators=[DataRequired(), Length(50, 500)])
submit = SubmitField('Publish Post')
The route is:
#main.route('/new_post/', methods=['GET', 'POST'])
#login_required
def add_post():
form = PostForm()
if form.validate_on_submit():
Post.post_new_entry(title=form.title.data,
content=form.post_content.data,
user_id=current_user)
flash("Amazing stuff! Thanks for your submission.")
return redirect(url_for('main.index'))
return render_template('single.html', form=form)
On my html, I'm importing the wtf.html page of the flask-bootstrap:
{{ wtf.quick_form(form) }}
The form shows right but I get the above error on form submission. Any tip or idea on how to proceed would be helpful.
Under def add_post() you write user_id=current_user, but that's not right.
Since you defined for class Post:
user_id = db.Column(db.String, db.ForeignKey('users.username'))
in def add_post() you should use user_id=current_user.username.
In your table class definition you need to add one more line to complete the foreign key relationship.
class Post(db.Model):
__tablename__ = 'blog_posts'
id = db.Column(db.Integer, unique=True, primary_key=True)
title = db.Column(db.String(50), unique=False)
content = db.Column(db.Text, unique=False)
user_id = db.Column(db.String, db.ForeignKey('users.username'))
# Setup the relationship to the User table
users = db.relationship(User)
I was having the same error message in an app which was working one day then not the next. Drove me nuts, the solution was that I had removed a relationship() somewhere.
I have received a similar message when writing data from my application to a database. This is due to the fact that the data that is written from the application needs to have the same format as a defined in the database a db.Column(db.String()) data type cannot have a list as input for example, or any other form.data. You need to use ``str()``` in these cases to prevent this error.
I think your problem came from this area:
Post.post_new_entry(title=form.title.data, content=form.post_content.data, user_id=current_user)
Try to be specific and do it this way:
Post.post_new_entry(title=form.title.data, content=form.post_content.data, user_id=current_user.id)