Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python SQLAlchemy是否与Django';我们是得到还是创造?_Python_Django_Sqlalchemy - Fatal编程技术网

Python SQLAlchemy是否与Django';我们是得到还是创造?

Python SQLAlchemy是否与Django';我们是得到还是创造?,python,django,sqlalchemy,Python,Django,Sqlalchemy,如果对象已经存在(根据提供的参数),我希望从数据库中获取该对象,如果不存在,则创建该对象 Django的(或)就是这样做的。在炼金术中有没有一条等价的捷径 我现在明确地写出来如下: def get_or_create_instrument(session, serial_number): instrument = session.query(Instrument).filter_by(serial_number=serial_number).first() if instrume

如果对象已经存在(根据提供的参数),我希望从数据库中获取该对象,如果不存在,则创建该对象

Django的(或)就是这样做的。在炼金术中有没有一条等价的捷径

我现在明确地写出来如下:

def get_or_create_instrument(session, serial_number):
    instrument = session.query(Instrument).filter_by(serial_number=serial_number).first()
    if instrument:
        return instrument
    else:
        instrument = Instrument(serial_number)
        session.add(instrument)
        return instrument

基本上就是这样,没有捷径可走

当然,你们可以概括如下:

def get_or_create(session, model, defaults=None, **kwargs):
    instance = session.query(model).filter_by(**kwargs).one_or_none()
    if instance:
        return instance, False
    else:
        params = {k: v for k, v in kwargs.items() if not isinstance(v, ClauseElement)}
        params.update(defaults or {})
        instance = model(**params)
        try:
            session.add(instance)
            session.commit()
        except Exception:  # The actual exception depends on the specific database so we catch all exceptions. This is similar to the official documentation: https://docs.sqlalchemy.org/en/latest/orm/session_transaction.html
            session.rollback()
            instance = session.query(model).filter_by(**kwargs).one()
            return instance, False
        else:
            return instance, True

2020年更新(仅限Python 3.9+版) 这是一个使用Python3.9的更干净的版本

注: 与Django版本类似,这将捕获重复的键约束和类似的错误。如果你的get或create不能保证返回一个结果,它仍然会导致比赛状态


为了缓解一些问题,您需要在
会话.commit()之后添加另一个
one\u或\u none()
样式的fetch。这仍然不是针对竞争条件的100%保证,除非您还使用
with_for_update()
或可序列化事务模式。

按照@WoLpH的解决方案,这是适用于我的代码(简单版本):

有了它,我就可以得到或创建我模型的任何对象

假设我的模型对象是:

class Country(Base):
    __tablename__ = 'countries'
    id = Column(Integer, primary_key=True)
    name = Column(String, unique=True)
要获取或创建我的对象,我写:

myCountry = get_or_create(session, Country, name=countryName)
这工作做得很好,很优雅

要做的第一件事是定义一个函数,该函数提供一个要使用的会话,并将字典与跟踪当前唯一键的会话()相关联

在mixin中使用此函数的示例如下:

class UniqueMixin(object):
    @classmethod
    def unique_hash(cls, *arg, **kw):
        raise NotImplementedError()

    @classmethod
    def unique_filter(cls, query, *arg, **kw):
        raise NotImplementedError()

    @classmethod
    def as_unique(cls, session, *arg, **kw):
        return _unique(
                    session,
                    cls,
                    cls.unique_hash,
                    cls.unique_filter,
                    cls,
                    arg, kw
            )
最后创建唯一的get_或_create模型:

from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

engine = create_engine('sqlite://', echo=True)

Session = sessionmaker(bind=engine)

class Widget(UniqueMixin, Base):
    __tablename__ = 'widget'

    id = Column(Integer, primary_key=True)
    name = Column(String, unique=True, nullable=False)

    @classmethod
    def unique_hash(cls, name):
        return name

    @classmethod
    def unique_filter(cls, query, name):
        return query.filter(Widget.name == name)

Base.metadata.create_all(engine)

session = Session()

w1, w2, w3 = Widget.as_unique(session, name='w1'), \
                Widget.as_unique(session, name='w2'), \
                Widget.as_unique(session, name='w3')
w1b = Widget.as_unique(session, name='w1')

assert w1 is w1b
assert w2 is not w3
assert w2 is not w1

session.commit()

该配方更深入地阐述了这个想法,并提供了不同的方法,但我成功地使用了这一方法。

