How to fix sqlalchemy.exc.NoForeignKeysError in flask? - python

I have this error:
sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship User.car - there are no foreign keys linking these tables via secondary table 'user'. Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify 'primaryjoin' and 'secondaryjoin' expressions.
127.0.0.1 - - [26/Jul/2019 23:05:40] "GET /user HTTP/1.1" 500 -
The following is my program
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
user_id = Column(Integer)
passport_number = Column(String(8), nullable=False, primary_key=True)
user_email = Column(String(10), nullable=False)
user_name = Column(String(10), nullable=False)
car = relationship('Car', secondary='user')
class Car(Base):
__tablename__ = 'car'
car_number = Column(Integer, primary_key=True, nullable=False)
car_model = Column(String(10), nullable=False)
user_passport_number = Column(String(8), ForeignKey('user.passport_number'), primary_key=True)
part = relationship('Part', secondary='car')
class Part(Base):
__tablename__ = 'part'
part_name = Column(String(10), primary_key=True, nullable=False)
part_price = Column(Integer, nullable=False)
car_number = Column(Integer, ForeignKey('car.car_number'), primary_key=True)

From the error message below:
...there are no foreign keys linking these tables via secondary table 'user'...
we understand that the error might be happening in the setting of the secondary table. Per the docs :
The default behavior of relationship() when constructing a join is that it equates the value of primary key columns on one side to that of foreign-key-referring columns on the other. We can change this criterion to be anything we’d like using the primaryjoin argument, as well as the secondaryjoin argument in the case when a “secondary” table is used.
Therefore, and assuming that you a have a one-to-many or one-to-one relationship between User and Car, you do not seem to need the secondary parameter:
class User(Base):
__tablename__ = 'user'
user_id = Column(Integer)
passport_number = Column(String(8), nullable=False, primary_key=True)
user_email = Column(String(10), nullable=False)
user_name = Column(String(10), nullable=False)
car = relationship('Car', backref='User') #removed secondary relationship
Also, note that you are passing a primary_key constraint to the foreign key field, which is used only in one-to-one relationships, in case it was not intentional. In case it was, then the docs show a simple example for the one-to-one tables:
class User(Base):
__tablename__ = 'user'
passport_number = Column(String(8), nullable=False, primary_key=True)
car = relationship("Car", uselist=False, back_populates="car")
class Car(Base):
__tablename__ = 'car'
id = Column(Integer, primary_key=True)
user_passport_number = Column(String(8), ForeignKey('user.passport_number'))
user = relationship("User", back_populates="car")

Related

Flask SQLAlchemy: adding third column to joining table

