2017-11-19 3 views
0

Da Country.name im folgenden Codebeispiel unique=True hat und weil zwei Länder mit demselben Namen erstellt werden, erwarte ich, dass session.commit() fehlschlagen würde.Erkennen der Verletzung der eindeutigen Integritätsbedingung in SQLAlchemy

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() 

Aber das Commit geht gut voran. Wie erkenne ich, dass die eindeutige Einschränkung verletzt wurde?

+2

Haben Sie vielleicht die DB erstellt, bevor Sie Ihrem Modell 'unique = True' hinzugefügt haben? Wenn dies der Fall ist, müssen Sie das DB-Schema auf die eine oder andere Weise migrieren. –

Antwort

0

Wenn ich Ihren Code ausführen wie geschrieben, es funktioniert nicht:

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',)] 

Die eindeutige Einschränkung Teil der Tabelle ist, nicht unbedingt das Modell. Wenn ich die Datenbankerstellung zuerst ohne es ausführen, dann führe deinen Code aus, ich bekomme keine Probleme.

Für eine kompliziertere Tabelle möchten Sie ein Migrations-Tool wie Alembic verwenden, aber Sie können dies selbst tun (Sie können es in einem Skript tun, aber ich mache nur zwei, um es zu behalten) einfach, sorry, wenn das nicht pythonic ist.

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() 

gefolgt von

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() 

Sie haben nicht die ID passieren, aber Sie können, wenn Sie brauchen für welchen Gründen auch immer, es zu halten Denken Sie auch daran zu begehen, bevor Sie den Tisch fallen lassen oder es wird nicht ganz richtig funktionieren

Verwandte Themen