first
This commit is contained in:
13
app/models/__init__.py
Normal file
13
app/models/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from app.models.base import Base, BaseModel
|
||||
from app.models.form import Form, FormField, Field
|
||||
from app.models.submission import Submission, SubmissionAuditLog
|
||||
|
||||
__all__ = [
|
||||
"Base",
|
||||
"BaseModel",
|
||||
"Form",
|
||||
"FormField",
|
||||
"Field",
|
||||
"Submission",
|
||||
"SubmissionAuditLog",
|
||||
]
|
||||
41
app/models/base.py
Normal file
41
app/models/base.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy import Column, Integer, DateTime
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
# Базовый класс для декларативных моделей
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
class BaseModel(Base):
|
||||
"""
|
||||
Абстрактный базовый класс для всех моделей.
|
||||
Содержит общие поля и методы.
|
||||
"""
|
||||
__abstract__ = True
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
|
||||
|
||||
def dict(self) -> dict[str, Any]:
|
||||
"""Преобразует модель в словарь"""
|
||||
return {
|
||||
column.name: getattr(self, column.name)
|
||||
for column in self.__table__.columns
|
||||
}
|
||||
|
||||
def update(self, **kwargs) -> None:
|
||||
"""Обновляет поля модели"""
|
||||
for key, value in kwargs.items():
|
||||
if hasattr(self, key):
|
||||
setattr(self, key, value)
|
||||
|
||||
@classmethod
|
||||
def get_column_names(cls) -> list[str]:
|
||||
"""Возвращает список имен колонок модели"""
|
||||
return [column.name for column in cls.__table__.columns]
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""Строковое представление модели"""
|
||||
return f"<{self.__class__.__name__}(id={self.id})>"
|
||||
79
app/models/form.py
Normal file
79
app/models/form.py
Normal file
@@ -0,0 +1,79 @@
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Boolean, Text, ForeignKey, Index
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
from app.models.base import BaseModel
|
||||
|
||||
|
||||
class Form(BaseModel):
|
||||
__tablename__ = "forms"
|
||||
|
||||
uuid = Column(UUID(as_uuid=True), default=uuid.uuid4, unique=True, nullable=False, index=True)
|
||||
name = Column(String(200), nullable=False)
|
||||
description = Column(Text)
|
||||
version = Column(Integer, default=1)
|
||||
is_active = Column(Boolean, default=True)
|
||||
is_published = Column(Boolean, default=False)
|
||||
form_settings = Column(JSONB, default={}) # переименовано с settings
|
||||
created_by = Column(Integer, nullable=True)
|
||||
|
||||
# Relationships
|
||||
fields = relationship("FormField", back_populates="form", cascade="all, delete-orphan")
|
||||
submissions = relationship("Submission", back_populates="form", cascade="all, delete-orphan")
|
||||
|
||||
__table_args__ = (
|
||||
Index('ix_forms_name_active', 'name', 'is_active'),
|
||||
Index('ix_forms_created_at', 'created_at'),
|
||||
)
|
||||
|
||||
@property
|
||||
def fields_count(self) -> int:
|
||||
return len(self.fields)
|
||||
|
||||
@property
|
||||
def submissions_count(self) -> int:
|
||||
return len(self.submissions)
|
||||
|
||||
|
||||
class FormField(BaseModel):
|
||||
__tablename__ = "form_fields"
|
||||
|
||||
form_id = Column(Integer, ForeignKey("forms.id", ondelete="CASCADE"))
|
||||
field_id = Column(Integer, ForeignKey("fields.id", ondelete="CASCADE"))
|
||||
order = Column(Integer, default=0)
|
||||
is_required = Column(Boolean, default=False)
|
||||
default_value = Column(JSONB, nullable=True)
|
||||
visibility_conditions = Column(JSONB, default={})
|
||||
validation_rules_override = Column(JSONB, nullable=True)
|
||||
|
||||
# Relationships
|
||||
form = relationship("Form", back_populates="fields")
|
||||
field = relationship("Field", back_populates="form_fields")
|
||||
|
||||
__table_args__ = (
|
||||
Index('ix_form_fields_order', 'form_id', 'order'),
|
||||
Index('ix_form_fields_unique', 'form_id', 'field_id', unique=True),
|
||||
)
|
||||
|
||||
|
||||
class Field(BaseModel):
|
||||
__tablename__ = "fields"
|
||||
|
||||
name = Column(String(100), unique=True, nullable=False, index=True)
|
||||
label = Column(String(200), nullable=False)
|
||||
field_type = Column(String(50), nullable=False)
|
||||
placeholder = Column(String(200))
|
||||
help_text = Column(Text)
|
||||
field_options = Column(JSONB, default={}) # переименовано с options
|
||||
validation_rules = Column(JSONB, default={})
|
||||
field_metadata = Column(JSONB, default={}) # переименовано с metadata
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
|
||||
# Relationships
|
||||
form_fields = relationship("FormField", back_populates="field")
|
||||
|
||||
__table_args__ = (
|
||||
Index('ix_fields_type', 'field_type'),
|
||||
Index('ix_fields_name_type', 'name', 'field_type'),
|
||||
)
|
||||
51
app/models/submission.py
Normal file
51
app/models/submission.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from sqlalchemy import Column, Integer, DateTime, ForeignKey, String, Float, Boolean, Index
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
from app.models.base import BaseModel
|
||||
|
||||
|
||||
class Submission(BaseModel):
|
||||
__tablename__ = "submissions"
|
||||
|
||||
form_id = Column(Integer, ForeignKey("forms.id", ondelete="CASCADE"))
|
||||
submission_uuid = Column(UUID(as_uuid=True), default=uuid.uuid4, unique=True, nullable=False, index=True)
|
||||
submission_data = Column(JSONB, nullable=False) # переименовано с data
|
||||
submission_metadata = Column(JSONB, default={}) # переименовано с metadata
|
||||
status = Column(String(50), default="completed")
|
||||
submitted_at = Column(DateTime, default=datetime.utcnow, index=True)
|
||||
submitted_by = Column(Integer, nullable=True)
|
||||
|
||||
# Для аналитики
|
||||
completion_time_seconds = Column(Float, nullable=True)
|
||||
referrer = Column(String(500))
|
||||
|
||||
# Relationships
|
||||
form = relationship("Form", back_populates="submissions")
|
||||
audit_logs = relationship("SubmissionAuditLog", back_populates="submission")
|
||||
|
||||
__table_args__ = (
|
||||
Index('ix_submissions_data_gin', submission_data, postgresql_using='gin'),
|
||||
Index('ix_submissions_metadata_gin', submission_metadata, postgresql_using='gin'),
|
||||
Index('ix_submissions_form_status', 'form_id', 'status'),
|
||||
Index('ix_submissions_date_status', 'submitted_at', 'status'),
|
||||
Index('ix_submissions_created_at', 'created_at'),
|
||||
)
|
||||
|
||||
|
||||
class SubmissionAuditLog(BaseModel):
|
||||
__tablename__ = "submission_audit_logs"
|
||||
|
||||
submission_id = Column(Integer, ForeignKey("submissions.id", ondelete="CASCADE"))
|
||||
action = Column(String(50))
|
||||
changed_data = Column(JSONB)
|
||||
ip_address = Column(String(45))
|
||||
user_agent = Column(String(500))
|
||||
|
||||
submission = relationship("Submission", back_populates="audit_logs")
|
||||
|
||||
__table_args__ = (
|
||||
Index('ix_audit_submission', 'submission_id'),
|
||||
Index('ix_audit_created_at', 'created_at'),
|
||||
)
|
||||
Reference in New Issue
Block a user