I am working on a Flask app, using Flask-SQLAlchemy extension for database interactions. Since I have multiple apps writing on the same DB, I was getting concurrency issues with SQLite and I wanted to switch to PostgreSQL instead. I am able to create the tables on new database without a problem and pgAdmin displays the tables and columns.
# working
def createTables():
with app.app_context():
from models import User, Invoice
db.create_all()
But when it comes to adding a user, I am now getting an error: sqlalchemy.exc.NoForeignKeysError Although, I think, I declared one-to-many relationship in my models, based on the documentation, I get an error states that "there are no foreign keys linking these tables."
# not working
def create_test_user():
with app.app_context():
user = User(
username="Bob",
email="bob#email.com",
password="test"
)
db.session.add(user)
db.session.commit()
The full error message:
""" NoForeignKeysError: Could not determine join condition between parent/child tables on relationship User.invoices
- there are no foreign keys linking these tables.
Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression. """
I can't figure out what causes the error. What is missing with my models?
# models.py
class User(db.Model):
__tablename__ = "user"
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)
invoices = db.relationship('Invoice', backref='user', lazy=True)
class Invoice(db.Model):
__tablename__ = "invoice"
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
amount = db.Column(db.Integer, nullable=False)
Solved
Your code works for me. Maybe you need to re-create your tables or something similar. To be sure that we have the identical code: I have tested the following code:
class User(db.Model):
__tablename__ = "user"
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)
invoices = db.relationship('Invoice', backref='user', lazy=True)
class Invoice(db.Model):
__tablename__ = "invoice"
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
amount = db.Column(db.Integer, nullable=False)
In the route:
user = User(
username="Bob",
email="bob#email.com",
password="test"
)
db.session.add(user)
db.session.commit()
print(user)
I finally solved the problem and it was not where I was looking for. I was getting NoForeignKeysError due to importing a wrong model file during initializing the app. One of my imported modules was calling a wrong/old version of the model. It was causing the table relationship in the actual model to break I guess.
When I went through step by step create_test_user() I noticed that the error occurs actually during the class creation, before even it hits to db.session.add and I replicated the error even without a DB. I went through all my modules that are calling the models and caught wrong model import.
Related
I´m developing a python app which uses Flask-SQLAlchemy and it´s ORM Mapper.
I´ve got two tables with a Many To One relation. The main problem is that I want to load the content of both Objects with one join query and not later, when I access the object of the child table.
I already tried to get these behavior by using the joinedload option like this:
Event.query.filter(Event.timestamp == day)
.join(Event.user)
.options(joinedload(Event.user))
.all()
Also tried to set the lazy="joined" attribute in the entity-class for the relationship, which caused no difference.
The SQL Query looks right and the join is correctly generated, but if I access the user attribute later, another join query is sent. With other strategies it´s also not working, like the contains_eager option.
So the expected behavior would be, to save all information on the first load and don´t execute a query later on.
All SQLAlchemy options are default, except the DATABASE_URI. Is there any global option to disable this behavior or to override the default?
The Entities are the following:
class Event(db.Model):
__tablename__ = "event"
__table_args__ = (
db.ForeignKeyConstraint(
["username", "userfirstname"], ["users.name", "users.firstname"]
),
)
timestamp = db.Column(db.Date, primary_key=True, index=True)
username= db.Column(db.String, primary_key=True)
userfirstname= db.Column(db.String, primary_key=True)
...
user = db.relationship("UserEntity")
class UserEntity(db.Model):
__tablename__ = "users"
name= db.Column(db.String, primary_key=True)
firstname= db.Column(db.String, primary_key=True)
...
try this
class Event(db.Model):
__tablename__ = "event"
__table_args__ = (
db.ForeignKeyConstraint(
["username", "userfirstname"], ["users.name", "users.firstname"]
),
)
timestamp = db.Column(db.Date, primary_key=True, index=True)
username= db.Column(db.String, primary_key=True)
userfirstname= db.Column(db.String, primary_key=True)
user = db.relationship("UserEntity", back_populates="events")
user_id = Column(Integer, ForeignKey('user.id'))
class UserEntity(db.Model):
__tablename__ = "users"
name= db.Column(db.String, primary_key=True)
firstname= db.Column(db.String, primary_key=True)
events = relationship(MyOtherClass, lazy='joined')
from sqlalchemy docs:
joined applies a JOIN to the given SELECT statement so that related rows are loaded in the same result set. Joined eager loading is detailed at Joined Eager Loading.
I´ve found the mistake. The basic idea of disabling lazyloading was right and worked fine. I just messed up the object refs on the validation step and destroyed the SQLAlchemy background logic.
I am new to flask and SQLite and have very little knowledge of how they work.
Models.py
from datetime import datetime
from flaskblog import db, login_manager
from flask_login import UserMixin
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
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)
posts = db.relationship('Post', backref='author', lazy=True)
image_file = db.Column(db.String(20), nullable=False, default='default.jpg')
password = db.Column(db.String(60), nullable=False)
def __repr__(self):
return f"User('{self.username}', '{self.email}', '{self.image_file}')"
class PostIntmath(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
content = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
def __repr__(self):
return f"Post('{self.title}', '{self.date_posted}')"
These are the models I have defined.
I don't know why this is wrong but it gives me InvalidRequestErrors.
sqlalchemy.exc.InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Triggering mapper: 'mapped class User->user'. Original exception was: When initializing mapper mapped class User->user, expression 'Post' failed to locate a name ("name 'Post' is not defined"). If this is a class name, consider adding this relationship() to the <class 'flaskblog.models.User'> class after both dependent classes have been defined.
My full project is at https://github.com/MGeureka/flask_web_app
HELP!!!
When declaring a relationship, you must use the class name exactly as it appears in the class definition.
The reason SQL Alchemy can't find 'Post' is because the name you used is 'PostIntmath' instead.
Change this line
posts = db.relationship('Post', backref='author', lazy=True)
to
posts = db.relationship('PostIntmath', backref='author', lazy=True)
And your error will go away.
SQL alchemy allows you to define the mapped relationship class with a string in this way so that these classes can be in separate files and still work without needing imports. Behind the scenes, the library looks for all the tables associated with the db object and attempts to find one that matches the relationship string name you've supplied.
I have three models: Role, User, and Post. Role -> User is one to many, and User -> Post is one to many. When I just had User and Post, everything worked. However, my website will need varying degrees of authorization.
class Role(db.Model):
id = db.Column(db.Integer, primary_key=True)
users = db.relationship('User', backref='role', lazy=True)
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
role_id = db.Column(db.Integer, db.ForeignKey('role.id'), nullable=False)
posts = db.relationship('Post', backref='author', lazy=True)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
Here is the error message I get:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError)
no such column: user.role_id
Thanks!
You don't need to explicitly define the role_id field - the relationship defined in Role (and the backref parameter you specified) has already created a backwards relationship on User objects for you. (The SQLAlchemy documentation around backrefs may be helpful to you.)
As such, if you have an instantiated User object u, you should be able to get details about the user's role via u.role (which will give you a Role instance) and the ID of the role as u.role.id.
Thus, your full set of model definitions only needs to look like this:
class Role(db.Model):
id = db.Column(db.Integer, primary_key=True)
users = db.relationship('User', backref='role', lazy=True)
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
posts = db.relationship('Post', backref='author', lazy=True)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
If you do this, then for a Post object p, you can get the id of the role of the author of the post as p.author.role.id (for example).
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(64), unique=True)
# 是不是应该加密下,不能明文存储?应该设置多长的空间? 14.7.18 4:22 by lee
password = db.Column(db.String(100))
nickname = db.Column(db.String(64))
school = db.Column(db.String(20))
sex = db.Column(db.String(5))
status = db.Column(db.String(10))
grade = db.Column(db.String(18))
I have a database remains. Then I add model to models.py:
class PubSquare(db.Model):
id = db.Column(db.Integer, primary_key=True)
author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
author = db.relationship('User', backref=db.backref('publish'))
subject = db.Column(db.String(100))
timestamp = db.Column(db.DateTime, default=datetime.datetime.now)
Then I run migrate script, it call bug:
NoReferencedTableError: Foreign key associated with column 'pub_square.author_id' could not find table 'user' with which to generate a foreign key to target column 'id'
Befor this time, I can run migrate script successfully for serveral times.But this time, when it refer to foreignkey relationship, it doesn't work.
to prove my models code is right, I re-create the database, it works.
So, it's the flask-migrate calls to this bug.
#knight We have migrated for many times.'user' table is in the database. But I found that if I
code like
author_id = db.Column(db.Integer)
and migrate, It's nothing wrong. And than add the code like
author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
and migrate again, It passed. It's strange. I don't know why exactly.
Our migrate code is
api.update_db_from_model(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, db.metadata)
From what I can see the user table is not being created (could not find table 'user' with which to generate a foreign key to target column 'id'). Try migrating the User first and, therefore, making the user table, and then doing the PubSquare.
EDIT: Have you tried reading the docs? http://sqlalchemy-migrate.readthedocs.org/en/v0.7.1/changeset.html#constraint seems to help.
I am learning python and using the framework pyramid with sqlalchemy as the orm. I can not figure out how relationships work. I have 2 tables, offices and users. the foreign key is on the users table 'offices_id'. I am trying to do a query that will return to me what office a user is a part of.
This is how I have my models set up.
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(Unicode(255), unique=True, nullable=False)
trec_number = Column(Unicode(255), unique=True, nullable=False)
office_id = Column(Integer, ForeignKey('offices.id'))
class Office(Base):
__tablename__ = 'offices'
id = Column(Integer, primary_key=True)
name = Column(Unicode(255), unique=True, nullable=False)
address = Column(Unicode(255), unique=True, nullable=False)
members = relationship("User", backref='offices')
In my view how would I write a query that would return the the office information for a given user?
I am trying this:
for user in DBSession.query(User).join(Office).all():
print user.address
but I think I am misunderstanding how the queries work because I keep getting errors
AttributeError: 'User' object has no attribute 'address'
when I do this:
for user in DBSession.query(User).join(Office).all():
print user.name
it prints out the users name fine since name is an attribute of the User class.
I also can not get the inverse to work
for offices in DBSession.query(Office).join(User).all():
print offices.users.name
You need to use the name you used in the backref argument to access the Office model. Try user.offices.address