我已经搜索了此错误,无法理解。我收到以下错误:
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)
请问我该如何解决
答案 0 :(得分:1)
请阅读有关如何创建Minimal, Complete and Verifiable Example的信息。听起来可能有些挑剔,但这样做有很多好处。
对于初学者来说,将问题简化为MCVE时,您经常会自己找出答案的频率令人惊讶。而且,在几秒钟内,其他人可以很轻松地为您提供帮助。
为使您对区别有所了解,不包括导入,您粘贴了181行代码(不会重现该问题)以表示可以在13行中准确重现的问题(还请注意,您已经使用过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()
运行该代码时出现错误:
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.
让我们分解异常消息:
无法确定上的父/子表之间的连接条件 关系Sale.payments-有多个外键路径 链接表格。
这是在告诉您什么地方出了问题。 Sqlalchemy不知道如何创建关系Sale.payments
。这是因为,如果未提供显式联接条件,则sqlalchemy会在表之间寻找外键作为指导。在这种情况下,有两个链接表的外键,Sale.payment
是Payment.payment_id
的FK,而Payment.sale
是Sale.sale_id
的FK。这就是联接条件“含糊”的原因-因为两个表之间有两个潜在的联接路径。
指定“ foreign_keys”参数,并提供这些参数的列表 应视为包含外键引用的列 到父表。
这告诉您如何解决问题。我们可以这样做,它将起作用:
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])
但是我认为这里的真正问题是两个外键不是必需的。无需循环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')
现在循环FK引用不见了,两个表之间只有一条FK路径,而sqlalchemy可以轻松推断出该关系的正确连接路径,我们不需要指定foreign_keys
参数