shop_platform - 新增種子資料時報錯
想完成的事
建立 user 的種子資料
遇到的問題,和解決過程
直接在 MySQL workbench 新增資料沒有問題,但是用 flask_seeder 新增資料會報錯。縮小範圍後,發現使用 flask_sqlalchemy 新增資料就會報錯。
錯誤訊息如下:
Traceback (most recent call last):
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/relationships.py", line 2744, in _determine_joins
self.primaryjoin = join_condition(
File "<string>", line 2, in join_condition
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/sql/selectable.py", line 1184, in _join_condition
cls._joincond_trim_constraints(
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/sql/selectable.py", line 1305, in _joincond_trim_constraints
raise exc.AmbiguousForeignKeysError(
sqlalchemy.exc.AmbiguousForeignKeysError: Can't determine join between 'user' and 'order'; tables have more than one foreign key constraint relationship between them. Please specify the 'onclause' of this join explicitly.
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/flora/projects/shop_platform/app.py", line 28, in <module>
user = User(
File "<string>", line 4, in __init__
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/state.py", line 474, in _initialize_instance
manager.dispatch.init(self, args, kwargs)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/event/attr.py", line 343, in __call__
fn(*args, **kw)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/mapper.py", line 3565, in _event_on_init
instrumenting_mapper._check_configure()
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/mapper.py", line 1873, in _check_configure
_configure_registries({self.registry}, cascade=True)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/mapper.py", line 3380, in _configure_registries
_do_configure_registries(registries, cascade)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/mapper.py", line 3419, in _do_configure_registries
mapper._post_configure_properties()
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/mapper.py", line 1890, in _post_configure_properties
prop.init()
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/interfaces.py", line 222, in init
self.do_init()
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/relationships.py", line 2142, in do_init
self._setup_join_conditions()
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/relationships.py", line 2238, in _setup_join_conditions
self._join_condition = jc = JoinCondition(
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/relationships.py", line 2633, in __init__
self._determine_joins()
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/relationships.py", line 2796, in _determine_joins
util.raise_(
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/util/compat.py", line 207, in raise_
raise exception
sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship User.delivery_orders - 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.
相關程式碼如下:
app.py
import os
from flask import Flask
from dotenv import load_dotenv
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_seeder import FlaskSeeder
if os.environ.get('FLASK_ENV', '') != 'production':
load_dotenv()
app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('SQLALCHEMY_DATABASE_URI')
db = SQLAlchemy(app)
migrate = Migrate(app, db)
seeder = FlaskSeeder()
seeder.init_app(app, db)
@app.route('/')
def home():
return 'Hello World!'
if __name__ == '__main__':
from models import User
user = User(
display_name='John Doe',
email='JohnDoe.example.com',
password='12345678'
)
user.save_to_db()
app.run()
models/user.py
from app import db
from datetime import datetime
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
display_name = db.Column(db.String(50), unique=True, nullable=False)
email = db.Column(db.String(50), unique=True, nullable=False)
password = db.Column(db.String(140), nullable=False)
cell_phone = db.Column(db.String(20))
address = db.Column(db.String(100))
store_introduction = db.Column(db.String(2000))
role = db.Column(db.String(10), nullable=False, default='user')
insert_time = db.Column(db.DateTime, nullable=False, default=datetime.now)
update_time = db.Column(db.DateTime, onupdate=datetime.now, nullable=False, default=datetime.now)
cart = db.relationship('Cart', back_populates='user', uselist=False)
for_sale = db.relationship('Product', back_populates='seller')
delivery_orders = db.relationship('Order', back_populates='seller')
purchase_order = db.relationship('Order', back_populates='buyer')
rating_record = db.relationship('Rating', back_populates='rater')
rated_record = db.relationship('Rating', back_populates='ratee')
asked_questions = db.relationship('Question', back_populates='author')
replies = db.relationship('Reply', back_populates='author')
models/order.py
from app import db
from datetime import datetime
from models import User
class Order(db.Model):
__tablename__ = 'order'
id = db.Column(db.Integer, primary_key=True)
amount = db.Column(db.Integer, nullable=False)
recipient = db.Column(db.String(50), nullable=False)
cell_phone = db.Column(db.String(20), nullable=False)
address = db.Column(db.String(100), nullable=False)
insert_time = db.Column(db.DateTime, nullable=False, default=datetime.now)
update_time = db.Column(db.DateTime, onupdate=datetime.now, nullable=False, default=datetime.now)
seller_id = db.Column(db.Integer, db.ForeignKey(User.id), nullable=False)
buyer_id = db.Column(db.Integer, db.ForeignKey(User.id), nullable=False)
shopping_status_id = db.Column(db.Integer, db.ForeignKey('shopping_status.id'))
payment_status_id = db.Column(db.Integer, db.ForeignKey('payment_status.id'))
seller = db.relationship('User', back_populates='delivery_orders')
buyer = db.relationship('User', back_populates='purchase_order')
shopping_status = db.relationship('ShoppingStatus', back_populates='orders')
payment_status = db.relationship('PaymentStatus', back_populates='orders')
payments = db.relationship('Payment', back_populates='order')
products = db.relationship('OrderItem', back_populates='order')
看樣子是因為,Order 裡面,有 seller_id 和 buyer_id 這兩個 User 的外鍵,但是 sqlalchemy 不知道分別對應到哪個外鍵,所以報錯(如果只有一個外鍵的話,就不會有這個問題)。
Rating 也有同樣的問題。Rating 裡面,有 rater_id 和 ratee_id 這兩個 User 的外鍵。
依照錯誤訊息的建議,加上 foreign_keys 參數:
models/user.py
from app import db
from datetime import datetime
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
display_name = db.Column(db.String(50), unique=True, nullable=False)
email = db.Column(db.String(50), unique=True, nullable=False)
password = db.Column(db.String(140), nullable=False)
cell_phone = db.Column(db.String(20))
address = db.Column(db.String(100))
store_introduction = db.Column(db.String(2000))
role = db.Column(db.String(10), nullable=False, default='user')
insert_time = db.Column(db.DateTime, nullable=False, default=datetime.now)
update_time = db.Column(db.DateTime, onupdate=datetime.now, nullable=False, default=datetime.now)
cart = db.relationship('Cart', back_populates='user', uselist=False)
for_sale = db.relationship('Product', back_populates='seller')
delivery_orders = db.relationship('Order', foreign_keys='Order.seller_id', back_populates='seller')
purchase_order = db.relationship('Order', foreign_keys='Order.buyer_id', back_populates='buyer')
rating_record = db.relationship('Rating', foreign_keys='Rating.rater_id', back_populates='rater')
rated_record = db.relationship('Rating', foreign_keys='Rating.ratee_id', back_populates='ratee')
asked_questions = db.relationship('Question', back_populates='author')
replies = db.relationship('Reply', back_populates='author')
models/order.py
from app import db
from datetime import datetime
from models import User
class Order(db.Model):
__tablename__ = 'order'
id = db.Column(db.Integer, primary_key=True)
amount = db.Column(db.Integer, nullable=False)
recipient = db.Column(db.String(50), nullable=False)
cell_phone = db.Column(db.String(20), nullable=False)
address = db.Column(db.String(100), nullable=False)
insert_time = db.Column(db.DateTime, nullable=False, default=datetime.now)
update_time = db.Column(db.DateTime, onupdate=datetime.now, nullable=False, default=datetime.now)
seller_id = db.Column(db.Integer, db.ForeignKey(User.id), nullable=False)
buyer_id = db.Column(db.Integer, db.ForeignKey(User.id), nullable=False)
shopping_status_id = db.Column(db.Integer, db.ForeignKey('shopping_status.id'))
payment_status_id = db.Column(db.Integer, db.ForeignKey('payment_status.id'))
seller = db.relationship('User', foreign_keys=User.id, back_populates='delivery_orders')
buyer = db.relationship('User', foreign_keys=User.id, back_populates='purchase_order')
shopping_status = db.relationship('ShoppingStatus', back_populates='orders')
payment_status = db.relationship('PaymentStatus', back_populates='orders')
payments = db.relationship('Payment', back_populates='order')
products = db.relationship('OrderItem', back_populates='order')
再次嘗試新增一筆 user 資料,結果出現了別的錯誤訊息:
/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/bin/python /Users/flora/projects/shop_platform/app.py
Traceback (most recent call last):
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/relationships.py", line 2744, in _determine_joins
self.primaryjoin = join_condition(
File "<string>", line 2, in join_condition
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/sql/selectable.py", line 1196, in _join_condition
raise exc.NoForeignKeysError(
sqlalchemy.exc.NoForeignKeysError: Can't find any foreign key relationships between 'order' and 'user'.
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/flora/projects/shop_platform/app.py", line 28, in <module>
user = User(
File "<string>", line 4, in __init__
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/state.py", line 474, in _initialize_instance
manager.dispatch.init(self, args, kwargs)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/event/attr.py", line 343, in __call__
fn(*args, **kw)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/mapper.py", line 3565, in _event_on_init
instrumenting_mapper._check_configure()
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/mapper.py", line 1873, in _check_configure
_configure_registries({self.registry}, cascade=True)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/mapper.py", line 3380, in _configure_registries
_do_configure_registries(registries, cascade)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/mapper.py", line 3419, in _do_configure_registries
mapper._post_configure_properties()
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/mapper.py", line 1890, in _post_configure_properties
prop.init()
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/interfaces.py", line 222, in init
self.do_init()
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/relationships.py", line 2142, in do_init
self._setup_join_conditions()
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/relationships.py", line 2238, in _setup_join_conditions
self._join_condition = jc = JoinCondition(
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/relationships.py", line 2633, in __init__
self._determine_joins()
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/relationships.py", line 2766, in _determine_joins
util.raise_(
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/util/compat.py", line 207, in raise_
raise exception
sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship Order.seller - there are no foreign keys linking these tables. Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.
我不太明白為什麼 sqlalchemy 竟然會跟我說 user 和 order 兩張 table 之間沒有 foreign keys linking。不知道是不是我弄錯了什麼事情。但總之,錯誤訊息裡提到了「or specify a 'primaryjoin' expression」,以及我看到 How do I do a join without a real foreign key constraint? 這篇文章,所以我改成底下這樣:
models/user.py
from app import db
from datetime import datetime
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
display_name = db.Column(db.String(50), unique=True, nullable=False)
email = db.Column(db.String(50), unique=True, nullable=False)
password = db.Column(db.String(140), nullable=False)
cell_phone = db.Column(db.String(20))
address = db.Column(db.String(100))
store_introduction = db.Column(db.String(2000))
role = db.Column(db.String(10), nullable=False, default='user')
insert_time = db.Column(db.DateTime, nullable=False, default=datetime.now)
update_time = db.Column(db.DateTime, onupdate=datetime.now, nullable=False, default=datetime.now)
cart = db.relationship('Cart', back_populates='user', uselist=False)
for_sale = db.relationship('Product', back_populates='seller')
delivery_orders = db.relationship('Order', primaryjoin='Order.seller_id == User.id', back_populates='seller')
purchase_order = db.relationship('Order', primaryjoin='Order.buyer_id == User.id', back_populates='buyer')
rating_record = db.relationship('Rating', primaryjoin='Rating.rater_id == User.id', back_populates='rater')
rated_record = db.relationship('Rating', primaryjoin='Rating.ratee_id == User.id', back_populates='ratee')
asked_questions = db.relationship('Question', back_populates='author')
replies = db.relationship('Reply', back_populates='author')
models/order.py
from app import db
from datetime import datetime
from models import User
class Order(db.Model):
__tablename__ = 'order'
id = db.Column(db.Integer, primary_key=True)
amount = db.Column(db.Integer, nullable=False)
recipient = db.Column(db.String(50), nullable=False)
cell_phone = db.Column(db.String(20), nullable=False)
address = db.Column(db.String(100), nullable=False)
insert_time = db.Column(db.DateTime, nullable=False, default=datetime.now)
update_time = db.Column(db.DateTime, onupdate=datetime.now, nullable=False, default=datetime.now)
seller_id = db.Column(db.Integer, db.ForeignKey(User.id), nullable=False)
buyer_id = db.Column(db.Integer, db.ForeignKey(User.id), nullable=False)
shopping_status_id = db.Column(db.Integer, db.ForeignKey('shopping_status.id'))
payment_status_id = db.Column(db.Integer, db.ForeignKey('payment_status.id'))
seller = db.relationship('User', primaryjoin='Order.seller_id == User.id', back_populates='delivery_orders')
buyer = db.relationship('User', primaryjoin='Order.buyer_id == User.id', back_populates='purchase_order')
shopping_status = db.relationship('ShoppingStatus', back_populates='orders')
payment_status = db.relationship('PaymentStatus', back_populates='orders')
payments = db.relationship('Payment', back_populates='order')
products = db.relationship('OrderItem', back_populates='order')
然後就可以新增 user 資料了。
commit: fix: Error when save user data to db
參考資料
Comments
Post a Comment