from __future__ import annotations from contextlib import closing from datetime import datetime from pycs import db from pycs.database.base import NamedBaseModel def compare_children(start_label: Label, id: int): """ check for cyclic relationships """ labels_to_check = [start_label] while labels_to_check: label = labels_to_check.pop(0) if label.id == id: return False labels_to_check.extend(label.children) return True class Label(NamedBaseModel): id = db.Column(db.Integer, primary_key=True) project_id = db.Column( db.Integer, db.ForeignKey("project.id", ondelete="CASCADE"), nullable=False) parent_id = db.Column( db.Integer, db.ForeignKey("label.id", ondelete="SET NULL")) created = db.Column(db.DateTime, default=datetime.utcnow, index=True, nullable=False) reference = db.Column(db.String) # contraints __table_args__ = ( db.UniqueConstraint('project_id', 'reference'), ) # relationships to other models parent = db.relationship("Label", backref="children", remote_side=[id]) serialize_only = ( "id", "name", "project_id", "parent_id", "reference", ) def set_parent(self, parent_id: int): """ set this labels parent :param parent_id: parent's id :return: """ if not compare_children(self, parent_id): raise ValueError('Cyclic relationship detected!') self.parent_id = parent_id self.commit()