【问题标题】:Can I use context-sensitive functions in SQLAlchemy with parameters?我可以在 SQLAlchemy 中使用带参数的上下文相关函数吗?
【发布时间】:2016-04-14 06:48:56
【问题描述】:

这是一个用于保存 IP 地址范围的小表格,以起始地址、结束地址和范围内的 IP 数量的形式显示

class IpRange(Base):
    __tablename__ = 'ip_range'
    ip_range_id = Column(Integer, Sequence('ip_range_id_seq'), primary_key=True)
    start_ip = Column(String(15))
    end_ip = Column(String(15))
    num_ips = Column(Integer)

我想做的是能够使用各种样式创建对象,并让类弄清楚如何填充自己的字段。

foo = IpRange(ip='192.168.0.1')
foo = IpRange(ip='192.168.0.0/24')
foo = IpRange(ip='192.168.0.0-192.168.0.255')

编写一个可以解析各种 IP 地址/范围符号的函数并不难:

def parseIp(desired_format):
    ... stuff to parse any valid IP address/network format ...
    if desired_format == 'start':
        return start_ip
    if desired_format == 'end':
        return end_ip
    if desired_format == 'num_ips':
        return num_ips

然后我希望我可以为我的列使用default 方法来获取每列所需的数据:

class IpRange(Base):
    __tablename__ = 'ip_range'
    ip_range_id = Column(Integer, Sequence('ip_range_id_seq'), primary_key=True)
    start_ip = Column(String(15), default=parseIp('start'))
    end_ip = Column(String(15), default=parseIp('end'))
    num_ips = Column(Integer, default=parseIp('num_ips')

但是,这不是有效的 SQLAlchemy 语法。文档谈到context-sensitive default columns,但语法不允许将参数传递给函数。因此,即使我可以使用 default=parseIp 调用该函数,我也无法告诉它我正在寻找什么样的返回值。

有没有办法在 SQLAlchemy 的列规范中做到这一点?

或者作为一个替代的想法,我应该把 parseIp 变成一个帮助脚本,它只生成新的 IpRange 对象本身并将它返回给调用者吗?比如:

def parseIp(ipstring):
    ... parse data ...
    return IpRange(start_ip=parsed_start_ip, end_ip=parsed_end_ip, num_ips=parsed_num_ips)

>>> ipobj = parseIp('192.168.0.0/24')

【问题讨论】:

    标签: python sqlalchemy


    【解决方案1】:

    只需重写构造函数。示例:

    class IpRange(Base):
        def __init__(self, ip=None, **kwargs):
            if ip is not None:
                self.start_ip, self.end_ip = my_super_awesome_cidr_parser(ip)
            else:
                super(IpRange, self).__init__(**kwargs)
    

    【讨论】:

    • 太棒了!我不完全理解它是如何工作的(最后一行特别神秘),但它确实有效。谢谢!
    • @GeorgeAdams 最后一行调用超类的 (Base) 构造函数,允许您使用默认行为,即通过关键字参数设置属性(例如 IpRange(start_ip="...", end_ip="..."))。 super() 的文档是 here。
    • 我想我明白了。正确的做法(如果没有指定“ip”)是用传统方式(foo = IpRange(start_ip='...'))创建一个IpRange 对象是有道理的。我想我认为我们需要将原始的__init__ 用于IpRange,但我相信您告诉我的是,IpRange 从来没有一个__init__,只有一个Base。另一个问题 - 你为什么打电话给super(IpRange, self) 而不仅仅是super(IpRange)?
    • @GeorgeAdams 简短回答:这就是你应该如何进行超类调用。长答案:它与多重继承有关。在处理多重继承时,解析方法的顺序称为方法解析顺序(mro),不能直接从类本身推断出来。例如,class Foo(IpRange, Bar): pass 的顺序为 Foo、IpRange、Bar、Base。如您所见,您调用哪个__init__ 方法取决于您拥有的对象,而不仅仅是类本身。
    猜你喜欢
    • 2018-12-07
    • 2012-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-09
    • 1970-01-01
    • 2016-11-02
    • 1970-01-01
    相关资源
    最近更新 更多