我已经搜索了这个错误,但什么也看不懂。我收到以下错误:
sqlalchemy.exc.AmbiguousForeignKeysError:无法确定关系 Sale.payments 上的父/子表之间的连接条件 - 有多个外键路径链接表。指定“foreign_keys”参数,提供应被视为包含对父表的外键引用的列的列表。
这是我的代码:
# -*- coding: utf-8 -*-
import sqlalchemy as sa
import bcrypt as bc
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker, relationship, backref
from sqlalchemy_utils import database_exists, create_database
engine = sa.create_engine('sqlite:///data/db/nestopol.db')
if not database_exists(engine.url):
create_database(engine.url)
session = scoped_session(sessionmaker(bind=engine))
Base = declarative_base()
class Staff(Base):
__tablename__ = 'staffs'
staff_id = sa.Column(sa.Integer, primary_key=True)
username = sa.Column(sa.String(64), unique=True, index=True, nullable=False)
# Needs a length if MySQL is used
# password is 93 in length
password = sa.Column(sa.String(124), nullable=False)
admin = sa.Column(sa.Boolean, default=False, nullable=False)
first_name = sa.Column(sa.String(26), nullable=False)
last_name = sa.Column(sa.String(26), nullable=False)
gender = sa.Column(sa.String(6), nullable=False)
birthday = sa.Column(sa.String(11), nullable=False)
mobile_number = sa.Column(sa.String(14), nullable=False)
city = sa.Column(sa.String(26), nullable=False)
state = sa.Column(sa.String(26), nullable=False)
country = sa.Column(sa.String(26), nullable=False)
address = sa.Column(sa.String(128), nullable=False)
added_on = sa.Column(sa.DateTime)
modified_on = sa.Column(sa.DateTime)
customers = relationship('Customer', backref='staffs', lazy='select')
products = relationship('Product', backref='staffs', lazy='select')
categories = relationship('Category', backref='staffs', lazy='select')
suppliers = relationship('Supplier', backref='staffs', lazy='select')
supply = relationship('Supply', backref='staffs', lazy='select')
sales = relationship('Sale', backref='staffs', lazy='select')
items = relationship('Item', backref='staffs', lazy='select')
payments = relationship('Payment', backref='staffs', lazy='select')
def generate_password_hash(self, password):
return bc.hashpw(self.password, bc.gensalt())
def check_password_hash(self, plain_text_password, password):
return bc.checkpw(plain_text_password, self.hashed_password)
class Customer(Base):
__tablename__ = 'customers'
customer_id = sa.Column(sa.Integer, primary_key=True)
first_name = sa.Column(sa.String(26), nullable=False)
last_name = sa.Column(sa.String(26), nullable=False)
email = sa.Column(sa.String(64), unique=True, index=True, nullable=False)
gender = sa.Column(sa.String(7), nullable=False)
birthday = sa.Column(sa.String(11), nullable=False)
phone = sa.Column(sa.String(14), nullable=False)
city = sa.Column(sa.String(26), nullable=False)
state = sa.Column(sa.String(26), nullable=False)
country = sa.Column(sa.String(26), nullable=False)
address = sa.Column(sa.String(128), nullable=False)
description = sa.Column(sa.String(128), nullable=False)
purchases = sa.Column(sa.Integer, nullable=False)
expenditure = sa.Column(sa.Float, nullable=False)
reward = sa.Column(sa.Integer, nullable=False)
author = sa.Column(sa.Integer, sa.ForeignKey('staffs.staff_id'))
added_on = sa.Column(sa.DateTime)
modified_on = sa.Column(sa.DateTime)
sales = relationship('Sale', backref='customers', lazy='select')
items = relationship('Item', backref='customers', lazy='select')
payments = relationship('Payment', backref='customers', lazy='select')
class Product(Base):
__tablename__ = 'products'
product_id = sa.Column(sa.Integer, primary_key=True)
code = sa.Column(sa.String(128), unique=True, index=True, nullable=False)
name = sa.Column(sa.String(26), nullable=False)
category = sa.Column(sa.Integer, sa.ForeignKey('categories.product_category_id'))
cost_price = sa.Column(sa.Float, nullable=False)
selling_price = sa.Column(sa.Float, nullable=False)
weight = sa.Column(sa.Float, nullable=False)
stock = sa.Column(sa.Integer, nullable=False)
discount = sa.Column(sa.Float, nullable=False)
expires = sa.Column(sa.DateTime)
supplier = sa.Column(sa.Integer, sa.ForeignKey('suppliers.supplier_id'))
description = sa.Column(sa.String(128), nullable=False)
author = sa.Column(sa.Integer, sa.ForeignKey('staffs.staff_id'))
added_on = sa.Column(sa.DateTime)
items = relationship('Item', backref='products', lazy='select')
class Category(Base):
__tablename__ = 'categories'
product_category_id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String(26), nullable=False)
author = sa.Column(sa.Integer, sa.ForeignKey('staffs.staff_id'))
added_on = sa.Column(sa.DateTime)
products = relationship('Product', backref='categories', lazy='select')
class Supplier(Base):
__tablename__ = 'suppliers'
supplier_id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String(26), nullable=False)
debt = sa.Column(sa.Float, nullable=False)
email = sa.Column(sa.String(64), unique = True, index=True, nullable=False)
phone = sa.Column(sa.String(14), nullable=False)
city = sa.Column(sa.String(26), nullable=False)
state = sa.Column(sa.String(26), nullable=False)
country = sa.Column(sa.String(26), nullable=False)
address = sa.Column(sa.Float, nullable=False)
description = sa.Column(sa.String(200), nullable=False)
author = sa.Column(sa.Integer, sa.ForeignKey('staffs.staff_id'))
added_on = sa.Column(sa.DateTime)
modified_on = sa.Column(sa.DateTime)
products = relationship('Product', backref='suppliers', lazy='select')
supplies = relationship('Supply', backref='suppliers', lazy='select')
class Supply(Base):
__tablename__ = 'supplies'
supply_id = sa.Column(sa.Integer, primary_key=True)
title = sa.Column(sa.String(26), nullable=False)
supplier = sa.Column(sa.Integer, sa.ForeignKey('suppliers.supplier_id'))
items = sa.Column(sa.Integer, nullable=False)
value = sa.Column(sa.Float, nullable=False)
author = sa.Column(sa.Integer, sa.ForeignKey('staffs.staff_id'))
added_on = sa.Column(sa.DateTime)
class Sale(Base):
__tablename__ = 'sales'
sale_id = sa.Column(sa.Integer, primary_key=True)
code = sa.Column(sa.String(8), unique=True, index=True, nullable=False)
title = sa.Column(sa.String(26), nullable=False)
customer = sa.Column(sa.Integer, sa.ForeignKey('customers.customer_id'))
total = sa.Column(sa.Float, nullable=False)
discount = sa.Column(sa.Float, nullable=False)
vat = sa.Column(sa.Float, nullable=False)
payment_method = sa.Column(sa.String(26), nullable=False)
payment = sa.Column(sa.Integer, sa.ForeignKey('payments.payment_id'))
state = sa.Column(sa.String(26), nullable=False)
item = sa.Column(sa.Integer, sa.ForeignKey('items.item_id'))
author = sa.Column(sa.Integer, sa.ForeignKey('staffs.staff_id'))
added_on = sa.Column(sa.DateTime)
payments = relationship('Payment', backref='sales', lazy='select')
items = relationship('Item', backref='sales', lazy='select')
class Item(Base):
__tablename__ = 'items'
item_id = sa.Column(sa.Integer, primary_key=True)
sale = sa.Column(sa.Integer, sa.ForeignKey('sales.sale_id'))
customer = sa.Column(sa.Integer, sa.ForeignKey('customers.customer_id'))
item = sa.Column(sa.Integer, sa.ForeignKey('products.product_id'))
quantity = sa.Column(sa.Integer, nullable=False)
total = sa.Column(sa.Float, nullable=False)
author = sa.Column(sa.Integer, sa.ForeignKey('staffs.staff_id'))
added_on = sa.Column(sa.DateTime)
sales = relationship('Sale', backref='items', lazy='select')
class Payment(Base):
__tablename__ = 'payments'
payment_id = sa.Column(sa.Integer, primary_key=True)
sale = sa.Column(sa.Integer, sa.ForeignKey('sales.sale_id'))
customer = sa.Column(sa.Integer, sa.ForeignKey('customers.customer_id'))
status = sa.Column(sa.String(26), nullable=False)
amount_paid = sa.Column(sa.Float, nullable=False)
amount_due = sa.Column(sa.Float, nullable=False)
comment = sa.Column(sa.String(128), nullable=False)
author = sa.Column(sa.Integer, sa.ForeignKey('staffs.staff_id'))
added_on = sa.Column(sa.DateTime)
modified_on = sa.Column(sa.DateTime)
sales = relationship('Sale', backref='payments', lazy='select')
Base.metadata.create_all(engine)
Run Code Online (Sandbox Code Playgroud)
请问我该如何解决这个问题
请阅读有关如何创建最小、完整和可验证示例的信息。这听起来可能很挑剔,但这样做有很多好处。
对于初学者来说,在将问题提炼成 MCVE 时,您自己找出答案的频率是惊人的。而且,在几秒钟内,它使其他人可以更轻松地帮助您。
为了让您了解差异,不包括导入,您粘贴了 181 行代码(不会重现问题)来表示可以在 13 行中准确重现的问题(另请注意,您在哪里used backref,我已修改为back_populates,您需要探索两者之间的区别):
class Sale(Base):
__tablename__ = 'sales'
sale_id = sa.Column(sa.Integer, primary_key=True)
payment = sa.Column(sa.Integer, sa.ForeignKey('payments.payment_id'))
payments = relationship('Payment', back_populates='sales', lazy='select')
class Payment(Base):
__tablename__ = 'payments'
payment_id = sa.Column(sa.Integer, primary_key=True)
sale = sa.Column(sa.Integer, sa.ForeignKey('sales.sale_id'))
sales = relationship('Sale', back_populates='payments', lazy='select')
Sale()
Run Code Online (Sandbox Code Playgroud)
当我运行该代码时,出现错误:
sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between
parent/child tables on relationship Sale.payments - there are multiple foreign key
paths linking the tables. Specify the 'foreign_keys' argument, providing a list of
those columns which should be counted as containing a foreign key reference to the
parent table.
Run Code Online (Sandbox Code Playgroud)
让我们分解异常消息:
无法确定关系 Sale.payments 上父/子表之间的连接条件 - 有多个外键路径链接表。
This is telling you what is wrong. Sqlalchemy doesn't know how to create the relationship Sale.payments. This is because, if no explicit join condition is provided, sqlalchemy looks to the Foreign Key's between the tables for guidance. In this case there are two foreign keys linking the tables, Sale.payment is a FK of Payment.payment_id and Payment.sale is a FK to Sale.sale_id. This is why the join condition is "ambiguous" - because there are two potential join path's between the two tables.
Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table.
This is telling you how to resolve the problem. We could do this and it will work:
class Sale(Base):
__tablename__ = 'sales'
sale_id = sa.Column(sa.Integer, primary_key=True)
payment = sa.Column(sa.Integer, sa.ForeignKey('payments.payment_id'))
payments = relationship('Payment', back_populates='sales', lazy='select',
foreign_keys=[payment])
class Payment(Base):
__tablename__ = 'payments'
payment_id = sa.Column(sa.Integer, primary_key=True)
sale = sa.Column(sa.Integer, sa.ForeignKey('sales.sale_id'))
sales = relationship('Sale', back_populates='payments', lazy='select',
foreign_keys=[Sale.payment])
Run Code Online (Sandbox Code Playgroud)
But I think the real issue here is that the two foreign keys aren't necessary. This code achieves the exact same result without the circular FK:
class Sale(Base):
__tablename__ = 'sales'
sale_id = sa.Column(sa.Integer, primary_key=True)
payments = relationship('Payment', back_populates='sales', lazy='select')
class Payment(Base):
__tablename__ = 'payments'
payment_id = sa.Column(sa.Integer, primary_key=True)
sale = sa.Column(sa.Integer, sa.ForeignKey('sales.sale_id'))
sales = relationship('Sale', back_populates='payments', lazy='select')
Run Code Online (Sandbox Code Playgroud)
Now that the circular FK refs are gone, there is only one FK path between the two tables and sqlalchemy can easily reason out the correct join path for the relationship and we don't need to specify the foreign_keys parameter.
| 归档时间: |
|
| 查看次数: |
780 次 |
| 最近记录: |