2017-06-13 91 views
2

我想知道是否有可能防止提交重複數據庫。例如,假設有一類如下SQLAlchemy,防止重複行

class Employee(Base): 
    id = Column(Integer, primary_key=True) 
    name = Column(String) 

如果我做了一系列的這些對象,

employee1 = Employee(name='bob') 
employee2 = Employee(name='bob') 

session.add_all([employee1, employee2]) 
session.commit() 

我想只有一行被添加到數據庫中, employee1employee2指向內存中的同一個對象(如果可能的話)。

SQLAlchemy中是否有完成此功能的功能?或者我需要確保重複項不存在編程?

+0

這聽起來像你有錯誤的主鍵。如果這是你想要的,你應該把名字作爲主鍵。儘管如此,人們無法真正改變名稱而不會引起巨大的多表數據庫更新,因爲所有外鍵都必須改變。另外,如果您嘗試將名稱更改爲現有名稱,會發生什麼情況?你只想消滅已經存在的那一排? – Omnifarious

+2

按名稱創建唯一索引(即使它不是主鍵)看起來像強制名稱唯一性的正確方法。 – 9000

+0

爲了防止提交重複文件,請執行@ 9000所說的操作。要讓會話在第二個例子中只創建一次bob,請參閱[unique object recipes](https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/UniqueObject)。 –

回答

1

您可以創建一個類的方法獲取或創建Employee - 得到它,如果它存在,否則創建:

@classmethod 
def get_or_create(cls, name): 
    exists = db.session.query(Employee.id).filter_by(name=name).scalar() is not None 
    if exists: 
     return db.session.query(Employee).filter_by(name=name).first() 
    return cls(name=name) 


employee1 = Employee(name='bob') 
db.session.add(employee1) 
employee2 = Employee(name='bob') 

employee1 == employee2 # False 


bob1 = Employee.get_or_create(name='bob') 
if bob1 not in db.session: 
    db.session.add(bob1) 

len(add_to_session) # 1 

bob2 = Employee.get_or_create(name='bob') 
if bob2 not in db.session: 
    db.session.add(bob2) 

len(add_to_session) # 1 

bob1 == bob2 # True 
+1

用於快速訪問https://stackoverflow.com/a/32952421/4386191存在'表達式。 –

+0

這很有趣。我希望有一種方法來解決這個提交。我認爲在'get_or_create'將'bob1'對象返回給'bob2 = Employee.get_or_create(name ='bob')''調用之前,需要首先將bob1添加到會話中?在我的包的當前工作流程中,我首先生成這些類型的對象的大型列表,並且根本沒有與數據庫進行交互,直到在@nven oops提交 – nven

+0

之前,您是對的,它必須被添加到會話中。我將此添加到示例中,並顯示了可能適用於您的模式。不需要提交,只需在檢查唯一性後將對象添加到會話中即可。 –

2

另一種get_or_cerate()解決方案。

from sqlalchemy.orm.exc import NoResultFound 
# ... 

def get_or_create(self, model, **kwargs): 
    """ 
    Usage: 
    class Employee(Base): 
     __tablename__ = 'employee' 
     id = Column(Integer, primary_key=True) 
     name = Column(String, unique=True) 

    get_or_create(Employee, name='bob') 
    """ 
    instance = get_instance(model, **kwargs) 
    if instance is None: 
     instance = create_instance(model, **kwargs) 
    return instance 


def create_instance(model, **kwargs): 
    """create instance""" 
    try: 
     instance = model(**kwargs) 
     sess.add(instance) 
     sess.flush() 
    except Exception as msg: 
     mtext = 'model:{}, args:{} => msg:{}' 
     log.error(mtext.format(model, kwargs, msg)) 
     sess.rollback() 
     raise(msg) 
    return instance 


def get_instance(self, model, **kwargs): 
    """Return first instance found.""" 
    try: 
     return sess.query(model).filter_by(**kwargs).first() 
    except NoResultFound: 
     return