【发布时间】:2018-09-15 02:01:22
【问题描述】:
我在Essential SQLAlchemy这本书的帮助下学习SQLAlchemy。
其中一个示例对我想要学习的内容至关重要 - 添加相关对象。我就是无法让它工作。我希望这个社区可以帮助确定我是否遗漏了什么,或者示例中是否存在缺陷。显然前者是最有可能的。但是,无论如何,我希望这可能对其他人有所帮助。对我来说,直到这个关键时刻,这本书都让人感觉直截了当。
给出的例子来自第 7 章,麻烦的“命令”代码是例 7-25。完整代码示例在https://github.com/jasonamyers/essential-sqlalchemy-2e
我从以下设置开始:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
engine = create_engine('mysql+pymysql://root:password@localhost/Alberta', pool_recycle=3600)
Session = sessionmaker(bind=engine)
session = Session()
from datetime import datetime
from sqlalchemy import (Table, Column, Integer, Numeric, String, DateTime, ForeignKey, Boolean, desc, func)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref
Base = declarative_base()
class Cookie(Base):
__tablename__ = 'cookies'
cookie_id = Column(Integer, primary_key=True)
cookie_name = Column(String(50), index=True)
cookie_recipe_url = Column(String(255))
cookie_sku = Column(String(55))
quantity = Column(Integer())
unit_cost = Column(Numeric(12, 2))
def __repr__(self):
return "Cookie(cookie_name='{self.cookie_name}', " \
"cookie_recipe_url='{self.cookie_recipe_url}', " \
"cookie_sku='{self.cookie_sku}', " \
"quantity={self.quantity}, " \
"unit_cost={self.unit_cost})".format(self=self)
class User(Base):
__tablename__ = 'users'
user_id = Column(Integer(), primary_key=True)
username = Column(String(15), nullable=False, unique=True)
email_address = Column(String(255), nullable=False)
phone = Column(String(20), nullable=False)
password = Column(String(25), nullable=False)
created_on = Column(DateTime(), default=datetime.now)
updated_on = Column(DateTime(), default=datetime.now, onupdate=datetime.now)
def __repr__(self):
return "User(username='{self.username}', " \
"email_address='{self.email_address}', " \
"phone='{self.phone}', " \
"password='{self.password}')".format(self=self)
class Order(Base):
__tablename__ = 'orders'
order_id = Column(Integer(), primary_key=True)
user_id = Column(Integer(), ForeignKey('users.user_id'))
shipped = Column(Boolean(), default=False)
user = relationship("User", backref=backref('orders', order_by=order_id))
def __repr__(self):
return "Order(user_id={self.user_id}, " \
"shipped={self.shipped})".format(self=self)
class LineItem(Base):
__tablename__ = 'line_items'
line_item_id = Column(Integer(), primary_key=True)
order_id = Column(Integer(), ForeignKey('orders.order_id'))
cookie_id = Column(Integer(), ForeignKey('cookies.cookie_id'))
quantity = Column(Integer())
extended_cost = Column(Numeric(12, 2))
order = relationship("Order", backref=backref('line_items', order_by=line_item_id))
cookie = relationship("Cookie", uselist=False)
def __repr__(self):
return "LineItems(order_id={self.order_id}, " \
"cookie_id={self.cookie_id}, " \
"quantity={self.quantity}, " \
"extended_cost={self.extended_cost})".format(
self=self)
Base.metadata.create_all(engine)
我成功运行以下示例,使用用户名 cookiemon 的用户填充数据库:
cookiemon = User(username='cookiemon',
email_address='wellhithere@gmail.com',
phone='416-555-1212',
password='password'
)
session.add(cookiemon)
session.commit()
现在在本书的下一个示例中,我被告知以下代码将起作用。
o1 = Order()
o1.user = cookiemon
session.add(o1)
cc = session.query(Cookie).filter(Cookie.cookie_name == "chocolate chip").one()
line1 = LineItem(cookie=cc, quantity=2, extended_cost=1.00)
pb = session.query(Cookie).filter(Cookie.cookie_name ==
"peanut butter").one()
line2 = LineItem(quantity=12, extended_cost=3.00)
line2.cookie = pb
line2.order = o1
o1.line_items.append(line1)
o1.line_items.append(line2)
session.commit()
但是,由于数据库中已经存在用户 cookiemon,我收到以下错误:
sqlalchemy.exc.IntegrityError: (由于查询调用自动刷新而引发;如果此刷新过早发生,请考虑使用 session.no_autoflush 块) (pymysql.err.IntegrityError) (1062, u"Duplicate entry 'cookiemon ' for key'username'") [SQL: u'INSERT INTO users (username, email_address, phone, password, created_on, updated_on) VALUES (%(username)s, %(email_address)s, %(phone)s, % (密码)s, %(created_on)s, %(updated_on)s)'] [参数: {'username': 'cookiemon', 'phone': '416-555-1212', 'created_on': datetime.datetime (2018, 9, 14, 13, 7, 29, 353673), 'updated_on': datetime.datetime(2018, 9, 14, 13, 7, 29, 353701), 'password': 'passwordEW', 'email_address' :'wellhithere@gmail.com'}]
如果我先删除 cookiemon 用户,上面的订单代码就像一个魅力(这并不奇怪)。但这在现实世界中很难奏效。
我可以看到有一个使用 no_autoflush 的解决方案。但这有必要吗?还是我只是在这里遗漏了一些简单但重要的东西?
这是回溯:
cd "/Users/chris/Desktop/Banff Experiments" ; env "PYTHONIOENCODING=UTF-8" "PYTHONUNBUFFERED=1" "PYTHONPATH=/Users/chris/.vscode/extensions/ms-python.python-2018.8.0/pythonFiles/experimental/ptvsd" "/Users/chris/Desktop/Banff Experiments/banff/bin/python" -m ptvsd --host localhost --port 59094 "/Users/chris/Desktop/Banff Experiments/workspace4.py"
Traceback (most recent call last):
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/runpy.py", line 162, in _run_module_as_main
"__main__", fname, loader, pkg_name)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/runpy.py", line 72, in _run_code
exec code in run_globals
File "/Users/chris/.vscode/extensions/ms-python.python-2018.8.0/pythonFiles/experimental/ptvsd/ptvsd/__main__.py", line 211, in <module>
singlesession=args.single_session)
File "/Users/chris/.vscode/extensions/ms-python.python-2018.8.0/pythonFiles/experimental/ptvsd/ptvsd/__main__.py", line 205, in main
debug_main(addr, name, kind, *extra, **kwargs)
File "/Users/chris/.vscode/extensions/ms-python.python-2018.8.0/pythonFiles/experimental/ptvsd/ptvsd/_local.py", line 30, in debug_main
run_file(address, name, *extra, **kwargs)
File "/Users/chris/.vscode/extensions/ms-python.python-2018.8.0/pythonFiles/experimental/ptvsd/ptvsd/_local.py", line 64, in run_file
run(argv, addr, **kwargs)
File "/Users/chris/.vscode/extensions/ms-python.python-2018.8.0/pythonFiles/experimental/ptvsd/ptvsd/_local.py", line 125, in _run
_pydevd.main()
File "/Users/chris/.vscode/extensions/ms-python.python-2018.8.0/pythonFiles/experimental/ptvsd/ptvsd/_vendored/pydevd/pydevd.py", line 1743, in main
debugger.connect(host, port)
File "/Users/chris/.vscode/extensions/ms-python.python-2018.8.0/pythonFiles/experimental/ptvsd/ptvsd/_vendored/pydevd/pydevd.py", line 1099, in run
return self._exec(is_module, entry_point_fn, module_name, file, globals, locals)
File "/Users/chris/.vscode/extensions/ms-python.python-2018.8.0/pythonFiles/experimental/ptvsd/ptvsd/_vendored/pydevd/pydevd.py", line 1106, in _exec
pydev_imports.execfile(file, globals, locals) # execute the script
File "/Users/chris/Desktop/Banff Experiments/workspace4.py", line 94, in <module>
session.commit()
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 943, in commit
self.transaction.commit()
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 467, in commit
self._prepare_impl()
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 447, in _prepare_impl
self.session.flush()
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 2254, in flush
self._flush(objects)
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 2380, in _flush
transaction.rollback(_capture_exception=True)
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/util/langhelpers.py", line 66, in __exit__
compat.reraise(exc_type, exc_value, exc_tb)
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 2344, in _flush
flush_context.execute()
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/unitofwork.py", line 391, in execute
rec.execute(self)
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/unitofwork.py", line 556, in execute
uow
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/persistence.py", line 181, in save_obj
mapper, table, insert)
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/persistence.py", line 866, in _emit_insert_statements
execute(statement, params)
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 948, in execute
return meth(self, multiparams, params)
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/sql/elements.py", line 269, in _execute_on_connection
return connection._execute_clauseelement(self, multiparams, params)
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1060, in _execute_clauseelement
compiled_sql, distilled_params
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1200, in _execute_context
context)
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1413, in _handle_dbapi_exception
exc_info
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/util/compat.py", line 265, in raise_from_cause
reraise(type(exception), exception, tb=exc_tb, cause=cause)
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1193, in _execute_context
context)
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/engine/default.py", line 509, in do_execute
cursor.execute(statement, parameters)
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/pymysql/cursors.py", line 170, in execute
result = self._query(query)
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/pymysql/cursors.py", line 328, in _query
conn.query(q)
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/pymysql/connections.py", line 516, in query
self._affected_rows = self._read_query_result(unbuffered=unbuffered)
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/pymysql/connections.py", line 727, in _read_query_result
result.read()
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/pymysql/connections.py", line 1066, in read
first_packet = self.connection._read_packet()
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/pymysql/connections.py", line 683, in _read_packet
packet.check_error()
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/pymysql/protocol.py", line 220, in check_error
err.raise_mysql_exception(self._data)
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/pymysql/err.py", line 109, in raise_mysql_exception
raise errorclass(errno, errval)
sqlalchemy.exc.IntegrityError: (pymysql.err.IntegrityError) (1062, u"Duplicate entry 'cookiemon' for key 'username'") [SQL: u'INSERT INTO users (username, email_address, phone, password, created_on, updated_on) VALUES (%(username)s, %(email_address)s, %(phone)s, %(password)s, %(created_on)s, %(updated_on)s)'] [parameters: {'username': 'cookiemon', 'phone': '416-555-1212', 'created_on': datetime.datetime(2018, 9, 14, 13, 44, 46, 916687), 'updated_on': datetime.datetime(2018, 9, 14, 13, 44, 46, 916712), 'password': 'passwordEW', 'email_address': 'wellhithere@gmail.com'}] (Background on this error at: http://sqlalche.me/e/gkpj)
【问题讨论】:
-
尝试捕获完整性错误有什么问题,然后向用户发送一条消息以通知他们用户名已被占用?我在这里错过了什么吗?
-
很抱歉在没有足够背景的情况下对上面的事情进行了简要介绍。据我了解,此示例与用户订购某些 cookie 的情况相匹配,但他们已经在两个月前订购了 cookie。结果,它们在数据库中。现在,两个月过去了,他们饿了。 :) 所以他们想下一个新订单。所以,它是同一个用户,但有一个新订单。
-
关于使用 SQLAlchemy 的对象数据库的非常有用的教程在这里 blog.miguelgrinberg.com/post/…
-
我正在尝试重现您的问题,但您的代码在这里运行良好。我唯一添加的是创建“巧克力片”和“花生酱”
Cookie()对象并将它们添加到会话中。您能否提供完整的错误回溯以及行号?另外请将其格式化为代码,否则将很难阅读。 -
你能幽默一下并检查你的数据库模式中的 cookie 表吗?我相信你在某个地方有一个你不应该有的独特约束。它没有显示在您的模型中,但是如果您创建了数据库并说用户 ID 字段必须是唯一的,那么无论您的类做什么,它都会失败或稍后导致更多问题。
标签: python sqlalchemy