How to have two relationships in one table SQLAlchemy - python

I'm struggling to configure my relationships for this database. I can't quite get my head around these relationships and foreign keys and how it all fits together.
I'm trying to have a user who can have multiple posts and multiple projects, but get "primaryjoin" errors ever since I had more than one relationship in the User class.
I would appreaciate any help, I know there have been similar questions asked but I can't seem to map those solutions to my own application.
Here is my models.py file:
from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin
from uuid import uuid4
import datetime
from api import app
db = SQLAlchemy(app)
def get_uuid():
return uuid4().hex
today = datetime.date.today()
next_week = today + datetime.timedelta(days=7)
class User(UserMixin, db.Model):
__tablename__ = "users"
id = db.Column(db.String(32), primary_key=True, unique=True, default=get_uuid)
name = db.Column(db.String(60))
email = db.Column(db.String(345), unique=True)
password = db.Column(db.Text, nullable=False)
projects = db.relationship("Project", backref="owner", primaryjoin="users.id == projects.owner_id")
posts = db.relationship("Post", backref="author", primaryjoin="users.id == posts.author_id")
class Project(db.Model):
__tablename__ = "projects"
id = db.Column(db.Integer, primary_key=True)
project_client = db.Column(db.String(60))
word_count = db.Column(db.Integer, nullable=False)
deadline = db.Column(db.DateTime, default=next_week)
completed = db.Column(db.Boolean, default=False)
owner_id = db.Column(db.String, db.ForeignKey("users.id"))
class Post(db.Model):
__tablename__ = "posts"
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(80))
views = db.Column(db.Integer)
author_id = db.Column(db.String, db.ForeignKey("users.id"))

Related

SQLAlchemy and Flask: NoReferencedTableError: Could not find table to associate with column

I am currently trying to learn Flask and SQLAlchemy off of a book, and I've gotten to a stage where I need to migrate the database (using flask db migrate).
I keep on running into this error:
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'recip
e.user_id' could not find table 'user' with which to generate a foreign key to t
arget column 'id'
I've looked all over SO and found a lot of answers using __tablename__ which I've already tried.
This is the code:
In recipe.py:
from extensions import db
recipe_list = []
def get_last_id():
if recipe_list:
last_recipe = recipe_list[-1]
else:
return 1
return last_recipe.id + 1
class Recipe(db.Model):
__tablename__ = 'recipe'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
description = db.Column(db.String(200))
num_of_servings = db.Column(db.Integer)
cook_time = db.Column(db.Integer)
directions = db.Column(db.String(1000))
is_publish = db.Column(db.Boolean(), default=False)
created_at = db.Column(db.DateTime(), nullable=False, server_default=db.func.now())
updated_at = db.Column(db.DateTime(), nullable=False, server_default=db.func.now(), onupdate=db.func.now())
user_id = db.Column(db.Integer(), db.ForeignKey("user.id"))
In users.py:
from extensions import db
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), nullable=False, unique=True)
email = db.Column(db.String(200), nullable=False, unique=True)
password = db.Column(db.String(200))
is_active = db.Column(db.Boolean(), default=False)
created_at = db.Column(db.DateTime(), nullable=False, server_default=db.func.now())
updated_at = db.Column(db.DateTime(), nullable=False, server_default=db.func.now(), onupdate=db.func.now())
recipes = db.relationship('Recipe', backref='user')
#classmethod
def get_by_username(cls, username):
return cls.query.filter_by(username=username).first()
#classmethod
def get_by_email(cls, email):
return cls.query.filter_by(email=email).first()
def save(self):
db.session.add(self)
db.session.commit()
Edit
It was because my models were in two different files like JustCarlos commented. In order to solve the problem, I just imported both the models into my app.py.
from flask import Flask
from flask_migrate import Migrate
from flask_restful import Api
from Config import Config
from extensions import db
from models.recipe import Recipe
from models.user import User
from resources.recipe import RecipeListResource, RecipeResource, RecipePublic
def register_extensions(app):
db.init_app(app)
migrate = Migrate(app, db)
def register_resources(app):
api = Api(app)
api.add_resource(RecipeListResource, '/recipes')
api.add_resource(RecipeResource, '/recipes/<int:id>')
api.add_resource(RecipePublic, '/recipes/<int:id>/publish')
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
register_extensions(app)
register_resources(app)
return app
if __name__ == '__main__':
app = create_app()
app.run(port=5000, debug=True)

