【问题标题】:Keep data ordered in db by order number in Flask-sqlalchemy在 Flask-sqlalchemy 中按订单号在 db 中对数据进行排序
【发布时间】:2020-12-12 18:22:26
【问题描述】:

我有一个 db(sqlite),其中包含防火墙和策略规则定义。而且我需要将每条记录按顺序排列在他们的防火墙中。我的防火墙和 PolicyRule 模型关系是多对多的。所以我将排名(订单号)存储在关联模型中。即使我在记录中间插入数据,我怎样才能让它们保持有序?

例如:

我按以下顺序使用 order_by 获取记录:

1- Rule A 
2- Rule B 
3- Rule C 

然后我想在规则B和规则C之间添加规则D。所以我的下一个查询结果必须是这样的:

1- Rule A
2- Rule B
3- Rule D
4- Rule C

我需要知道规则的确切顺序,因为我将它们应用于 iptables 并且 iptables 策略必须与用户的顺序完全相同。

这是我的模型:

class PolicyRule(db.Model):
    __tablename__ = 'policy_rule'
    id = db.Column(db.Integer(), primary_key=True)
    active = db.Column('is_active', db.Boolean(), nullable=False, server_default='1')
    name = db.Column(db.String(255, collation='NOCASE'), nullable=False, unique=True)
    rule_type = db.Column(db.String(255, collation='NOCASE'), nullable=False) # IPv4 or IPv6
    direction = db.Column(db.Text())
    action = db.Column(db.Text())
    comment = db.Column(db.Text())
    log = db.Column(db.Boolean(), nullable=False, server_default='1')
    firewalls = db.relationship("FwPolicyRules", back_populates="rule")

    # Foreign key assignments for relationships
    src_addr_id = db.Column(db.Integer(), db.ForeignKey(Address.id, ondelete='CASCADE'))
    dst_addr_id = db.Column(db.Integer(), db.ForeignKey(Address.id, ondelete='CASCADE'))
    src_service_id = db.Column(db.Integer(), db.ForeignKey(Service.id, ondelete='CASCADE'))
    dst_service_id = db.Column(db.Integer(), db.ForeignKey(Service.id, ondelete='CASCADE'))
    interface_id = db.Column(db.Integer(), db.ForeignKey(Interface.id, ondelete='CASCADE'))
    time_profile_id = db.Column(db.Integer(), db.ForeignKey(TimeProfile.id, ondelete='CASCADE'))
    
    # Relationship definitions for access the objects directly like "policy_rule.src_addr".
    src_addr = db.relationship("Address", foreign_keys=[src_addr_id], lazy='subquery', backref=db.backref("policy_src_addr", uselist=True))
    src_port = db.relationship("Service", foreign_keys=[src_service_id], lazy='subquery',backref=db.backref("policy_src_port", uselist=True))
    dst_addr = db.relationship("Address", foreign_keys=[dst_addr_id], lazy='subquery',backref=db.backref("policy_dst_addr", uselist=True))
    dst_port = db.relationship("Service", foreign_keys=[dst_service_id], lazy='subquery',backref=db.backref("policy_dst_port", uselist=True))
    interface = db.relationship("Interface", foreign_keys=[interface_id], lazy='subquery',backref=db.backref("policy_interface", uselist=True))
    time_profile = db.relationship("TimeProfile", foreign_keys=[time_profile_id], lazy='subquery',backref=db.backref("policy_time_profile", uselist=True))


class Firewall(db.Model):
    __tablename__ = 'firewall'
    id = db.Column(db.Integer(), primary_key=True)
    name = db.Column(db.String(255, collation='NOCASE'), nullable=False, unique=True)

    policy_rules = db.relationship("FwPolicyRules", back_populates="firewall", lazy='subquery', cascade="delete-orphan")
    nat_rules = db.relationship("FwNatRules", back_populates="firewall", lazy='subquery', cascade="delete-orphan")
    routing_rules = db.relationship("FwRoutingRules", back_populates="firewall", lazy='subquery', cascade="delete-orphan")

    interfaces = db.relationship('Interface', secondary=interfaces, lazy='subquery', backref=db.backref('used_firewalls', lazy=True, uselist=True))

