mirror of
https://github.com/Comfy-Org/ComfyUI.git
synced 2026-09-28 17:08:11 -05:00
156 lines
5.2 KiB
Python
156 lines
5.2 KiB
Python
"""Declares the asset schema: content rows describing bytes on disk, records
|
|
describing what a user sees, and the asset_tags table hanging off them.
|
|
The split is the point — many records can name one content row, and retiring
|
|
content by marking it missing rather than deleting it is what keeps a path's
|
|
history intact. Constraints declared here, not application code, are what make
|
|
a negative size or a second live row at one path impossible.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import uuid
|
|
from datetime import datetime
|
|
from typing import Any
|
|
|
|
from sqlalchemy import (
|
|
JSON,
|
|
BigInteger,
|
|
Boolean,
|
|
CheckConstraint,
|
|
DateTime,
|
|
ForeignKey,
|
|
Index,
|
|
String,
|
|
Text,
|
|
text,
|
|
)
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
|
|
from app.assets.helpers import get_utc_now
|
|
from app.database.models import Base
|
|
|
|
|
|
class AssetContent(Base):
|
|
__tablename__ = "asset_contents"
|
|
|
|
id: Mapped[str] = mapped_column(
|
|
String(36), primary_key=True, default=lambda: str(uuid.uuid4())
|
|
)
|
|
hash: Mapped[str | None] = mapped_column(String(256), index=True)
|
|
size_bytes: Mapped[int] = mapped_column(
|
|
BigInteger,
|
|
CheckConstraint("size_bytes >= 0", name="ck_asset_contents_size_nonneg"),
|
|
nullable=False,
|
|
default=0,
|
|
)
|
|
path: Mapped[str] = mapped_column(Text, nullable=False)
|
|
mtime_ns: Mapped[int | None] = mapped_column(
|
|
BigInteger,
|
|
CheckConstraint("mtime_ns >= 0", name="ck_asset_contents_mtime_nonneg"),
|
|
)
|
|
is_missing: Mapped[bool] = mapped_column(
|
|
Boolean, nullable=False, server_default="0"
|
|
)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=False), nullable=False, default=get_utc_now
|
|
)
|
|
|
|
records: Mapped[list[Asset]] = relationship(back_populates="content")
|
|
|
|
__table_args__ = (
|
|
Index(
|
|
"uq_asset_contents_path_live",
|
|
"path",
|
|
unique=True,
|
|
sqlite_where=text("is_missing = 0"),
|
|
),
|
|
)
|
|
|
|
|
|
class Asset(Base):
|
|
__tablename__ = "assets"
|
|
|
|
id: Mapped[str] = mapped_column(
|
|
String(36), primary_key=True, default=lambda: str(uuid.uuid4())
|
|
)
|
|
content_id: Mapped[str] = mapped_column(
|
|
String(36),
|
|
ForeignKey("asset_contents.id", ondelete="RESTRICT"),
|
|
nullable=False,
|
|
)
|
|
name: Mapped[str] = mapped_column(String(512), nullable=False)
|
|
mime_type: Mapped[str | None] = mapped_column(String(255))
|
|
system_metadata: Mapped[dict[str, Any] | None] = mapped_column(JSON(none_as_null=True))
|
|
job_id: Mapped[str | None] = mapped_column(String(36))
|
|
user_metadata: Mapped[dict[str, Any] | None] = mapped_column(JSON)
|
|
loader_path: Mapped[str | None] = mapped_column(Text)
|
|
preview_id: Mapped[str | None] = mapped_column(
|
|
String(36), ForeignKey("assets.id", ondelete="SET NULL")
|
|
)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=False), nullable=False, default=get_utc_now
|
|
)
|
|
# Explicit user/API edit time; never use onupdate because row writes are not always edits.
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=False), nullable=False, default=get_utc_now
|
|
)
|
|
last_access_time: Mapped[datetime | None] = mapped_column(DateTime(timezone=False))
|
|
|
|
content: Mapped[AssetContent] = relationship(back_populates="records", lazy="selectin")
|
|
preview: Mapped[Asset | None] = relationship(
|
|
"Asset", foreign_keys=[preview_id], remote_side=lambda: [Asset.id]
|
|
)
|
|
tag_links: Mapped[list[AssetTag]] = relationship(
|
|
back_populates="asset", cascade="all,delete-orphan", passive_deletes=True
|
|
)
|
|
tags: Mapped[list[Tag]] = relationship(
|
|
secondary="asset_tags", back_populates="assets", viewonly=True, lazy="selectin"
|
|
)
|
|
|
|
__table_args__ = (
|
|
Index("ix_assets_content_id", "content_id"),
|
|
Index("ix_assets_name", "name"),
|
|
Index("ix_assets_created_at", "created_at"),
|
|
Index("ix_assets_preview_id", "preview_id"),
|
|
)
|
|
|
|
|
|
class AssetTag(Base):
|
|
__tablename__ = "asset_tags"
|
|
|
|
asset_id: Mapped[str] = mapped_column(
|
|
String(36), ForeignKey("assets.id", ondelete="CASCADE"), primary_key=True
|
|
)
|
|
tag_name: Mapped[str] = mapped_column(
|
|
String(512), ForeignKey("tags.name", ondelete="RESTRICT"), primary_key=True
|
|
)
|
|
origin: Mapped[str] = mapped_column(String(32), nullable=False, default="manual")
|
|
added_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=False), nullable=False, default=get_utc_now
|
|
)
|
|
|
|
asset: Mapped[Asset] = relationship(back_populates="tag_links")
|
|
tag: Mapped[Tag] = relationship(back_populates="asset_links")
|
|
|
|
__table_args__ = (
|
|
Index("ix_asset_tags_tag_name", "tag_name"),
|
|
Index("ix_asset_tags_asset_id", "asset_id"),
|
|
)
|
|
|
|
|
|
class Tag(Base):
|
|
__tablename__ = "tags"
|
|
|
|
name: Mapped[str] = mapped_column(String(512), primary_key=True)
|
|
asset_links: Mapped[list[AssetTag]] = relationship(back_populates="tag")
|
|
assets: Mapped[list[Asset]] = relationship(
|
|
secondary="asset_tags", back_populates="tags", viewonly=True
|
|
)
|
|
|
|
|
|
class AssetSystemState(Base):
|
|
__tablename__ = "asset_system_state"
|
|
|
|
key: Mapped[str] = mapped_column(String(256), primary_key=True)
|
|
value: Mapped[str] = mapped_column(Text, nullable=False)
|