Context: I'm making an auctioning website for which I am using Flask-SQLAlchemy. My tables will need to have a many-to-many relationship (as one artpiece can have many user bids and a user can bid on many artpieces)
My question is: it is possible to add another column to my joining table to contain the id of the user bidding, the id of artpiece that they are bidding on and also how much they bid? Also if yes, how would I include this bid in the table when I add a record to said table?
bid_table = db.Table("bid_table",
db.Column("user_id", db.Integer, db.ForeignKey("user.user_id")),
db.Column("item_id", db.Integer, db.ForeignKey("artpiece.item_id"))
)
class User(db.Model):
user_id = db.Column(db.Integer, unique=True, primary_key=True, nullable=False)
username = db.Column(db.Integer, unique=True, nullable=False)
email = db.Column(db.String(50), unique =True, nullable=False)
password = db.Column(db.String(60), nullable=False)
creation_date = db.Column(db.DateTime, default=str(datetime.datetime.now()))
bids = db.relationship("Artpiece", secondary=bid_table, backref=db.backref("bids", lazy="dynamic"))
class Artpiece(db.Model):
item_id = db.Column(db.Integer, unique=True, primary_key=True, nullable=False)
artist = db.Column(db.String(40), nullable=False)
buyer = db.Column(db.String(40), nullable=False)
end_date = db.Column(db.String(40))
highest_bid = db.Column(db.String(40))
It is possible to do this with SQL Alchemy, but it's very cumbersome in my opinion.
SQLAlchemy uses a concept called an Association Proxy to turn a normal table into an association table. This table can have whatever data fields you want on it, but you have to manually tell SQLAlchemy which columns are foreign keys to the other two tables in question.
This is a good example from the documentation.
In your case, the UserKeyword table is the association proxy table that you want to build for your user/bid scenario.
The special_key column is the arbitrary data you would store like the bid amount.
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import backref, declarative_base, relationship
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String(64))
# association proxy of "user_keywords" collection
# to "keyword" attribute
keywords = association_proxy('user_keywords', 'keyword')
def __init__(self, name):
self.name = name
class UserKeyword(Base):
__tablename__ = 'user_keyword'
user_id = Column(Integer, ForeignKey('user.id'), primary_key=True)
keyword_id = Column(Integer, ForeignKey('keyword.id'), primary_key=True)
special_key = Column(String(50))
# bidirectional attribute/collection of "user"/"user_keywords"
user = relationship(User,
backref=backref("user_keywords",
cascade="all, delete-orphan")
)
# reference to the "Keyword" object
keyword = relationship("Keyword")
def __init__(self, keyword=None, user=None, special_key=None):
self.user = user
self.keyword = keyword
self.special_key = special_key
class Keyword(Base):
__tablename__ = 'keyword'
id = Column(Integer, primary_key=True)
keyword = Column('keyword', String(64))
def __init__(self, keyword):
self.keyword = keyword
def __repr__(self):
return 'Keyword(%s)' % repr(self.keyword)
Check out the full documentation for instructions on how to access and create this kind of model.
Having used this in a real project, it's not particularly fun and if you can avoid it, I would recommend it.
https://docs.sqlalchemy.org/en/14/orm/extensions/associationproxy.html

SQLalchemy-Flask: ArgumentError for one to many relationship

I am trying to use sqlalchemy to run queries for one to many relationship. I am having trouble getting my queries to run.
class Quote(db.Model):
id = db.Column(db.Integer, primary_key=True)
description = db.Column(db.String(1000))
category = db.Column(db.String(100))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
date_added = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
class Category(db.Model):
id = db.Column(db.Integer, primary_key=True)
quote_cat = db.relationship("Quote", backref='category', lazy=True)
quote_id_ = db.Column(db.Integer, db.ForeignKey('quote.id'))
sqlalchemy.exc.ArgumentError: Mapper mapped class Category->category
could not assemble any primary key columns for mapped table 'category'
Your quote_cat backref references a property that already exists on the Quote class. Either remove this or change the backref value.
Here are the backref docs:
backref –
indicates the string name of a property to be placed on the related mapper’s class that will handle this relationship in the other direction

sqlalchemy constraint in models inheritance

I have two simple models:
class Message(Backend.instance().get_base()):
__tablename__ = 'messages'
id = Column(Integer, primary_key=True, autoincrement=True)
sender_id = Column(Integer, ForeignKey('users.id'))
content = Column(String, nullable=False)
class ChatMessage(Message):
__tablename__ = 'chat_messages'
id = Column(Integer, ForeignKey('messages.id'), primary_key=True)
receiver_id = Column(Integer, ForeignKey('users.id'))
How to define constraint sender_id!=receiver_id?
This doesn't seem to work with joined table inheritance, I've tried and it complains that the column sender_id from Message doesn't exist when creating the constraint in ChatMessage.
This complaint makes sense, since sender_id wouldn't be in the same table as receiver_id when the tables are created, so the foreign key relationship would need to be followed to check the constraint.
One option is to make ChatMessage a single table.
Use CheckConstraint, placed in table args.
class ChatMessage(Base):
__tablename__ = 'chat_messages'
id = sa.Column(sa.Integer, primary_key=True)
sender_id = sa.Column(sa.Integer, sa.ForeignKey(User.id))
receiver_id = sa.Column(sa.Integer, sa.ForeignKey(User.id))
content = sa.Column(sa.String, nullable=False)
__table_args__ = (
sa.CheckConstraint(receiver_id != sender_id),
)

