Flask Rest API SQLAlchemy foreign key error - python

I got SQLALchemy error, when I tried "flask db migrate"
NoReferencedTableError: Foreign key associated with column 'user.menu_id' could not find table 'menu' with which to generate a foreign key to target column 'id
Menu table
class Menu(db.Model):
__tablename__ = 'menus'
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(64), index=True, unique=True)
price = db.Column(db.String(64), index=True, unique=True)
description = db.Column(db.String(64), index=True, unique=True)
picture = db.Column(db.String(64), index=True, unique=True)
create_date = db.Column(db.DateTime, default=datetime.utcnow)
users = db.relationship('User', backref="menu", lazy=True)
User table
class User(Model):
""" User model for storing user related data """
id = Column(db.Integer, primary_key=True)
email = Column(db.String(64), unique=True, index=True)
username = Column(db.String(15), unique=True, index=True)
name = Column(db.String(64))
password_hash = Column(db.String(128))
admin = Column(db.Boolean, default=False)
joined_date = Column(db.DateTime, default=datetime.utcnow)
userdataset = db.relationship("Dataset", backref="user", lazy="dynamic")
menu_id = Column(db.Integer(), db.ForeignKey('menu.id'), nullable=False)
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
How can ı solve this problem? Where am i doing wrong?

You have renamed your 'Menu' table to 'menus' with this __tablename__ property in your 'Menu' model:
__tablename__ = 'menus'
You then try to reference to the 'Menu' table, when in fact, its name has been changed to 'menus'. The simplest way to solve this would be to change your User.menu_id column to this:
menu_id = Column(db.Integer(), db.ForeignKey('menus.id'), nullable=False)
Another way of fixing this issue would be modifying the __tablename__ property to 'menu'. (You could also just delete it.)

Related

sqlalchemy InvalidRequestError

I'm using flask-sqlalchemy, this is not the first relations that i've built, but for some reason it gives me an error when i start flask:
sqlalchemy.exc.InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Triggering mapper: 'mapped class User->users'. Original exception was: 'Table' object has no attribute 'sender_id'
These are two models that i'm trying to connect via ForeignKeys:
User:
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
about_me = db.Column(db.String(140))
last_seen = db.Column(db.DateTime, default=datetime.utcnow)
#relations
posts = db.relationship('Post', back_populates='author', lazy='dynamic')
messages_sent = db.relationship('Message', foreign_keys='messages.sender_id',
back_populates='author', lazy='dynamic')
messages_received = db.relationship('Message', foreign_keys='messages.recipient_id',
back_populates='recipient', lazy='dynamic')
last_message_read_time = db.Column(db.DateTime)
followed = db.relationship(
'User', secondary=followers,
primaryjoin=(followers.c.follower_id == id),
secondaryjoin=(followers.c.followed_id == id),
backref=db.backref('followers', lazy='dynamic'), lazy='dynamic')
def __repr__(self):
return '<User {}>'.format(self.username)
And Messsage:
class Message(db.Model):
__tablename__ = 'messages'
id = db.Column(db.Integer, primary_key=True)
sender_id = db.Column(db.Integer, db.ForeignKey('users.id'))
recipient_id = db.Column(db.Integer, db.ForeignKey('users.id'))
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
#relations
author = db.relationship('User', back_populates='messages_sent', lazy='dynamic')
recipient = db.relationship('User', back_populates='messages_received', lazy='dynamic')
def __repr__(self):
return '<Message {}>'.format(self.body)
I've checked the database itself and it certainly has sender_id in the correct table, tried to change "foreign_keys" parameter to directly name of model Message.sender... Tried to change parameters of lazy on those relations. It still gives me same error.
This is subtle but messages in this case is a table so columns are referenced off of c, like messages.c.sender_id. To use the column of the model class (the mapped class) you would do Message.sender_id.
So...
messages_sent = db.relationship('Message', foreign_keys='messages.c.sender_id',
back_populates='author', lazy='dynamic')
# OR
messages_sent = db.relationship('Message', foreign_keys='Message.sender_id',
back_populates='author', lazy='dynamic')
There is some information here but it doesn't explain the table vs class situation: handling-multiple-join-paths I think I would just use ORM style references until you are more comfortable and then you could use table references if needed.

flask admin one to one inline_models

