Files
classifieds/app/models/category.py
T
2026-07-13 16:52:00 -04:00

57 lines
2.3 KiB
Python

"""Category model. Self-referential (parent_id) for subcategories.
`field_schema` is admin-editable JSON describing category-specific listing
fields. Shape:
{
"fields": [
{"name":"condition","label":"Condition","type":"select",
"required":true,"options":["new","like_new","good","fair"],"hot":true},
{"name":"brand","label":"Brand","type":"text","required":false,"max":80},
{"name":"salary_min","label":"Min salary","type":"number","required":false,"hot":true}
]
}
Supported types: text, number, select, bool. `hot:true` marks a field whose
value is denormalized onto an indexed Listing column for fast filtering.
"""
from datetime import datetime
from app.utils.time import utcnow
from sqlalchemy import JSON
from app.extensions import db
class Category(db.Model):
__tablename__ = "categories"
id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"),
primary_key=True, autoincrement=True)
slug = db.Column(db.String(60), unique=True, nullable=False, index=True)
name = db.Column(db.String(80), nullable=False)
parent_id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"),
db.ForeignKey("categories.id"), nullable=True, index=True)
field_schema = db.Column(JSON, nullable=False, default=dict)
icon = db.Column(db.String(40), nullable=True)
sort_order = db.Column(db.Integer, nullable=False, default=0)
sponsor_id = db.Column(db.BigInteger, nullable=True) # FK wired in Phase 5
is_active = db.Column(db.Boolean, nullable=False, default=True)
created_at = db.Column(db.DateTime, default=utcnow, nullable=False)
updated_at = db.Column(db.DateTime, default=utcnow,
onupdate=utcnow, nullable=False)
children = db.relationship("Category", backref=db.backref("parent",
remote_side=[id]), lazy="selectin")
listings = db.relationship("Listing", back_populates="category", lazy="dynamic")
@property
def is_subcategory(self):
return self.parent_id is not None
def fields(self):
return (self.field_schema or {}).get("fields", [])
def hot_fields(self):
return [f for f in self.fields() if f.get("hot")]
def __repr__(self):
return f"<Category {self.slug}>"