onupdate基于另一个具有sqlalchemy声明基础的字段

时间:2012-06-21 08:46:12

标签: python sqlalchemy pyramid

我使用sqlalchemy和金字塔框架,我想使用他的邮政编码将一个人链接到他的地理部门。 所以我在定义department_id列定义department_id时尝试使用onupdate参数。 看到以下代码:

from datetime import date
from emailing.models import Base, DBSession
from sqlalchemy import Column, Integer, Unicode, Text, DateTime, Sequence, Boolean, Date, UnicodeText, UniqueConstraint, Table, ForeignKey
from sqlalchemy.orm import scoped_session, sessionmaker, column_property, relationship, backref
from sqlalchemy.sql import func

class Person(Base):
    __tablename__ = u'person'
    id = Column(Integer, primary_key=True)

    firstName = Column(Unicode(255))
    lastName = Column(Unicode(255))

    created_at = Column(Date, default=func.now())
    updated_at = Column(Date, onupdate=func.now())

    department_id = Column(Integer(), ForeignKey('department.id'), onupdate=dep_id_from_postcode)
    department = relationship("Department", backref='persons')


    __table_args__ = (UniqueConstraint('firstName', 'lastName'), {})


    def dep_id_from_postcode(self):
        return int(self.postcode[:2]) 
更新updated_at字段时,

工作正常,但对于deparment_id字段,它告诉我:

  

NameError:名称'dep_id_from_postcode'未定义

我在这里找到了关于python执行函数的文档:http://docs.sqlalchemy.org/en/latest/core/schema.html?highlight=trigger#python-executed-functions 但是在onupdate参数中没有使用其他字段的东西。

我希望我很清楚,因为我不是“天生的英语演讲者” 谢谢大家

3 个答案:

答案 0 :(得分:7)

在使用之前移动函数定义:

class Person(Base):
    # ...
    def dep_id_from_postcode(self):
        return int(self.postcode[:2])
    # ...
    department_id = Column(Integer(), ForeignKey('department.id'), onupdate=dep_id_from_postcode)
    # ...

postcode真的是Person中的字段吗?因为如果不是,您可能需要完全不同地处理这个问题。例如,如果postcode派生自primary_address关系,则需要检查primary_address关系的添加/删除以及相关Address对象中的更改是否正确钩。

答案 1 :(得分:5)

SQLAlchemy具有在默认(即onupdate)函数中使用其他列值的特殊机制:上下文敏感默认函数 http://docs.sqlalchemy.org/en/rel_0_7/core/schema.html#context-sensitive-default-functions

  

关于默认情况的此上下文的典型用例   生成是为了访问要插入的其他值或   在行上更新。

正如Van指出的那样,您需要确保邮政编码是为Person定义的字段,或者您需要添加功能,以便获取与Person实例关联的邮政编码。

什么对我有用 - 常规功能,不受任何对象的约束。 SQLAlchemy将在插入和/或更新时调用它,并使用“context”传递特殊参数 - 这不是您要更新的实际对象。

所以对于你的例子,我会做这样的事情。

def dep_id_from_postcode(context):
    postcode = context.current_parameters['postcode']
    return int(postcode[:2])

class Person(Base):
    postcode = Column(String)
    # ...

    # ...
    department_id = Column(Integer(), ForeignKey('department.id'), onupdate=dep_id_from_postcode)
    # ...

小心这个上下文参数 - 如果我没有使用相同的操作更新'postcode'值,在某些情况下上下文具有None字段的值时,我最终会遇到问题。

带有调试器的带有pydev的Eclipse帮助我查看了哪些信息作为上下文传递。

答案 2 :(得分:0)

  

请谨慎使用此上下文参数-如果我不使用相同的操作更新“邮政编码”值,在某些情况下上下文具有“无”字段的值将导致一个问题。

@vvladymyrov提到他最终遇到的问题是,如果您使用的是未更新的字段值,则上下文将生成None。但是,您仍然需要计算另一个。

例如:

您具有firstName和lastName,这些名称将作为用户的输入。您还具有基于firstName和lastName计算的fullName。以下是您遵循的代码:

#The below code is the ERModel class in sqlalchemy
def calculateFullName(context):
    name = context.current_parameters['firstName'] + " " + context.current_parameters['lastName']
    return name

class User(Base):
    firstName= Column(String)
    lastName= Column(String)
    name= Column(String,default=calculateFullName,onupdate=calculateFullName)
    # ...
    # ...
# End of ER Model

现在让我们考虑一种情况,您想更新用户的lastName,并且在内部也应使用您可以实现的validateFullName函数来更新名称,但是如果您尝试执行以下操作,则需要警告:

user = session.query(User).filter(User.id=<id>).one() # Here you will get the specific user based on id.
user.lastName = "XXX" #This is the new value you want to be updated for the existing user.
session.update(user)
session.commit()

如前所述,上面将调用calculateFullName,您将获得上下文,但是在该上下文中。current_parameters的firstName将为None。 (由于您不更新firstName,因此如果需要,您将获得该值作为None,如果您可以打印(上下文。 dict )来检查您得到的内容)。

因此,我为这类情况找到的解决方案(“更新一列,该列取决于要更新的​​唯一一列的2列”)使用会话查询update()函数,如下所示:

query = session.query(User).filter(User.id=<id>)  # This will return query object
query.update({"lastName"="XXX", "firstName"=query.one().__dict__.get("firstName")},syncronize_session=False)

在更新lastName时,需要firstName来计算名称。因此,您需要将firstName发送到上下文。当您已经查询并将当前记录从数据库获取到内存时,可以使用该记录并将其发送到query.update(),以便在上下文对象中获取该记录。

注意:我采用的方法可能不是有效的方法。如果我有任何错误,请指导我。我很高兴学习。