i have those 2 models:
#derive_schema
class Organization(db.Model):
id = Column(UUID(as_uuid=True), unique=True, primary_key=True, server_default=sqlalchemy.text("uuid_generate_v4()"))
name = Column(String, nullable=False, unique=True)
code = Column(String, nullable=False, unique=True)
owner_email = Column(String, nullable=False)
labels = Column(JSONB)
status = Column(Enum(OrganizationStatus), nullable=False)
logo_url = Column(String)
configuration = Column(JSONB, nullable=False)
def __repr__(self):
return self.name
#derive_schema
class PortalSettings(db.Model):
id = Column(UUID(as_uuid=True), unique=True, primary_key=True, server_default=sqlalchemy.text("uuid_generate_v4()"))
organization_id = db.Column(UUID(as_uuid=True), ForeignKey('organization.id'), nullable=False)
portal_settings = Column(JSONB)
organization = relationship(Organization, backref=backref('portal_settings', uselist=False, lazy="joined"))
def __repr__(self):
return self.portal_settings
and this ModelView
class OrganizationView(ConfigurationModelView):
inline_models = (PortalSettings,)
the relationship between organization and portal settings should be one to one,
but i dont understand why in flask admin i got this field when i can add as many portal settings as i want instead of just seen a input field with the portal_settings JSONB field
Based on this gist
https://gist.github.com/DrecDroid/398a05e4945805bc09d1
i've created PR onto Flask-Admin repo and maybe soon it will be merged. Anyway, you may copy-paste code from Gist and use it in your project
https://github.com/flask-admin/flask-admin/pull/2091

Many to Many relationship calling on Flask SQL Alchemy

I have the following badge (achievement) system database structure:
class Base(db.Model):
__abstract__ = True
id = db.Column(db.Integer, primary_key=True)
date_created = db.Column(db.DateTime, default=db.func.current_timestamp())
date_modified = db.Column(db.DateTime, default=db.func.current_timestamp(),
onupdate=db.func.current_timestamp())
class User(UserMixin, Base):
__tablename__ = 'users'
username = db.Column(db.String(20), nullable=False, unique=True)
email = db.Column(db.String(50), nullable=False, unique=True)
password = db.Column(db.String(200), nullable=False)
name = db.Column(db.String(30), nullable=False)
badges = db.relationship('UserBadge', backref='ubadge',
lazy='dynamic')
class Badge(Base):
__tablename__ = 'badges'
name = db.Column(db.String(35), unique=True)
description = db.Column(db.String(300))
imgfile = db.Column(db.String(80))
badges = db.relationship('UserBadge', backref='badge',
lazy='dynamic')
def __repr__(self):
return '<Achievement: {} - {}>'.format(self.name, self.description)
class UserBadge(Base):
__tablename__ = 'userbadges'
user_id = db.Column(db.Integer(), db.ForeignKey('users.id'))
badge_id = db.Column(db.Integer(), db.ForeignKey('badges.id'))
def __repr__(self):
return '<Achievement: {} - {}>'.format(self.user_id, self.badge_id)
So i can return all the badges by a specific user, using:
ubadges = UserBadge.query.filter_by(user_id=user.id).all()
It returns:
[<Achievement: 1 - 1>]
But instead of 1 (user_id) and 1 (badge_id) i want to show the users.name and badges.name. How can i access those attributes?
In your UserBadge class, just use:
def __repr__(self):
return '<Achievement: {} - {}>'.format(self.ubadge.name, self.badge.name)
It has both properties because you set them up using backref in the other classes.
PS: You might need to change the User backref to user, and then use self.user.name int he function above

Using SQLAlchemy, how can I make a field in a class that is a list of other instances of said class? [duplicate]