How do I ensure that the user can follow of multiple contents like topic, user

I have a models like:
from app import db
from marshmallow_sqlalchemy import SQLAlchemyAutoSchema, fields
from datetime import datetime
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, db.ForeignKey('follower.followable_id'), primary_key=True)
username = db.Column(db.String(32), nullable=False)
email = db.Column(db.String(320), nullable=False)
password_hash = db.Column(db.String(128), nullable=False)
password_salt = db.Column(db.String(22), nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow())
user_comment = db.relationship('UserComment', back_populates='author')
def to_json(self):
return {'username': self.username, 'email': self.email}
def get_id(self):
return self.id
class Topic(db.Model):
__tablename__ = 'topic'
id = db.Column(db.Integer, db.ForeignKey('follower.followable_id'), primary_key=True)
topic_name = db.Column(db.String(100))
class Follower(db.Model):
__tablename__ = 'follower'
id = db.Column(db.Integer, primary_key = True)
follower_id = db.Column(db.Integer, db.ForeignKey('user.id'))
followable_id = db.Column(db.Integer, db.ForeignKey('followable.followable_id'))
My goal is for the user to be able to follow topics and users. I tried to connect the id column of the user and topic table to the followable_id column of the follower table for this. And I linked the follower_id of the follower table to the user id.
In this case, I received an error when creating a topic table. (sqlalchemy.greetings.OperationalError: (pymysql.mistake.OperationalError) (1822, "Foreign key restriction could not be added. The missing index for the 'topic_ibfk_1' constraint in the referenced table is 'follower'")
https://stackoverflow.com/a/11618048/13975329 I took a reference from here and tried to create these tables but I couldn't.
How can I create a better design for user-topic follow mechanism and why does it give this error in topic table creation?

Querying data with nested relationships

I have a user table that has a relationship to my accounts table. My accounts table has a relationship to the account_permission table which is used to define permissions for accounts.
A user may have multiple accounts and he may have permissions to view his own accounts or other user's accounts.
I'm not sure how to query all the accounts that a user has and the accounts that he has permissions to view.
I thought that Account.query.filter_by(owner_id = user).all() would include permissions in it's results since it has a db relationship with a backref to the account_permission table but that is not the case.
# querying information
current_user = get_jwt_identity()
user = User.query.filter_by(email = current_user).first()
accounts = Account.query.filter_by(owner_id = user).all() # how do I get all the accounts that the user has permissions to?
# user model
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True, nullable=False)
password = db.Column(db.String(255), nullable=False)
isAdmin = db.Column(db.Boolean, default=False, nullable=False)
accounts = db.relationship('Account', backref='owner')
# accounts model
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from app import db, ma
class Account(db.Model):
account_id = db.Column(db.Integer, primary_key=True)
description = db.Column(db.String(255), nullable=False)
owner_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
permissions = db.relationship('AccountPermission', backref='permissions')
def __init__(self, account_id, description, owner_id):
self.account_id = account_id
self.description = description
self.owner_id = owner_id
class AccountSchema(ma.Schema):
class Meta:
fields = ('account_id', 'description', 'owner_id')
ordered = True
class AccountPermission(db.Model):
id = db.Column(db.Integer, primary_key=True)
account = db.Column(db.Integer, db.ForeignKey('account.account_id'))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
class AccountPermissionSchema(ma.Schema):
class Meta:
fields = ('account', 'user_id')
ordered = True

Use Flask-SqlAlchemy to query relationship database

