feat: modules and module-editor pages
This commit is contained in:
@ -1,13 +1,15 @@
|
||||
from sqlalchemy.orm import configure_mappers
|
||||
|
||||
from modules.fulfillment_base.models import * # noqa: F401
|
||||
from .attribute import (
|
||||
AttributeType as AttributeType,
|
||||
Attribute as Attribute,
|
||||
AttributeValue as AttributeValue,
|
||||
AttributeLabel as AttributeLabel,
|
||||
module_attribute as module_attribute,
|
||||
)
|
||||
from .base import BaseModel as BaseModel
|
||||
from .board import Board as Board
|
||||
from .module import ( # noqa: F401
|
||||
Module as Module,
|
||||
project_module as project_module,
|
||||
module_dependencies as module_dependencies,
|
||||
)
|
||||
from .deal import Deal as Deal
|
||||
from .deal_group import DealGroup as DealGroup
|
||||
from .deal_tag import (
|
||||
@ -15,6 +17,11 @@ from .deal_tag import (
|
||||
DealTagColor as DealTagColor,
|
||||
deals_deal_tags as deals_deal_tags,
|
||||
)
|
||||
from .module import ( # noqa: F401
|
||||
Module as Module,
|
||||
project_module as project_module,
|
||||
module_dependencies as module_dependencies,
|
||||
)
|
||||
from .project import Project as Project
|
||||
from .status import Status as Status, DealStatusHistory as DealStatusHistory
|
||||
|
||||
|
||||
139
models/attribute.py
Normal file
139
models/attribute.py
Normal file
@ -0,0 +1,139 @@
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from sqlalchemy import ForeignKey, Table, Column
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from models.base import BaseModel
|
||||
from models.mixins import IdMixin, SoftDeleteMixin
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from models import Module, Deal
|
||||
|
||||
|
||||
module_attribute = Table(
|
||||
"module_attribute",
|
||||
BaseModel.metadata,
|
||||
Column("module_id", ForeignKey("modules.id"), primary_key=True),
|
||||
Column("attribute_id", ForeignKey("attributes.id"), primary_key=True),
|
||||
)
|
||||
|
||||
|
||||
class AttributeType(BaseModel, IdMixin, SoftDeleteMixin):
|
||||
__tablename__ = "attribute_types"
|
||||
|
||||
type: Mapped[str] = mapped_column(unique=True)
|
||||
name: Mapped[str] = mapped_column(unique=True)
|
||||
|
||||
attributes: Mapped["Attribute"] = relationship(
|
||||
back_populates="type",
|
||||
lazy="noload",
|
||||
)
|
||||
|
||||
|
||||
class Attribute(BaseModel, IdMixin, SoftDeleteMixin):
|
||||
__tablename__ = "attributes"
|
||||
|
||||
label: Mapped[str] = mapped_column()
|
||||
is_applicable_to_group: Mapped[bool] = mapped_column(
|
||||
default=False,
|
||||
comment="Применять ли изменения атрибута карточки ко всем карточкам в группе",
|
||||
)
|
||||
is_shown_on_dashboard: Mapped[bool] = mapped_column(
|
||||
default=False,
|
||||
comment="Отображается ли атрибут на дашборде",
|
||||
)
|
||||
is_highlight_if_expired: Mapped[bool] = mapped_column(
|
||||
default=False,
|
||||
comment="Подсветка атрибута, если Дата/ДатаВремя просрочена",
|
||||
)
|
||||
is_nullable: Mapped[bool] = mapped_column(default=False)
|
||||
default_value: Mapped[Optional[dict[str, any]]] = mapped_column(JSONB)
|
||||
description: Mapped[str] = mapped_column(default="")
|
||||
|
||||
is_built_in: Mapped[bool] = mapped_column(default=False)
|
||||
|
||||
type_id: Mapped[int] = mapped_column(ForeignKey("attribute_types.id"))
|
||||
type: Mapped[AttributeType] = relationship(
|
||||
back_populates="attributes",
|
||||
lazy="joined",
|
||||
)
|
||||
|
||||
modules: Mapped[list["Module"]] = relationship(
|
||||
secondary=module_attribute,
|
||||
back_populates="attributes",
|
||||
lazy="noload",
|
||||
)
|
||||
|
||||
values: Mapped[list["AttributeValue"]] = relationship(
|
||||
uselist=True,
|
||||
back_populates="attribute",
|
||||
lazy="noload",
|
||||
)
|
||||
|
||||
|
||||
class AttributeValue(BaseModel, IdMixin):
|
||||
__tablename__ = "attribute_values"
|
||||
|
||||
value: Mapped[Optional[dict[str, any]]] = mapped_column(JSONB)
|
||||
|
||||
deal_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("deals.id"),
|
||||
primary_key=True,
|
||||
)
|
||||
deal: Mapped["Deal"] = relationship(
|
||||
back_populates="attributes_values",
|
||||
lazy="noload",
|
||||
)
|
||||
|
||||
module_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("modules.id"),
|
||||
primary_key=True,
|
||||
)
|
||||
module: Mapped["Module"] = relationship(
|
||||
back_populates="attribute_values",
|
||||
lazy="noload",
|
||||
)
|
||||
|
||||
attribute_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("attributes.id"),
|
||||
primary_key=True,
|
||||
)
|
||||
attribute: Mapped[Attribute] = relationship(
|
||||
back_populates="values",
|
||||
lazy="joined",
|
||||
)
|
||||
|
||||
def set_value(self, value: Optional[dict | str | bool | int | float]):
|
||||
if value is None:
|
||||
return
|
||||
self.value = {"value": value}
|
||||
|
||||
def get_value(self) -> Optional[dict | str | bool | int | float]:
|
||||
if self.value is None:
|
||||
return None
|
||||
return self.value["value"]
|
||||
|
||||
|
||||
class AttributeLabel(BaseModel):
|
||||
__tablename__ = "attribute_labels"
|
||||
|
||||
label: Mapped[str] = mapped_column()
|
||||
|
||||
module_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("modules.id"),
|
||||
primary_key=True,
|
||||
)
|
||||
module: Mapped["Module"] = relationship(
|
||||
backref="attribute_labels",
|
||||
lazy="noload",
|
||||
)
|
||||
|
||||
attribute_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("attributes.id"),
|
||||
primary_key=True,
|
||||
)
|
||||
attribute: Mapped[Attribute] = relationship(
|
||||
backref="attribute_labels",
|
||||
lazy="joined",
|
||||
)
|
||||
@ -7,7 +7,14 @@ from models.base import BaseModel
|
||||
from models.mixins import SoftDeleteMixin, CreatedAtMixin, IdMixin
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from models import Status, Board, DealStatusHistory, DealGroup, DealTag
|
||||
from models import (
|
||||
Status,
|
||||
Board,
|
||||
DealStatusHistory,
|
||||
DealGroup,
|
||||
DealTag,
|
||||
AttributeValue,
|
||||
)
|
||||
from modules.clients.models import Client
|
||||
|
||||
|
||||
@ -49,6 +56,10 @@ class Deal(BaseModel, IdMixin, SoftDeleteMixin, CreatedAtMixin):
|
||||
secondaryjoin="and_(DealTag.id == deals_deal_tags.c.deal_tag_id, DealTag.is_deleted == False)",
|
||||
)
|
||||
|
||||
attributes_values: Mapped[list["AttributeValue"]] = relationship(
|
||||
back_populates="deal",
|
||||
)
|
||||
|
||||
# module client
|
||||
client_id: Mapped[Optional[int]] = mapped_column(
|
||||
ForeignKey("clients.id", ondelete="CASCADE"),
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import enum
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from sqlalchemy import Table, Column, ForeignKey
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from models import AttributeValue, Attribute
|
||||
from models.base import BaseModel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -31,8 +32,9 @@ class Module(BaseModel):
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
key: Mapped[str] = mapped_column(unique=True)
|
||||
label: Mapped[str] = mapped_column()
|
||||
description: Mapped[str] = mapped_column()
|
||||
description: Mapped[Optional[str]] = mapped_column()
|
||||
is_deleted: Mapped[bool] = mapped_column(default=False)
|
||||
is_built_in: Mapped[bool] = mapped_column(default=False, server_default="0")
|
||||
|
||||
depends_on: Mapped[list["Module"]] = relationship(
|
||||
secondary=module_dependencies,
|
||||
@ -61,6 +63,14 @@ class Module(BaseModel):
|
||||
lazy="immediate", backref="module", cascade="all, delete-orphan"
|
||||
)
|
||||
|
||||
attributes: Mapped[list["Attribute"]] = relationship(
|
||||
secondary="module_attribute", back_populates="modules"
|
||||
)
|
||||
|
||||
attribute_values: Mapped[list["AttributeValue"]] = relationship(
|
||||
lazy="noload", back_populates="module"
|
||||
)
|
||||
|
||||
|
||||
class DeviceType(enum.StrEnum):
|
||||
MOBILE = "mobile"
|
||||
|
||||
Reference in New Issue
Block a user