使用setattr和__init__清理SQLAlchemy数据

时间:2017-01-30 12:26:52

标签: python constructor sqlalchemy

我正在尝试清理一些我想要添加到SQLAlchemy数据库的绳索数据以确保它是正确的类型。所以我试图例如将字符串截断为正确的列长度。

我尝试过创建一个构造函数,然后使用getattr()setattr()来强制执行此操作。但由于某些原因,字符串没有被截断等等。有什么建议吗?

class Property(Base):
    """
    Property details as imported from various Council sources
    """
    MAXPROPREFLEN  =  20
    MAXADDRESSLEN  = 100
    MAXDESCRIPLEN  = 120
    MAXPOSTCODELEN =  10

    __tablename__  = 'properties'
    id             = Column(Integer, primary_key=True)
    PropertyRef    = Column(String(MAXPROPREFLEN)) # Council reference, diffrerent from UPRN
    AccountHolder  = Column(String(MAXDESCRIPLEN))
    Address1       = Column(String(MAXADDRESSLEN))
    Address2       = Column(String(MAXADDRESSLEN))
    Address3       = Column(String(MAXADDRESSLEN))
    Address4       = Column(String(MAXADDRESSLEN))
    PostCode       = Column(String(MAXPOSTCODELEN), index=True)
    UPRN           = Column(BigInteger)
    Description    = Column(String(MAXDESCRIPLEN))
    RV             = Column(Numeric(10, 0))
    Empty          = Column(Boolean)
    LiableFrom     = Column(Date)
    EmptySince     = Column(Date)
    MEBID          = Column(Integer) # Key in MEB table if applicable
    Authority      = Column(Integer) # Key in authorities table

    def __init__(self, **kwargs):
        """
        Ordinarily we wouldn't require a constructor, but the data from the
        various LAs is of such poor quality and the Psycopg2 connector
        so strict about types that we have to clean it up. So we need to 
        truncate overly long strings etc.
        """
        for key, value in kwargs.items():
            if key == 'PropertyRef':
                setattr(self, key, value[:Property.MAXPROPREFLEN] if value else None)
            elif key == 'PostCode':
                setattr(self, key, value[:Property.MAXPOSTCODELEN] if value else None)
            elif key in ['AccountHolder', 'Description']:
                if type(value) is str:
                    setattr(self, key, value[:Property.MAXDESCRIPLEN])
                else:
                    setattr(self, key, None)
            elif key in ['Address1', 'Address2', 'Address3', 'Address4']:
                setattr(self, key, value[:Property.MAXADDRESSLEN] if value else None)
            elif key in ['LiableFrom','EmptySince']: 
                if type(value) == datetime.datetime:
                    setattr(self, key, value.date())
                elif type(value) == datetime.date:
                    setattr(self, key, value)
                else:
                    setattr(self, key, None)
            if key == 'UPRN':
                if type(value) is str:
                    try:
                       setattr(self, key, int(value))
                    except ValueError:
                        setattr(self, key, None)
                elif type(value) is int:
                    setattr(self, key, value)
                else:
                    setattr(self, key, None)
            else:
                setattr(self, key, value)

更新

修复了感谢Piotr Dawidiuk。它现在

class Property(Base):
    """
    Property details as imported from various Council sources
    """
    MAXPROPREFLEN  =  20
    MAXADDRESSLEN  =  80
    MAXDESCRIPLEN  =  80
    MAXPOSTCODELEN =  10

    __tablename__  = 'properties'
    id             = Column(Integer, primary_key=True)
    PropertyRef    = Column(String(MAXPROPREFLEN)) # Council reference, diffrerent from UPRN
    AccountHolder  = Column(String(MAXDESCRIPLEN))
    Address1       = Column(String(MAXADDRESSLEN))
    Address2       = Column(String(MAXADDRESSLEN))
    Address3       = Column(String(MAXADDRESSLEN))
    Address4       = Column(String(MAXADDRESSLEN))
    PostCode       = Column(String(MAXPOSTCODELEN), index=True)
    UPRN           = Column(BigInteger)
    Description    = Column(String(MAXDESCRIPLEN))
    RV             = Column(Numeric(10, 0))
    Empty          = Column(Boolean)
    LiableFrom     = Column(Date)
    EmptySince     = Column(Date)
    MEBID          = Column(Integer) # Key in MEB table if applicable
    Authority      = Column(Integer) # Key in authorities table


    @validates('PropertyRef', 'AccountHolder', 'Description', 
               'Address1', 'Address2', 'Address3', 'Address4', 'PostCode')
    def ValidateString(self, key, value):
        maxlengths = {'PropertyRef':   Property.MAXPROPREFLEN,
                      'AccountHolder': Property.MAXDESCRIPLEN,
                      'Description':   Property.MAXDESCRIPLEN,
                      'Address1':      Property.MAXADDRESSLEN,
                      'Address2':      Property.MAXADDRESSLEN,
                      'Address3':      Property.MAXADDRESSLEN,
                      'Address4':      Property.MAXADDRESSLEN,
                      'PostCode':      Property.MAXPOSTCODELEN
                      }

        if type(value) is str:
            value = value.strip().upper()
            if len(value) > maxlengths[key]:
                logger.debug("Timmming {} <{}> to <{}> ({} to {} chars)".format(
                             key, value, value[:maxlengths[key]], 
                             len(value), maxlengths[key]))
            return value[:maxlengths[key]]
        else:
            return None

    @validates('LiableFrom', 'EmptySince')
    def ValidateDate(self, key, value):
        if type(value) == datetime.datetime:
            return value.date()
        elif type(value) == datetime.date:
            return value
        else:
            return None

    @validates('UPRN')
    def ValidateInteger(self, key, value):
        try:
            return int(value)
        except:
            return None

    @validates('RV')
    def ValidateFloat(self, key, value):
        try:
            return float(value)
        except:
            return None

当你知道怎么做时很容易!

1 个答案:

答案 0 :(得分:1)

不要这样做。有一个概念 - validates decorator

  

属性验证器可以引发异常,暂停进程   改变属性的值,或可以改变给定的值   不同的东西

只需在验证器中返回已修改,已过滤,干净的数据。

另请参阅Changing Attribute Behavior文档部分。