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)
Related
This question already has an answer here:
IntegrityError when inserting data in an association table using SQLAlchemy
(1 answer)
Closed last year.
I'm creating a web app with Flask and Flask-SQLAlchemy. I'm trying to model a many to many relationship between Users and Products with an association table called UserProduct which I've mapped to a class.
This is the error that I'm getting when I try to post data to my flask app:
sqlalchemy.exc.IntegrityError: (raised as a result of Query-invoked autoflush; consider
using a session.no_autoflush block if this flush is occurring prematurely)
(sqlite3.IntegrityError) NOT NULL constraint failed:
user_product.user_id [SQL: INSERT INTO user_product (product_id, price_cutoff)
VALUES (?, ?)] [parameters: (2, 15.0)]
models.py:
#login_manager.user_loader
def user_loader(user_id):
return(User.query.get(int(user_id)))
class UserProduct(db.Model):
user_id = db.Column(db.ForeignKey('user.id'), primary_key=True)
product_id = db.Column(db.ForeignKey('product.id'), primary_key=True)
price_cutoff = db.Column(db.Integer, nullable=False)
user = db.relationship('User', backref='products')
product = db.relationship('Product', backref='users')
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
def __repr__(self):
return self.username
class Product(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20), nullable=False)
price = db.Column(db.Float, nullable=False)
url = db.Column(db.String(500), unique=True, nullable=False)
image_file = db.Column(db.String(20), nullable=False, server_default='unavailable.png')
routes.py:
#app.route('/home', methods=['GET' 'POST'])
def index():
form = ProductInfoForm()
if form.validate_on_submit():
product = Product.query.filter_by(url=form.url.data).first()
if not product:
product = Product(name='TestName', price=10.2, url=form.url.data)
db.session.add(product)
a = UserProduct(price_cutoff=float(form.price_cutoff.data))
a.product = product
current_user.products.append(a)
db.session.add(a)
db.session.commit()
return render_template('index.html', form=form)
However, everything goes as expected and my UserProduct table does get populated when I test with some dummy data in the command line:
>>> url = 'https://www.example.com/'
>>> product = Product.query.filter_by(url=url).first()
>>> if not product:
... product = Product(name='TestName', price=10.2, url=url)
... db.session.add(product)
...
>>> a = UserProduct(price_cutoff=15)
>>> a.product = product
>>> u = User(username='testuser', email='testemail', password='testpassword')
>>> u.products.append(a)
>>> db.session.add(a)
>>> db.session.commit()
I can't figure out what I'm doing wrong. The only difference that I see between my routes.py code and my command line code is that instead of using current_user (which I've imported from the Flask-login module), I manually created a dummy User object. However, I've double checked in the flask debugger and I'm pretty sure current_user exists and is working as expected.
Any help is greatly appreciated!
The type of price_cutoff you put in was Float, and it expects Int
a = UserProduct(price_cutoff=float(form.price_cutoff.data))
I'm new to using Flask, so apologies for what might be a basic question.
I'm working on a new Flask application that has multiple tables in a database. There is a table for storing the users information (name, password, enabled). Another table holds the group names and if it is an admin group (type is boolean). A third table relates the user to the group(s) they are a member of, since they could be a member of more than one. Here is the relevant code from models.py
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(10), index=True, unique=True)
password_hash = db.Column(db.String(128))
active_user = db.Column(db.Boolean)
group_ids = db.relationship('GroupMembers', backref='user', lazy='dynamic' )
def __repr__(self):
return 'User {} | Active {}'.format(self.username, self.active_user)
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
def check_admin(self):
group_ids = self.group_ids.all()
for row in group_ids:
group = Group.query.get(int(row.gid))
if group.admin_rights:
return True
return False
class Group(db.Model):
gid = db.Column(db.Integer, primary_key=True)
groupname = db.Column(db.String(60), index=True, unique=True)
admin_rights = db.Column(db.Boolean)
group_members = db.relationship('GroupMembers', backref='members', lazy='dynamic')
def __repr__(self):
return 'ID: {} | Name: {} | Admin: {}'.format(self.gid, self.groupname, self.admin_rights)
def is_admin_group(self, gid):
_group = self.query.get(int(gid))
return _group.admin_rights
class GroupMembers(db.Model):
__table_args__ = ( db.UniqueConstraint('gid', 'uid'), )
id = db.Column(db.Integer, prmary_key=True)
gid = db.Column(db.Integer, primary_key=True, db.ForeignKey('group.gid'))
uid = db.Column(db.Integer, primary_key=True, db.ForeignKey('user.id'))
def __repr__(self):
return "{}.{}".format(self.gid, self.uid)
I'm trying to find the best way to determine if a user is a member of a admin group (group.admin_rights == True). The 'check_admin' function in 'User' works, but I'm unsure of how to call this from within the html templates. Could this be called using something like
{% if current_user.check_admin() %}
assuming that your check_admin() is working fine, you can pass the additional check_admin flag like this
return render_template('sample_page.html', admin=check_admin())
and in the Jinja template, you can simply check for an admin user with:
{% if admin %}
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 am trying to create a blog using Flask. Every post can have multiple tags also every tag could be associated with multiple posts. So I created a many-to-many relationship. My questions is how do i save multiple tags when creating a new post. And since every post can have different number of tags how do i show this is in the form? Also, how can i create new tags along with the post and then use those tags with other posts?
This is models.py -
postcategory = db.Table('tags',
db.Column('posts_id', db.Integer, db.ForeignKey('posts.id')),
db.Column('categories_id', db.Integer, db.ForeignKey('categories.id'))
)
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
content = db.Column(db.Text)
slug = db.Column(db.String, unique=True)
published = db.Column(db.Boolean, index=True)
timestamp = db.Column(db.DateTime, index=True)
categories = db.relationship('Category', secondary=postcategory, backref='posts' )
def __init__(self, title, content):
self.title = title
self.content = content
class Category(db.Model):
__tablename__ = 'categories'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String, index=True)
This is the view i am working on -
def create_article():
if request.method == 'POST':
if request.form.get('title') and request.form.get('content') and request.form.get('slug') and request.form.get('published'):
post = Post(request.form['title'], request.form['content'], request.form['slug'], request.form['published'])
I am sure there is a easy solution and i am just complicating this, but i am new to web development, so please help.
You can pull the categories out of the form with getlist and add them to the Post object. If you have checkboxes like the following:
<form>
<input type="checkbox" name="categories" value="foo">
<input type="checkbox" name="categories" value="bar" checked>
</form>
In your view method you can just do:
categories_from_form = request.form.getlist('categories') # ['bar']
# create Category objects with the form data
categories = [Category(title=title) for title in categories_from_form]
post = Post(request.form['title'], request.form['content'], request.form['slug'], request.form['published'])
post.categories = categories # attach the Category objects to the post
...
I have models:
class Post(db.Model):
id = db.Column(db.Integer, primary_key = True)
body = db.Column(db.String(2000))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
comments = db.relationship('Comment', backref='parent_post', lazy='dynamic')
class Comment(db.Model):
id = db.Column(db.Integer, primary_key = True)
body = db.Column(db.String(140))
post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
When entering a post to database I do this:
if form.validate_on_submit():
post = Post(body=form.post.data, author=g.user)
db.session.add(post)
db.session.commit()
This is working right.
But how can I enter a comment to database if I want to pass the 'post.id' value directly
instead of object 'post'. (Not able to 'pass' object via form in html)
if form.validate_on_submit():
comment = Comment(body=form.post.data, parent_post=form.p_id.data)
db.session.add(comment)
db.session.commit()
currently p_id holds value post.id and it gives me error:
AttributeError: 'int' object has no attribute '_sa_instance_state'
Comment.parent_post is a relationship, backed by the integer column Comment.post_id. Currently, you are trying to assign an int (from form.p_id) too the relationship. Assign an int to the column or a Post instance to the relationship.
comment = Comment(post_id=form.p_id.data, body=form.post.data)
# or
post = Post.query.get_or_404(form.p_id.data)
comment = Comment(parent_post=post, body=form.post.data)
The second way is preferable, because you validate that a post with the id exists before trying to use the id.
if form.validate_on_submit():
comment = Comment(body=form.post.data, parent_post=form.p_id.data)
### you need to add `comment` instead `post` in the line below
db.session.add(comment)
db.session.commit()
I strongly think that an error was because of the db.session.add(post). Please the line db.session.add(post) with db.session.add(comment) while adding the comments.
Please make sure that POST-ID that being passed through the form is existing in the post table.