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.
Related
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"))
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)
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?
I have a basic many to many relationship as below,
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
user_favs = relationship('Loop', secondary='active_association')
def __repr__(self):
return '<User {}>'.format(self.username)
class Loop(db.Model):
id = db.Column(db.Integer, primary_key=True)
creation_date = db.Column(db.DateTime())
genre = db.Column(db.String())
author = db.Column(db.Integer, db.ForeignKey('user.id'))
class Active_Association(db.Model):
__tablename__ = 'active_association'
id = db.Column(db.Integer(), primary_key=True)
user_id = db.Column(db.Integer(), db.ForeignKey('user.id', ondelete='CASCADE'))
loop_id = db.Column(db.Integer(), db.ForeignKey('loop.id', ondelete='CASCADE'))
active = db.Column(db.Boolean(), default=False)
I am adding user_favs like so:
loop = Loop.query.filter_by(id = request.form.get('loop')).first()
user = User.query.filter_by(id = request.form.get('user')).first()
user.user_favs.append(loop)
db.session.commit()
Later I am retrieving all user_favs like so:
user = User.query.filter_by(id = current_user.id).first()
loops = user.user_favs
This gives me an array containing all user_favs but I am struggling to a way to filter it by genre.
I could loop over it but that doesn't seem like the proper solution.
I attempted
user = User.query.filter_by(id = current_user.id).first()
loops = user.active_loops
filter(loops.genre == 'jazz')
But this gives me an AttributeError: 'AppenderQuery' object has no attribute 'genre'
Perhaps I have set my whole relationship up wrong and should be able to just filter it directly if set up correctly? This is my first many - many relationship so I may also be on the wrong track with:
active = db.Column(db.Boolean(), default=False)
Which was intended for some other functionality but I do not know how to address that either.
You can use: lazy='dynamic' which will give you a BaseQuery for the relationship itself. Dynamic just means the join type between the two models.
class Loop(db.Model):
id = db.Column(db.Integer, primary_key=True)
creation_date = db.Column(db.DateTime())
genre = db.Column(db.String())
author = db.Column(db.Integer, db.ForeignKey('user.id'))
# will be .authors from Loop.authors
authors = db.relationship(
'User',
secondary=Active_Association,
backref=db.backref('loops'), # will be .loops on User
lazy='dynamic'
)
In essence, it is a query in a query for maximum flexibility.
# gets the user
user = User.query.filter_by(id = current_user.id).first()
# this will return a collection of loops
loops = user.loops.filter_by(genre='jazz')
I have been trying to manage this in one query without the second query but need to sit down to run through it because my joining skillz are a bit rusty with SQLAlchemy.
I want to write a self-referential database structure for my messaging system using Flask and SQLAlchemy.
Tables are very simple:
class User(db.Model):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
messages = db.relationship(...)
def addMessage (self, friend, message):
...
self.messages.append(friend)
return self
and message table is:
class Message (db.Model):
__tablename__ = 'message'
id = db.Column(db.Integer, primary_key=True)
emmiter_id = db.Column(db.Integer, db.ForeignKey('user.id'))
receiver_id = db.Column(db.Integer, db.ForeignKey('user.id'))
text = db.Column(db.Text)
I want to know how will the relationship with my user table look like?
and how can I insert data on the message table?
This is what I wrote in user table as relationship, but it is not working:
messages = db.relationship('User',
secondary = Message,
primaryjoin = (Message.emmiter_id == id),
secondaryjoin = (Message.receiver_id == id),
backref = db.backref('correspondence', lazy = 'dynamic'),
lazy = 'dynamic')
After working a lot around this problem I found the solution with primaryjoin between user table and message table. Here is the working code
# Messaging system table
class Message(db.Model):
__tablename__ = 'message'
emmiter_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
recipient_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
text = db.Column(db.Text, nullable=False)
and user table like :
class User (db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
inbox = db.relationship('Message',backref='sender', primaryjoin=id==Message.emmiter_id)
outbox = db.relationship('Message',backref='receiver', primaryjoin=id==Message.recipient_id)
and here is how you can add a message in message table:
message = Message(text="dsf", sender=sender , receiver=receiver)