I'm trying to use Flask-SQLAlchemy to query out database for the user profile page
So far I don't have a solution for this problem, only able to query all the User data by using users.query.all()
Each user has their own role_id, department_id, researchfield_id.
How can i query out all the Role, Department, ResearchField data that has relationship with User through ID?
class User(UserMixin, db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
password_hash = db.Column(db.String(128))
is_admin = db.Column(db.Boolean, default=False)
department_id = db.Column(db.Integer, db.ForeignKey('departments.id'))
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
research_id = db.Column(db.Integer, db.ForeignKey('researchfields.id'))
class Department(db.Model):
__tablename__ = "departments"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(sqlalchemy.types.NVARCHAR(100), unique=True)
user = db.relationship('User', backref='department',
lazy='dynamic')
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(sqlalchemy.types.NVARCHAR(100), unique=True)
users = db.relationship('User', backref='role',
lazy='dynamic')
class ResearchField(db.Model):
__tablename__ = "researchfields"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(60), index=True)
parent_id = db.Column(db.Integer, db.ForeignKey("researchfields.id") , nullable=True)
users = db.relationship('User', backref='researchfield', lazy='dynamic')
If I understand correctly, what you're seeking for is a way to filter out users based on a specific model. Because in your example, the other way around is redundant - every user has only one department, so no need to filter out departments for that user. In order to achieve that, I would use the backref method provided by SQLAlchemy from the User model.
Here's an example consisting of two of the models:
from sqlalchemy.orm import backref
class User(UserMixin, db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
password_hash = db.Column(db.String(128))
is_admin = db.Column(db.Boolean, default=False)
department_id = db.Column(db.Integer, db.ForeignKey('departments.id'))
department = db.relationship("Department", backref=backref("users", lazy="dynamic"))
class Department(db.Model):
__tablename__ = "departments"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(sqlalchemy.types.NVARCHAR(100), unique=True)
Now you can use:
department = Department.query.filter_by(id=1).first()
print(department.users.filter_by(is_admin=True).all()) # get all admins with that department
Every user has only one department, so you could just get the user's department by:
user = User.query.filter_by(id=1).first()
print(user.department) # prints Department object

One to Many Self-Referencing relation SQLAlchemy

I am pretty new in python (less than a day!).
I am trying to build a Restful API using Flask. I have the class class TaskModel(db.Model): which has a self-referencing relation to the task.
Actually, a task can have many predecessors (dependsOn field).
But when I add the relation below I got this error:
sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship TaskModel.dependsOn - there are no foreign keys linking these tables.
My whole Task class is below:
class TaskModel(db.Model):
"""
Task Model
"""
# table name
__tablename__ = 'tasks'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128), nullable=False)
department = db.Column(db.String(128), nullable=False)
earliestStart = db.Column(db.String(10), nullable=False)
latestEnd = db.Column(db.String(10), nullable=False)
duration = db.Column(db.String(10), nullable=False)
dnetworkId = db.Column(db.Integer, db.ForeignKey('dnetworks.id'), nullable=False)
dependsOn = db.relationship('TaskModel', backref='tasks',remote_side=[id], lazy=True)
....
class TaskSchema(Schema):
"""
Task Schema
"""
id = fields.Int(dump_only=True)
name = fields.Str(required=True)
department = fields.Str(required=True)
earliestStart = fields.Str(required=True)
latestEnd = fields.Str(required=True)
dnetworkId = fields.Int(required=True)
duration = fields.Str(required=True)
dependsOn = fields.Nested('self', many=True, exclude=('dependsOn',))
# dependsOn = fields.Nested('self', exclude=('dependsOn',), default=None, many=True)
Thanks in advance :)
In SqlAlchemy for relationship you should define both way, following is a complete working example of what you may looking for:
import os
from sqlalchemy.orm import relationship, backref
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////{}/my.db'.format(BASE_DIR)
db = SQLAlchemy(app)
class TaskModel(db.Model):
__tablename__ = 'tasks'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128), nullable=True)
parent_id = db.Column(db.Integer, db.ForeignKey("tasks.id"))
children = relationship("TaskModel",
backref=backref('parent', remote_side=[id])
)
if __name__ == "__main__":
db.create_all()
tm = TaskModel(name='Test1')
db.session.add(tm)
db.session.commit()
Attention to parent_id and children, these combination of two will give you what you needed, a self relationship on tasks, on database a parent_id will be added to your table.

Categories