class FwPolicyRules(db.Model):
    __tablename__ = 'fw_policy_rules'

    id = db.Column(db.Integer, primary_key=True)
    firewall_id = db.Column(db.Integer, db.ForeignKey('firewall.id', ondelete='cascade'))
    policy_rule_id = db.Column(db.Integer, db.ForeignKey('policy_rule.id', ondelete='cascade'))
    rank = db.Column(db.Integer, autoincrement=True)
    rule = db.relationship("PolicyRule", back_populates="firewalls", lazy='subquery')
    firewall = db.relationship("Firewall", back_populates="policy_rules", lazy='subquery')

编辑:我认为尽管有很多解释,但没有人完全理解我的问题。要清楚,我想要这个的多对多版本: https://docs.sqlalchemy.org/en/13/orm/extensions/orderinglist.html

【问题讨论】:

  • 不是一个正式的答案,但您使用 SQLite 遵循的一般模式是不担心 SQL 中数据的内部顺序。这是因为,一般来说,真的根本没有任何内部秩序。相反,如果您想以特定顺序查看您的数据,只需使用适当的ORDER BY 子句。并且,可能考虑使用索引调整查询,这也有助于排序步骤。
  • 也许你的“rank”可能是一个浮点数而不是一个整数,所以如果你需要在两个现有记录之间插入一条新记录,你可以将它设置在它们的“rank”值之间。

标签: python sqlite flask sqlalchemy flask-sqlalchemy


【解决方案1】:

我终于找到了解决方案

这是我用python的方法:

我用这个导入了订购清单:

from sqlalchemy.ext.orderinglist import ordering_list

然后我修改了我的防火墙模型并添加了默认的 order_by 参数。之后 collection_class=ordering_list('rank') 参数起到了作用。

class Firewall(db.Model):
__tablename__ = 'firewall'
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(255, collation='NOCASE'), nullable=False, unique=True)

policy_rules = db.relationship("FwPolicyRules", back_populates="firewall", lazy='subquery', cascade="save-update, merge, delete, delete-orphan", order_by="FwPolicyRules.rank" ,collection_class=ordering_list('rank'))
nat_rules = db.relationship("FwNatRules", back_populates="firewall", lazy='subquery', cascade="save-update, merge, delete, delete-orphan", order_by="FwNatRules.rank" ,collection_class=ordering_list('rank'))
routing_rules = db.relationship("FwRoutingRules", back_populates="firewall", lazy='subquery', cascade="save-update, merge, delete, delete-orphan", order_by="FwRoutingRules.rank" ,collection_class=ordering_list('rank'))

interfaces = db.relationship('Interface', secondary=interfaces, lazy='subquery', backref=db.backref('used_firewalls', lazy=True, uselist=True))

然后我只是像这样向防火墙添加新规则:

db.session.autoflush = False # This is important because of "cascade = delete-orphan" parameter. Otherwise PolicyRule will be deleted before its added.
new_rule = PolicyRule(...)
a = FwPolicyRules(rule=new_rule)
fw.policy_rules.insert(rank, a) # Alternatively you can use "fw.policy_rules.append(a)" for auto add rule to the end
db.session.autoflush = True
db.session.commit()

为了改变现有规则的顺序,我写了一个函数:

def change_rule_rank(rule_rank, new_rank):
    db.session.autoflush = False
    rule_assoc = fw.policy_rules.pop(rule_rank)
    a = FwPolicyRules(rule=rule_assoc.rule)
    fw.policy_rules.insert(new_rank, a)
    db.session.autoflush = True
    db.session.commit()

希望它对某人有所帮助。

【讨论】:

    猜你喜欢
    • 2018-10-07
    • 1970-01-01
    • 1970-01-01
    • 2018-05-09
    • 2016-03-15
    • 1970-01-01
    • 1970-01-01
    • 2022-01-26
    • 2014-02-23
    相关资源
    最近更新 更多