我一直在研究这个问题,最终得到了一个相当可靠的解决方案:

def get_one_或_create(会话、,
模型
创建方法=“”,
创建\u方法\u kwargs=None,
**kwargs):
尝试:
return session.query(model).filter_by(**kwargs).one(),False
除NoResultFound外:
update(创建方法或{})
created=getattr(model,create_方法,model)(**kwargs)
尝试:
会话。添加(已创建)
session.flush()
返回已创建,True
除完整性错误外:
会话。回滚()
return session.query(model).filter_by(**kwargs).one(),False
我只是写了一篇关于所有细节的文章,但是对于我为什么使用这个,我有一些相当好的想法

  • 它解压成一个元组,告诉你对象是否存在。这在您的工作流中通常很有用

  • 该函数提供了使用
    @classmethod
    装饰的创建者函数(以及特定于这些函数的属性)的能力

  • 当有多个进程连接到数据存储时,该解决方案可以防止争用情况

  • 编辑:我已将
    session.commit()
    更改为
    session.flush()
    ,如中所述。请注意,这些决定特定于所使用的数据存储(本例中为Postgres)


    编辑2:我在函数中使用{}作为默认值进行了更新,因为这是典型的Python gotcha。谢谢你,奈杰尔!如果您对此感到好奇,请查看并。

    最接近的语义可能是:

    def get_or_create(model, **kwargs):
        """SqlAlchemy implementation of Django's get_or_create.
        """
        session = Session()
        instance = session.query(model).filter_by(**kwargs).first()
        if instance:
            return instance, False
        else:
            instance = model(**kwargs)
            session.add(instance)
            session.commit()
            return instance, True
    
    不确定在sqlalchemy中依赖一个全局定义的
    会话是多么神圣,但是Django版本没有连接,所以

    返回的元组包含实例和一个布尔值,指示是否创建了实例(即,如果从数据库读取实例,则为False)


    Django的
    get\u或\u create
    通常用于确保全局数据可用,因此我将尽可能早地提交。

    根据您采用的隔离级别,上述解决方案均无效。 我找到的最佳解决方案是以下形式的原始SQL:

    INSERT INTO table(f1, f2, unique_f3) 
    SELECT 'v1', 'v2', 'v3' 
    WHERE NOT EXISTS (SELECT 1 FROM table WHERE f3 = 'v3')
    
    无论隔离级别和并行度如何,这都是事务安全的


    注意:为了提高效率,最好为unique列设置索引。

    erik的优秀索引的修改版

    • 使用a仅回滚新项的添加,而不是回滚所有内容(参见此以使用SQLite的嵌套事务)
    • 移动
      创建方法
      。如果创建的对象具有关系,并且通过这些关系为其分配了成员,则会自动将其添加到会话中。例如,创建一个
      book
      ,它将
      user\u id
      user
      作为对应关系,然后执行
      book。user=
      create\u方法的内部将
      book
      添加到会话中。这意味着
      create\u方法
      必须位于
      中,并带有
      ,才能从最终的回滚中获益。请注意,
      begin\u nested
      会自动触发刷新

    请注意,如果使用MySQL,则必须将事务隔离级别设置为
    readcommitted
    ,而不是
    REPEATABLE READ
    ,这样才能工作。Django(和)使用了相同的策略,另请参见Django。

    我稍微简化了@Kevin。避免将整个函数包装在
    if
    /
    else
    语句中的解决方案。这样,只有一个
    返回值
    ,我发现它更干净:

    def get_or_create(session, model, **kwargs):
        instance = session.query(model).filter_by(**kwargs).first()
    
        if not instance:
            instance = model(**kwargs)
            session.add(instance)
    
        return instance
    

    有一个Python包包含@erik的解决方案以及一个版本的
    update\u或\u create()

    我经常遇到的一个问题是,当一个字段有一个最大长度时(比如,
    STRING(40)
    ),而您想用一个较大长度的字符串执行
    get或create
    ,上述解决方案将失败

    基于上述解决方案,我的方法如下:

    from sqlalchemy import Column, String
    
    def get_or_create(self, add=True, flush=True, commit=False, **kwargs):
        """
    
        Get the an entity based on the kwargs or create an entity with those kwargs.
    
        Params:
            add: (default True) should the instance be added to the session?
            flush: (default True) flush the instance to the session?
            commit: (default False) commit the session?
            kwargs: key, value pairs of parameters to lookup/create.
    
        Ex: SocialPlatform.get_or_create(**{'name':'facebook'})
            returns --> existing record or, will create a new record
    
        ---------
    
        NOTE: I like to add this as a classmethod in the base class of my tables, so that
        all data models inherit the base class --> functionality is transmitted across
        all orm defined models.
    
        """
    
    
        # Truncate values if necessary
        for key, value in kwargs.items():
    
            # Only use strings
            if not isinstance(value, str):
                continue
    
            # Only use if it's a column
            my_col = getattr(self.__table__.columns, key)
    
            if not isinstance(my_col, Column):
                continue
    
            # Skip non strings again here
            if not isinstance(my_col.type, String):
                continue
    
            # Get the max length
            max_len = my_col.type.length
    
            if value and max_len and len(value) > max_len:
    
                # Update the value
                value = value[:max_len]
                kwargs[key] = value
    
        # -------------------------------------------------
    
        # Make the query...
        instance = session.query(self).filter_by(**kwargs).first()
    
        if instance:
            return instance
    
        else:
            # Max length isn't accounted for here.
            # The assumption is that auto-truncation will happen on the child-model
            # Or directtly in the db
            instance = self(**kwargs)
    
        # You'll usually want to add to the session
        if add:
            session.add(instance)
    
        # Navigate these with caution
        if add and commit:
            try:
                session.commit()
            except IntegrityError:
                session.rollback()
    
        elif add and flush:
            session.flush()
    
    
        return instance
    
    我认为在阅读“session.Query(model.filter_by(**kwargs.first()”)的地方,应该阅读“session.Query(model.filter
    INSERT INTO table(f1, f2, unique_f3) 
    SELECT 'v1', 'v2', 'v3' 
    WHERE NOT EXISTS (SELECT 1 FROM table WHERE f3 = 'v3')
    
    def get_one_or_create(session,
                          model,
                          create_method='',
                          create_method_kwargs=None,
                          **kwargs):
        try:
            return session.query(model).filter_by(**kwargs).one(), True
        except NoResultFound:
            kwargs.update(create_method_kwargs or {})
            try:
                with session.begin_nested():
                    created = getattr(model, create_method, model)(**kwargs)
                    session.add(created)
                return created, False
            except IntegrityError:
                return session.query(model).filter_by(**kwargs).one(), True
    
    def get_or_create(session, model, **kwargs):
        instance = session.query(model).filter_by(**kwargs).first()
    
        if not instance:
            instance = model(**kwargs)
            session.add(instance)
    
        return instance
    
    from sqlalchemy import Column, String
    
    def get_or_create(self, add=True, flush=True, commit=False, **kwargs):
        """
    
        Get the an entity based on the kwargs or create an entity with those kwargs.
    
        Params:
            add: (default True) should the instance be added to the session?
            flush: (default True) flush the instance to the session?
            commit: (default False) commit the session?
            kwargs: key, value pairs of parameters to lookup/create.
    
        Ex: SocialPlatform.get_or_create(**{'name':'facebook'})
            returns --> existing record or, will create a new record
    
        ---------
    
        NOTE: I like to add this as a classmethod in the base class of my tables, so that
        all data models inherit the base class --> functionality is transmitted across
        all orm defined models.
    
        """
    
    
        # Truncate values if necessary
        for key, value in kwargs.items():
    
            # Only use strings
            if not isinstance(value, str):
                continue
    
            # Only use if it's a column
            my_col = getattr(self.__table__.columns, key)
    
            if not isinstance(my_col, Column):
                continue
    
            # Skip non strings again here
            if not isinstance(my_col.type, String):
                continue
    
            # Get the max length
            max_len = my_col.type.length
    
            if value and max_len and len(value) > max_len:
    
                # Update the value
                value = value[:max_len]
                kwargs[key] = value
    
        # -------------------------------------------------
    
        # Make the query...
        instance = session.query(self).filter_by(**kwargs).first()
    
        if instance:
            return instance
    
        else:
            # Max length isn't accounted for here.
            # The assumption is that auto-truncation will happen on the child-model
            # Or directtly in the db
            instance = self(**kwargs)
    
        # You'll usually want to add to the session
        if add:
            session.add(instance)
    
        # Navigate these with caution
        if add and commit:
            try:
                session.commit()
            except IntegrityError:
                session.rollback()
    
        elif add and flush:
            session.flush()
    
    
        return instance