[英]Detecting breach of unique constraint in SQLAlchemy
Because Country.name
in the following code sample has unique=True
, and because two countries of the same name are created, I'm expecting that session.commit()
would fail. 由于以下代码示例中的Country.name
具有unique=True
,并且由于创建了两个具有相同名称的国家,所以我期望session.commit()
会失败。
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class Country(Base):
__tablename__ = 'countries'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(64), unique=True, nullable=False, index=True)
engine = create_engine('sqlite:///countries_example.db')
Base.metadata.create_all(engine)
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
session = DBSession()
malta1 = Country(name='Malta')
malta2 = Country(name='Malta')
session.add(malta1)
session.add(malta2)
session.commit()
But the commit proceeds just fine. 但是提交进行得很好。 How do I detect that the unique constraint was breached? 如何检测到唯一约束已被违反?
When I run your code as written, it does fail: 当我以书面形式运行您的代码时,它的确失败了:
Traceback (most recent call last):
File "so4.py", line 26, in <module>
session.commit()
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/orm/session.py", line 906, in commit
self.transaction.commit()
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/orm/session.py", line 461, in commit
self._prepare_impl()
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/orm/session.py", line 441, in _prepare_impl
self.session.flush()
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/orm/session.py", line 2177, in flush
self._flush(objects)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/orm/session.py", line 2297, in _flush
transaction.rollback(_capture_exception=True)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/util/langhelpers.py", line 66, in __exit__
compat.reraise(exc_type, exc_value, exc_tb)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/util/compat.py", line 187, in reraise
raise value
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/orm/session.py", line 2261, in _flush
flush_context.execute()
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/orm/unitofwork.py", line 389, in execute
rec.execute(self)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/orm/unitofwork.py", line 548, in execute
uow
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/orm/persistence.py", line 181, in save_obj
mapper, table, insert)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/orm/persistence.py", line 835, in _emit_insert_statements
execute(statement, params)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 945, in execute
return meth(self, multiparams, params)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/sql/elements.py", line 263, in _execute_on_connection
return connection._execute_clauseelement(self, multiparams, params)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1053, in _execute_clauseelement
compiled_sql, distilled_params
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1189, in _execute_context
context)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1402, in _handle_dbapi_exception
exc_info
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/util/compat.py", line 203, in raise_from_cause
reraise(type(exception), exception, tb=exc_tb, cause=cause)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/util/compat.py", line 186, in reraise
raise value.with_traceback(tb)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1182, in _execute_context
context)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/engine/default.py", line 470, in do_execute
cursor.execute(statement, parameters)
sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) UNIQUE constraint failed: countries.name [SQL: 'INSERT INTO countries (name) VALUES (?)'] [parameters: ('Malta',)]
The unique constraint is part of the table, not necessarily the model. 唯一约束是表的一部分,不一定是模型。 When I run the database creation first without it, then run your code, I get no problems. 当我首先运行没有它的数据库创建,然后运行您的代码时,我没有任何问题。
For a more complicated table, you would want to use a migration tool like alembic, but for this, you could go ahead and do it yourself (you can do it in one script, but I just do two to keep it simple, sorry if that's not pythonic: 对于更复杂的表,您可能想要使用像alembic这样的迁移工具,但是为此,您可以自己进行操作(您可以在一个脚本中进行操作,但是我只做两个来简化操作,抱歉那不是pythonic:
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class Country(Base):
__tablename__ = 'countries'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(64), unique=True, nullable=False, index=True)
class TempCountry(Base):
__tablename__ = 'temp_countries'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(64), nullable=False, index=True)
engine = create_engine('sqlite:///countries_example.db')
Base.metadata.create_all(engine)
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
session = DBSession()
foreach country in session.query(Country).all():
if len(session.query(TempCountry).filter_by(name=country.name).all()) == 0:
temp_country = TempCountry(id=country.id, name=country.name)
session.add(temp_country)
session.commit()
Country.__table__.drop(engine)
session.commit()
followed by 其次是
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class Country(Base):
__tablename__ = 'countries'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(64), unique=True, nullable=False, index=True)
class TempCountry(Base):
__tablename__ = 'temp_countries'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(64), nullable=False, index=True)
engine = create_engine('sqlite:///countries_example.db')
Base.metadata.create_all(engine)
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
session = DBSession()
foreach temp_country in session.query(TempCountry).all():
country = Country(id=country.id, name=country.name)
session.add(country)
session.commit()
TempCountry.__table__.drop(engine)
session.commit()
You don't have to pass the id, but you can if you need to keep it for whatever reason. 您不必传递ID,但是如果出于任何原因需要保留ID,则可以传递。 Also, remember to commit before dropping the table or it won't work quite right. 另外,请记住在删除表之前先进行提交,否则它将无法正常工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.