NoForeignKeysError: Could not determine join condition ... there are no foreign keys linking these tables

Im using sqlalchemy to design a forum style website. I started knocking out the design but everytime I try to test it with a few inserts, it dumps a brick;
NoForeignKeysError: Could not determine join condition between parent/child
tables on relationship Thread.replies - there are no foreign keys linking
these tables. Ensure that referencing columns are associated with a
ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.
Here are my "models"
from sqlalchemy import Integer, Column, String, create_engine, ForeignKey
from sqlalchemy.orm import relationship, sessionmaker, backref
from .database import Base # declarative base instance
class User(Base):
__tablename__ = "user"
id = Column(Integer, primary_key=True)
username = Column(String, unique=True)
email = Column(String, unique=True)
threads = relationship("Thread", backref="user")
posts = relationship("Post", backref="user")
class Post(Base):
__tablename__ = "post"
id = Column(Integer, primary_key=True)
name = Column(String)
body = Column(String)
author = Column(Integer, ForeignKey("user.id"))
class Thread(Base):
__tablename__ = "thread"
id = Column(Integer, primary_key=True)
name = Column(String)
desc = Column(String)
replies = relationship("Post", backref="thread")
author_id = Column(Integer, ForeignKey("user.id"))
board_id = Column(Integer, ForeignKey("board.id"))
class Board(Base):
__tablename__ = "board"
id = Column(Integer, primary_key=True)
name = Column(String)
desc = Column(String)
threads = relationship("Thread", backref="board")
category_id = Column(Integer, ForeignKey("category.id"))
class Category(Base):
__tablename__ = "category"
id = Column(Integer, primary_key=True)
name = Column(String)
desc = Column(String)
threads = relationship("Board", backref="category")
engine = create_engine('sqlite:///:memory:', echo=True)
Base.metadata.create_all(engine)
session_factory = sessionmaker(bind=engine)
session = session_factory()
Your Post model has no thread reference. Add a column to Post referencing the Thread a post belongs to:
class Post(Base):
__tablename__ = "post"
id = Column(Integer, primary_key=True)
name = Column(String)
body = Column(String)
author = Column(Integer, ForeignKey("user.id"))
thread_id = Column(Integer, ForeignKey('thread.id'))
We can't use the name thread because that's what the Post.replies relationship will add to retrieved Thread instances.
This is a One to Many relationship as documented in the SQLAlchemy Relationship Configuration documentation.
You should add a field in the Post model that reads:
thread_id = Column(Integer, ForeignKey("thread.id"), nullable=True, default=None)
How is SQLAlchemy supposed to know how this relationship you defined supposed to link a thhread to a post? That's why you should have a foreign key from a post to its thread. You can allow it to be null, if it does not belong to a thread, it depends on your use case.

SQLAlchemy Polymorphic Loading

I've this model in SQLAlchemy:
class User(Base):
__tablename = 'users'
id = Column(Integer, primary_key=True, autoincrement=True)
type = Column(Text, nullable=False)
user_name = Column(Text, unique=True, nullable=False)
__mapper_args__ = {'polymorphic_on': type}
class Client(User):
__tablename__ = 'clients'
__mapper_args__ = {'polymorphic_identity': 'client'}
id = Column(Integer, ForeignKey('users.id'), primary_key=True)
client_notes = Column(Text)
This is a joined table inheritance. The problem is when I'm querying User:
self.session.query(User).all()
all I get is records from clients, while what I want is all record on User without Client. How do I solve this problem?
Edit: I'm using SQLAlchemy 0.7.4 and Pyramid 1.3a3
session.query(User) does not perform any filtering based on the value of type column.
However, the SQL SELECT statement it generates selects only data from the users table (unless with_polymorphic(...) is used).
But you can add the filter explicitely to achive the desired result:
session.query(User).filter(User.type=='user').all()

Categories

Resources