This question already has an answer here:
How to implement following/followers relationship in SQLAlchemy
(1 answer)
Closed 5 years ago.
I have a user class as detailed below:
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.Unicode(length=128), unique=True)
username = db.Column(db.Unicode(length=128), unique=True)
_password = db.Column("password", db.String(length=60))
admin = db.Column(db.Boolean, default=False)
joined = db.Column(db.DateTime, default=datetime.utcnow)
confirmed = db.Column(db.Boolean, default=False)
profile_picture = db.Column(db.Unicode(length=128), unique=True, nullable=True)
twitter = db.Column(db.Unicode(length=256), unique=True, nullable=True)
github = db.Column(db.Unicode(length=256), unique=True, nullable=True)
I would like to add another column to the user class which is a list of users. How can I accomplish this?
I think the proper name of what I am looking for is a self-referential one-to-many relationship.
Based on your comment you want to store a association table that stores which user follows which user(s). This is what is known as a many-to-many relation. Since a user can follow many other users, and a user can be followed by many users.
For that we need to define an additional table, and relationship(http://docs.sqlalchemy.org/en/latest/orm/basic_relationships.html#many-to-many) to specify the usage of that table, for instance:
class UserFollows(db.Model):
__tablename__ = 'user_follows'
follower = Column(Integer, ForeignKey('users.id'))
followee = Column(Integer, ForeignKey('users.id'))
Now we can define two virtual columns to the User class and specify that SQLAlchemy should look into the user_follows table for this:
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.Unicode(length=128), unique=True)
username = db.Column(db.Unicode(length=128), unique=True)
_password = db.Column("password", db.String(length=60))
admin = db.Column(db.Boolean, default=False)
joined = db.Column(db.DateTime, default=datetime.utcnow)
confirmed = db.Column(db.Boolean, default=False)
profile_picture = db.Column(db.Unicode(length=128), unique=True, nullable=True)
twitter = db.Column(db.Unicode(length=256), unique=True, nullable=True)
github = db.Column(db.Unicode(length=256), unique=True, nullable=True)
followers = db.relationship('User',
secondary = followers,
primaryjoin = (UserFollows.c.follower == id),
secondaryjoin = (followers.c.followee == id))
follows = db.relationship('User',
secondary = followers,
primaryjoin = (UserFollows.c.followee == id),
secondaryjoin = (followers.c.follower == id))
Now a User object has two attributes followers and follows which act as collections of users that store the persons the User follows as well as the followers of that User.

Unable to delete parent record in one to many relationship - Flask-SqlAlchemy

I've created to 3 simple model with flask-sqlalchemy with one to many relationship. Here is the code for models:
class UsersModel(BaseModel, UserMixin):
__tablename__ = 'user'
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
confirmed_at = db.Column(db.DateTime())
info = db.relationship('UserInfoModel', backref="user", cascade="all, delete" , lazy='dynamic')
notes = db.relationship('NotesModel', backref="owner", cascade="all, delete" , lazy='dynamic')
class UserInfoModel(db.Model):
__tablename__ = 'user_info'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
first_name = db.Column(db.String(55))
last_name = db.Column(db.String(55))
age = db.Column(db.Integer)
profession = db.Column(db.String(255))
class NotesModel(BaseModel):
__tablename__ = 'notes'
title = db.Column(db.String(255), nullable=False)
desc = db.Column(db.Text)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
I can insert and retrive relational data without any problem but when I'm trying to delete a user it should also delete notes & info rather it gives error and don't let me delete. Here is the error that I see: http://prntscr.com/ek5cx1
But if I delete notes & info and then try to delete user it works. It's doing the reverse. I tried using 'delete-orphan' but didn't worked. I have read the documentation and read some blog about it but nothing helps. Am I wrong about declaring the relation? If so please help me to implement this or help me to find error within my code.
Appriciate your help, Thanks
Update: After adding delete-orphan I can delete data from session but not form phpmyadmin.
i think you want your relationships defined opposite how you have them, so like this:
class UsersModel(BaseModel, UserMixin):
__tablename__ = 'user'
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
confirmed_at = db.Column(db.DateTime())
class UserInfoModel(db.Model):
__tablename__ = 'user_info'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
first_name = db.Column(db.String(55))
last_name = db.Column(db.String(55))
age = db.Column(db.Integer)
profession = db.Column(db.String(255))
user = db.relationship('User',uselist=False, cascade='all, delete-orphan',backref=db.backref('info', uselist=False))
class NotesModel(BaseModel):
__tablename__ = 'notes'
title = db.Column(db.String(255), nullable=False)
desc = db.Column(db.Text)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
user = db.relationship('User',uselist=False, cascade='all, delete-orphan',backref=db.backref('notes'.lazy='dynamic'))
use this way
cascade="all,delete"

Categories

Resources