"""Custom fields: site-defined extra attributes per asset type. A CustomField is a definition scoped to one asset type (machine, computer, printer, network_device). A CustomFieldValue holds one asset's value for one field. This is the generic form of the built-in identifier columns - sites add their own attributes without a schema change. """ from shopdb.extensions import db # Allowed datatypes for a custom field. Values are always stored as text and # cast on render/input by the frontend. CUSTOM_FIELD_DATATYPES = ('text', 'number', 'date', 'boolean', 'select') class CustomField(db.Model): __tablename__ = 'customfields' fieldid = db.Column(db.Integer, primary_key=True) assettypeid = db.Column( db.Integer, db.ForeignKey('assettypes.assettypeid'), nullable=False, comment='Which asset category this field applies to' ) # Machine-name key, unique per asset type. Used for stable references. fieldkey = db.Column(db.String(50), nullable=False) label = db.Column(db.String(150), nullable=False) datatype = db.Column(db.String(20), nullable=False, server_default='text') # JSON array of options, only meaningful when datatype='select' options = db.Column(db.Text) showondetail = db.Column(db.Boolean, nullable=False, server_default='1') showonform = db.Column(db.Boolean, nullable=False, server_default='1') sortorder = db.Column(db.Integer, nullable=False, server_default='0') isactive = db.Column(db.Boolean, nullable=False, server_default='1') # When true, this field's values are matched by global search. searchable = db.Column(db.Boolean, nullable=False, server_default='0') __table_args__ = ( db.UniqueConstraint('assettypeid', 'fieldkey', name='uq_customfield_type_key'), ) def to_dict(self): import json parsed_options = [] if self.options: try: parsed_options = json.loads(self.options) except (ValueError, TypeError): parsed_options = [] return { 'fieldid': self.fieldid, 'assettypeid': self.assettypeid, 'fieldkey': self.fieldkey, 'label': self.label, 'datatype': self.datatype, 'options': parsed_options, 'showondetail': bool(self.showondetail), 'showonform': bool(self.showonform), 'sortorder': self.sortorder, 'isactive': bool(self.isactive), 'searchable': bool(self.searchable), } class CustomFieldValue(db.Model): __tablename__ = 'customfieldvalues' valueid = db.Column(db.Integer, primary_key=True) fieldid = db.Column( db.Integer, db.ForeignKey('customfields.fieldid', ondelete='CASCADE'), nullable=False ) assetid = db.Column( db.Integer, db.ForeignKey('assets.assetid', ondelete='CASCADE'), nullable=False ) value = db.Column(db.Text) field = db.relationship('CustomField') __table_args__ = ( db.UniqueConstraint('fieldid', 'assetid', name='uq_customfieldvalue_field_asset'), ) def to_dict(self): return { 'valueid': self.valueid, 'fieldid': self.fieldid, 'assetid': self.assetid, 'value': self.value, }