简体   繁体   English

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

[英]onupdate based on another field with sqlalchemy declarative base

I use sqlalchemy with the pyramid framework, and i want to link a person to his geographical department using his postcode. 我使用sqlalchemy和金字塔框架,我想用他的邮政编码将一个人链接到他的地理部门。 So i try to use the onupdate argument when defining the department_id column define the department_id. 所以我在定义department_id列定义department_id时尝试使用onupdate参数。 see fallowing code: 看到以下代码:

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

on update for the updated_at field works fine, but for the deparment_id field it tell my: 更新的updated_at字段工作正常,但对于deparment_id字段,它告诉我:

NameError: name 'dep_id_from_postcode' is not defined NameError:未定义名称'dep_id_from_postcode'

i've found documentation about python executed function here: http://docs.sqlalchemy.org/en/latest/core/schema.html?highlight=trigger#python-executed-functions but nothing that uses another field to use in onupdate argument. 我在这里找到了关于python执行函数的文档: http ://docs.sqlalchemy.org/en/latest/core/schema.html?highlight = strigger#python-executed-functions但没有使用其他字段在onupdate中使用论点。

i hope i'm clear enought as i'm not a "natural english speaker" Thank you all 我希望我能说清楚,因为我不是“天生的英语演讲者”。谢谢大家

Move the function definition before its usage: 在使用之前移动函数定义:

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)
    # ...

Is the postcode really a field directly in Person ? postcode真的是直接在Person的字段吗? Because if it is not, you might need to handle this completely differently. 因为如果不是,您可能需要完全不同地处理这个问题。 For example, if the postcode is derived from the primary_address relationship, you need to check add/remove of the primary_address relationships and the changes in the related Address object for proper hooking. 例如,如果postcode是从primary_address关系派生的,则需要检查primary_address关系的添加/删除以及相关Address对象中的更改,以便正确挂钩。

SQLAlchemy has special mechanism for using other column value in default (ie onupdate) function called: Context-Sensitive Default Functions http://docs.sqlalchemy.org/en/rel_0_7/core/schema.html#context-sensitive-default-functions : SQLAlchemy具有在默认(即onupdate)函数中使用其他列值的特殊机制:上下文敏感默认函数http://docs.sqlalchemy.org/en/rel_0_7/core/schema.html#context-sensitive-default-functions

The typical use case for this context with regards to default generation is to have access to the other values being inserted or updated on the row. 关于默认生成的此上下文的典型用例是访问在行上插入或更新的其他值。

As Van pointed out you need to make sure that postcode is a field defined for Person or you'll need to add functionality that will take care about getting postcode associated with Person instance. 正如Van指出的那样,您需要确保邮政编码是为Person定义的字段,或者您需要添加将关注获取与Person实例关联的邮政编码的功能。

What worked for me - regular function, not bound to any object. 什么对我有用 - 常规功能,不受任何对象的约束。 SQLAlchemy will call it at the time of insert and/or update and pass special argument with "context" - which is not actual object you are updating. SQLAlchemy将在插入和/或更新时调用它,并使用“context”传递特殊参数 - 这不是您正在更新的实际对象。

So for your example I would do something like this. 所以对于你的例子,我会做这样的事情。

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)
    # ...

Be careful with this context argument - I end up with problem when context had None field's value in some cases if I not updating 'postcode' value with the same operation. 小心这个上下文参数 - 如果我没有使用相同的操作更新'postcode'值,在某些情况下上下文具有None字段的值时,我最终会遇到问题。

Eclipse with pydev with debugger helped me to see what information is passed as context. 带有调试器的带有pydev的Eclipse帮助我查看了哪些信息作为上下文传递。

Be careful with this context argument - I end up with a problem when context had None field's value in some cases if I not updating 'postcode' value with the same operation. 小心这个上下文参数 - 如果我没有使用相同的操作更新'postcode'值,在某些情况下上下文具有None字段值时,我会遇到问题。

As @vvladymyrov mentioned that he ended up with the problem that context will produce None if in case you are using field values which you are not updating. 正如@vvladymyrov提到的那样,如果您使用的字段值没有更新,他最终会遇到上下文无效的问题。 But still, you need to calculate another one. 但是,你还需要计算另一个。

For Example: 例如:

You have firstName and lastName that will be taken as input from a user. 您有firstName和lastName将作为用户的输入。 You also have fullName that you calculate based on firstName and lastName. 您还有基于firstName和lastName计算的fullName。 The following is the code which you follow: 以下是您遵循的代码:

#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

Now let's consider a case where you want to update a user's lastName and internally the name also should get updated by using the calculateFullName function you can achieve it but there is a caveat if you try to do the following: 现在让我们考虑一个你想要更新用户的lastName的情况,并且在内部也应该通过使用你可以实现的calculateFullName函数来更新名称,但是如果你尝试执行以下操作则需要注意:

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

As mentioned, the above will call calculateFullName and you will get the context but in that context.current_parameters, the firstName will be None. 如上所述,上面将调用calculateFullName,您将获得上下文但在该context.current_parameters中,firstName将为None。 (As you are not updating firstName you will get that value as None, if you want you can print(context. dict ) to check what you are getting). (因为你没有更新firstName,你将获得该值为None,如果你想要你可以打印( context.dict )来检查你得到了什么)。

So the solution that I found for these kinds of cases (Update a column which depends on 2 columns on which the only one column is getting updated) is using session query update() function as following: 因此,我为这些情况找到的解决方案(更新一个依赖于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)

As you are updating lastName and you need firstName to calculate the name. 在更新lastName时,需要firstName来计算名称。 So you need to send firstName to context. 所以你需要将firstName发送到上下文。 As you already queried and get the current record from database to memory you can use that and send to query.update() such that you will get that in the context object. 当你已经查询并从数据库到内存获取当前记录时,你可以使用它并发送到query.update(),这样你就可以在上下文对象中获得它。

Note: The approach that I took may not be the efficient one. 注意:我采取的方法可能不是有效的方法。 Please guide me if I did any mistake. 如果我有任何错误,请指导我。 I am happy to learn. 我很高兴学习。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM