SQLAlchemy中的每列时间戳

时间:2012-08-25 00:34:07

标签: python sqlalchemy

我正在尝试在我的SQLAlchemy数据源中的实体和从其他地方(外部REST API)引入的实体之间执行每个字段的合并。我想做的是这样的事情:

class Person:
    __tablename__ = "people"
    id = Column(Integer,primary_key=True)
    name = Column(String)
    nameDatestamp = Column(DateTime)
    address = Column(String)
    addressDatestamp = Column(DateTime)

def merge(myPerson, foreignPerson):
   if myPerson.nameDateStamp < foreignPerson.nameDateStamp:
      myPerson.name = foreignPerson.name
      myPerson.nameDateStamp = foreignPerson.nameDateStamp
   if myPerson.addressDatestamp < foreignPerson.addressDateStamp:
      myPerson.addressDatestamp = foreignPerson.addressDateStamp
      myPerson.address = foreignPerson.address

对于很多课程和很多领域。这似乎过于冗长而不是最佳实践。

  • 我可以介绍新的数据模型,例如DateStampedString(由日期戳和字符串组成),DateStampedRelationship等,但我担心使用多个表的额外间接将有助于合并的速度

  • 我可以使用带有参数的Python装饰器,这些参数可以在运行时和创建表之前动态地将其他日期戳列添加到我的模型中。

    @datestamp(name,address)
    class Person:
       ...
    
  • 也许我可以以某种方式利用sqlalchemy.types.TypeDecorator来构建一个新的数据类型,但似乎有兴趣从(opaque类型) - &gt;(sqlalchemy类型)转而不是将两种类型捆绑在一起。

我正在尝试做什么是最好的做法?

编辑:我正在寻找

  1. 一种更简洁的方式来声明某些字段具有日期戳
  2. 迭代datestamped字段的某种方式,以便我的合并函数不必知道它正在合并哪个键
  3. 在值更改时保持日期戳更新的某种方式

1 个答案:

答案 0 :(得分:3)

通用合并

您可以通过查询对象轻松迭代(thingdatestamp)。例如,如果您想获得地址和日期戳,您可以这样做:

session.query(Person.address, Person.addressDatestamp).all()

将返回一组(addressaddressDatestamp)元组。 (它们实际上是命名元组,但您可以只使用索引)。如果您想要更新一堆属性,则实际上并不需要这样做。动态执行此操作的一种方法是传递合并属性元组列表和作为(Person,foreignPerson)元组的查询,并执行以下操作:

attrs = [("address", "addressDatestamp"), ("name", "nameDatestamp")]
person_tuples = # some way to generate (Person, ForeignPerson) tuples
def merge(attrs, person_tuples):
    for person, foreign in person_tuples:
        for attr, date in attrs:
            if getattr(person, date) < getattr(foreign, date):
                setattr(person, attr) = getattr(foreign, attr)
                setattr(person, date) = getattr(foreign, date)

    return person_tuples

这将检查每个属性的日期戳,然后存储该属性,如果外来更新(+也存储日期)。

如果您的属性始终采用<attr><attr>Datestamp格式,那么您可以将其缩短为:

attrs = ["name", "address"]
def merge(attrs, person_tuples):
    for person, foreign in person_tuples:
        for attr in attrs:
            date = attr + "Datestamp"
            if getattr(person, date) < getattr(foreign, date):
                setattr(person, attr) = getattr(foreign, attr)
                setattr(person, date) = setattr(foreign, date)

如果有时可能不存在该属性,则可以将getattr调用更改为getattr(object, attr, default),并且不会引发错误。

动态类

如果您希望能够动态生成带有日期戳的模型,则可以使用元类(稍微复杂一点,特别是因为它会干扰SQLA的声明性基础等),或者您可以创建类工厂,如下所示:

def datestamped_factory(class_name, attrlist, timestamp="Datestamp", superclass_list=None):
    superclass_list = superclass or (object,)
    cols = dict((attr, Column(String)) for attr in attrlist)
    cols.update(dict((attr + timestamp, Column(DateTime)) for attr in attrlist)
    cols["timestamped_attrs"] = attrlist
    # create a merge specific to the class (so only need to pass person_tuples)
    cols["merge"] = classmethod(lambda cls, person_tuples: merge(cls.timestamped_attrs, person_tuples))
    return type(class_name, superclass_list, cols)

(可以使用cols["class_merge"] = classmethod(lambda cls, person_tuples: merge(cls.timestamped_attrs, person_tuples)将其添加到工厂中)))

要创建人员方法,您可以执行以下操作:

class Base(sqlalchemy.declarative_base()):
     id = Column(Integer, primary_key=True)

Person = datestamped__factory("Person", ["name", "address"], superclass_list = (Base,))
Person.__tablename__ = "person"

(将sqlalchemy.declarative_base()替换为您正在使用的任何基类...假设您正在使用ORM。

你可以变得更好,并编写一个元类,它找到所有字符串并为它们添加日期戳+创建适当的合并并创建适当更新时间戳的方法,但这可能比你需要的更加花哨。