Publish curated MobilityOps source snapshot
This commit is contained in:
commit
7adc072f5e
317 files changed
+48851
No files matched your search
@@ -0,0 +1,27 @@
|
||||
from app.core.db import Base
|
||||
from app.models.audit import AuditEvent
|
||||
from app.models.booking import Booking
|
||||
from app.models.customer import Customer
|
||||
from app.models.data_quality import DataQualityIssue
|
||||
from app.models.idempotency import IdempotencyRecord
|
||||
from app.models.inspection import Inspection
|
||||
from app.models.maintenance import MaintenanceRecord
|
||||
from app.models.outbox import OutboxEvent
|
||||
from app.models.revoked_session import RevokedSession
|
||||
from app.models.user import User
|
||||
from app.models.vehicle import Vehicle
|
||||
|
||||
__all__ = [
|
||||
"Base",
|
||||
"AuditEvent",
|
||||
"Booking",
|
||||
"Customer",
|
||||
"DataQualityIssue",
|
||||
"IdempotencyRecord",
|
||||
"Inspection",
|
||||
"MaintenanceRecord",
|
||||
"OutboxEvent",
|
||||
"RevokedSession",
|
||||
"User",
|
||||
"Vehicle",
|
||||
]
|
||||
@@ -0,0 +1,32 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import CheckConstraint, DateTime, Index, String
|
||||
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.db import Base
|
||||
from app.models.mixins import UUIDPrimaryKeyMixin
|
||||
|
||||
ACTOR_TYPES = ("user", "service", "system")
|
||||
|
||||
|
||||
class AuditEvent(UUIDPrimaryKeyMixin, Base):
|
||||
__tablename__ = "audit_events"
|
||||
__table_args__ = (
|
||||
CheckConstraint("actor_type IN ('user','service','system')", name="ck_audit_actor_type"),
|
||||
Index("ix_audit_action_occurred", "action", "occurred_at"),
|
||||
Index("ix_audit_entity", "entity_type", "entity_id"),
|
||||
)
|
||||
|
||||
actor_type: Mapped[str] = mapped_column(String(20), nullable=False)
|
||||
actor_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True))
|
||||
actor_label: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
action: Mapped[str] = mapped_column(String(80), nullable=False)
|
||||
entity_type: Mapped[str] = mapped_column(String(30), nullable=False)
|
||||
entity_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True))
|
||||
correlation_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), nullable=False)
|
||||
before_json: Mapped[dict | None] = mapped_column(JSONB)
|
||||
after_json: Mapped[dict | None] = mapped_column(JSONB)
|
||||
metadata_json: Mapped[dict | None] = mapped_column(JSONB)
|
||||
occurred_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
@@ -0,0 +1,45 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import Boolean, CheckConstraint, DateTime, ForeignKey, Index, Integer, String
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.db import Base
|
||||
from app.models.mixins import TimestampMixin, UUIDPrimaryKeyMixin
|
||||
|
||||
BOOKING_STATUSES = ("reserved", "active", "returned", "cancelled", "blocked")
|
||||
|
||||
|
||||
class Booking(UUIDPrimaryKeyMixin, TimestampMixin, Base):
|
||||
__tablename__ = "bookings"
|
||||
__table_args__ = (
|
||||
CheckConstraint(
|
||||
"status IN ('reserved','active','returned','cancelled','blocked')",
|
||||
name="ck_bookings_status",
|
||||
),
|
||||
CheckConstraint("ends_at > starts_at", name="ck_bookings_time_window"),
|
||||
CheckConstraint(
|
||||
"start_odometer_km IS NULL OR start_odometer_km >= 0",
|
||||
name="ck_bookings_start_odometer",
|
||||
),
|
||||
CheckConstraint(
|
||||
"end_odometer_km IS NULL OR end_odometer_km >= 0",
|
||||
name="ck_bookings_end_odometer",
|
||||
),
|
||||
Index("ix_bookings_vehicle_status_window", "vehicle_id", "status", "starts_at", "ends_at"),
|
||||
)
|
||||
|
||||
public_ref: Mapped[str] = mapped_column(String(20), unique=True, nullable=False)
|
||||
customer_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("customers.id"), nullable=False
|
||||
)
|
||||
vehicle_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("vehicles.id"), nullable=False
|
||||
)
|
||||
starts_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
ends_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
status: Mapped[str] = mapped_column(String(20), nullable=False)
|
||||
start_odometer_km: Mapped[int | None] = mapped_column(Integer)
|
||||
end_odometer_km: Mapped[int | None] = mapped_column(Integer)
|
||||
requirements_complete: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||
@@ -0,0 +1,26 @@
|
||||
import uuid
|
||||
from datetime import date, datetime
|
||||
|
||||
from sqlalchemy import Date, DateTime, ForeignKey, String
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.db import Base
|
||||
from app.models.mixins import TimestampMixin, UUIDPrimaryKeyMixin
|
||||
|
||||
|
||||
class Customer(UUIDPrimaryKeyMixin, TimestampMixin, Base):
|
||||
__tablename__ = "customers"
|
||||
|
||||
public_ref: Mapped[str] = mapped_column(String(20), unique=True, nullable=False)
|
||||
first_name: Mapped[str] = mapped_column(String(80), nullable=False)
|
||||
last_name: Mapped[str] = mapped_column(String(80), nullable=False)
|
||||
email: Mapped[str | None] = mapped_column(String(200))
|
||||
phone: Mapped[str | None] = mapped_column(String(40))
|
||||
postal_code: Mapped[str | None] = mapped_column(String(20))
|
||||
city: Mapped[str | None] = mapped_column(String(120))
|
||||
date_of_birth: Mapped[date | None] = mapped_column(Date)
|
||||
merged_into_customer_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("customers.id")
|
||||
)
|
||||
anonymized_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), index=True)
|
||||
@@ -0,0 +1,65 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sqlalchemy import CheckConstraint, DateTime, ForeignKey, Index, String, text
|
||||
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.core.db import Base
|
||||
from app.models.mixins import TimestampMixin, UUIDPrimaryKeyMixin
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from app.models.user import User
|
||||
|
||||
RULE_TYPES = (
|
||||
"possible_duplicate_customer",
|
||||
"missing_required_field",
|
||||
"odometer_regression",
|
||||
"booking_overlap",
|
||||
"vehicle_status_conflict",
|
||||
)
|
||||
SEVERITIES = ("low", "medium", "high")
|
||||
ISSUE_STATUSES = ("open", "deferred", "resolved", "rejected")
|
||||
|
||||
|
||||
class DataQualityIssue(UUIDPrimaryKeyMixin, TimestampMixin, Base):
|
||||
__tablename__ = "data_quality_issues"
|
||||
__table_args__ = (
|
||||
CheckConstraint(
|
||||
"rule_type IN ('possible_duplicate_customer','missing_required_field',"
|
||||
"'odometer_regression','booking_overlap','vehicle_status_conflict')",
|
||||
name="ck_data_quality_rule_type",
|
||||
),
|
||||
CheckConstraint("severity IN ('low','medium','high')", name="ck_data_quality_severity"),
|
||||
CheckConstraint(
|
||||
"status IN ('open','deferred','resolved','rejected')",
|
||||
name="ck_data_quality_status",
|
||||
),
|
||||
Index("ix_data_quality_work_queue", "status", "due_at", "severity"),
|
||||
Index(
|
||||
"uq_data_quality_one_open_condition",
|
||||
"rule_type",
|
||||
"entity_type",
|
||||
"entity_id",
|
||||
unique=True,
|
||||
postgresql_where=text("status = 'open'"),
|
||||
),
|
||||
)
|
||||
|
||||
public_ref: Mapped[str] = mapped_column(String(20), unique=True, nullable=False)
|
||||
rule_type: Mapped[str] = mapped_column(String(40), nullable=False)
|
||||
entity_type: Mapped[str] = mapped_column(String(30), nullable=False)
|
||||
entity_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), nullable=False)
|
||||
severity: Mapped[str] = mapped_column(String(10), nullable=False)
|
||||
status: Mapped[str] = mapped_column(String(20), nullable=False, default="open")
|
||||
evidence_json: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict)
|
||||
proposed_action_json: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict)
|
||||
detected_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
due_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), index=True)
|
||||
assigned_to_user_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), index=True
|
||||
)
|
||||
assigned_to_user: Mapped["User | None"] = relationship(lazy="selectin")
|
||||
resolved_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
resolved_by: Mapped[str | None] = mapped_column(String(120))
|
||||
@@ -0,0 +1,22 @@
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import ForeignKey, Integer, String
|
||||
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.db import Base
|
||||
from app.models.mixins import TimestampMixin, UUIDPrimaryKeyMixin
|
||||
|
||||
|
||||
class IdempotencyRecord(UUIDPrimaryKeyMixin, TimestampMixin, Base):
|
||||
__tablename__ = "idempotency_records"
|
||||
|
||||
idempotency_key: Mapped[str] = mapped_column(String(128), unique=True, nullable=False)
|
||||
booking_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("bookings.id"), nullable=False
|
||||
)
|
||||
# SHA-256 of the canonical request body. Replaying a key with a *different* body is
|
||||
# a client bug and must be rejected instead of silently answered with the old result.
|
||||
request_fingerprint: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
response_status: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
response_body: Mapped[dict] = mapped_column(JSONB, nullable=False)
|
||||
@@ -0,0 +1,32 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.db import Base
|
||||
from app.models.mixins import TimestampMixin, UUIDPrimaryKeyMixin
|
||||
|
||||
INSPECTION_TYPES = ("checkout", "return")
|
||||
|
||||
|
||||
class Inspection(UUIDPrimaryKeyMixin, TimestampMixin, Base):
|
||||
__tablename__ = "inspections"
|
||||
|
||||
public_ref: Mapped[str] = mapped_column(String(20), unique=True, nullable=False)
|
||||
booking_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("bookings.id"), nullable=False
|
||||
)
|
||||
vehicle_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("vehicles.id"), nullable=False
|
||||
)
|
||||
type: Mapped[str] = mapped_column(String(20), nullable=False)
|
||||
fuel_level_percent: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
cleanliness_ok: Mapped[bool] = mapped_column(Boolean, nullable=False)
|
||||
damage_reported: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||
technical_warning: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||
notes: Mapped[str | None] = mapped_column(Text)
|
||||
odometer_km: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
completed_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
completed_by: Mapped[str | None] = mapped_column(String(120))
|
||||
@@ -0,0 +1,22 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, ForeignKey, Integer, String, Text
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.db import Base
|
||||
from app.models.mixins import UUIDPrimaryKeyMixin
|
||||
|
||||
|
||||
class MaintenanceRecord(UUIDPrimaryKeyMixin, Base):
|
||||
__tablename__ = "maintenance_records"
|
||||
|
||||
public_ref: Mapped[str] = mapped_column(String(20), unique=True, nullable=False)
|
||||
vehicle_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("vehicles.id"), nullable=False
|
||||
)
|
||||
occurred_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
odometer_km: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
category: Mapped[str] = mapped_column(String(60), nullable=False)
|
||||
summary: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
@@ -0,0 +1,22 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, func
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
|
||||
class UUIDPrimaryKeyMixin:
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
|
||||
|
||||
class TimestampMixin:
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(), nullable=False
|
||||
)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
server_default=func.now(),
|
||||
onupdate=func.now(),
|
||||
nullable=False,
|
||||
)
|
||||
@@ -0,0 +1,61 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import CheckConstraint, DateTime, Index, Integer, String, Text
|
||||
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.db import Base
|
||||
from app.models.mixins import TimestampMixin
|
||||
|
||||
DELIVERY_STATUSES = ("pending", "delivering", "succeeded", "failed")
|
||||
|
||||
# The one delivery failure the demo seed deliberately plants (BK-H-0020, see
|
||||
# seed/workflow_runs.csv). It exists to show retry and audit working, so it must never
|
||||
# be read as an integration-health problem: it is a scripted prop, not evidence that
|
||||
# n8n is unhealthy. A dedicated error code -- rather than the generic
|
||||
# "connectionError" a real timeout produces -- is what lets every reader tell the two
|
||||
# apart without guessing from the message text.
|
||||
#
|
||||
# It is deliberately a `last_error_code` value and not a new column: the code is
|
||||
# already persisted, already surfaced to the UI, and already localizable, so no schema
|
||||
# change or migration is needed. A genuine later failure of this same event overwrites
|
||||
# the code with the real one, which is exactly right -- from that moment it *is* a real
|
||||
# failure.
|
||||
DEMO_SCENARIO_ERROR_CODE = "demoScenarioTimeout"
|
||||
|
||||
|
||||
def is_demo_scenario_failure(event: "OutboxEvent") -> bool:
|
||||
"""True for the prepared demo failure, false for every real one."""
|
||||
return event.delivery_status == "failed" and event.last_error_code == DEMO_SCENARIO_ERROR_CODE
|
||||
|
||||
|
||||
class OutboxEvent(TimestampMixin, Base):
|
||||
__tablename__ = "outbox_events"
|
||||
__table_args__ = (
|
||||
CheckConstraint(
|
||||
"delivery_status IN ('pending','delivering','succeeded','failed')",
|
||||
name="ck_outbox_delivery_status",
|
||||
),
|
||||
CheckConstraint("attempts >= 0", name="ck_outbox_attempts"),
|
||||
Index("ix_outbox_delivery_next_attempt", "delivery_status", "next_attempt_at"),
|
||||
)
|
||||
|
||||
event_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
|
||||
)
|
||||
event_type: Mapped[str] = mapped_column(String(60), nullable=False)
|
||||
aggregate_type: Mapped[str] = mapped_column(String(30), nullable=False)
|
||||
aggregate_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), nullable=False)
|
||||
payload_json: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict)
|
||||
occurred_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
delivery_status: Mapped[str] = mapped_column(String(20), nullable=False, default="pending")
|
||||
attempts: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||
next_attempt_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
last_error: Mapped[str | None] = mapped_column(Text)
|
||||
# Stable, localizable classification of last_error -- the frontend renders a
|
||||
# localized summary from this code as the primary text and shows last_error itself
|
||||
# only under "Technical details" (section 10 of docs/fleet-ops-correction/
|
||||
# current-gap-audit.md). Kept alongside the raw message for backward compatibility.
|
||||
last_error_code: Mapped[str | None] = mapped_column(String(60))
|
||||
external_run_id: Mapped[str | None] = mapped_column(String(120))
|
||||
@@ -0,0 +1,16 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.db import Base
|
||||
from app.models.mixins import TimestampMixin, UUIDPrimaryKeyMixin
|
||||
|
||||
|
||||
class RevokedSession(UUIDPrimaryKeyMixin, TimestampMixin, Base):
|
||||
__tablename__ = "revoked_sessions"
|
||||
|
||||
token_hash: Mapped[str] = mapped_column(String(64), unique=True, index=True, nullable=False)
|
||||
expires_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, index=True
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
from sqlalchemy import Boolean, String, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.db import Base
|
||||
from app.models.mixins import TimestampMixin, UUIDPrimaryKeyMixin
|
||||
|
||||
ROLES = ("operations_manager", "rental_employee")
|
||||
|
||||
|
||||
class User(UUIDPrimaryKeyMixin, TimestampMixin, Base):
|
||||
__tablename__ = "users"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("identity_provider", "external_subject", name="uq_user_external_identity"),
|
||||
)
|
||||
|
||||
public_ref: Mapped[str] = mapped_column(String(20), unique=True, nullable=False)
|
||||
email: Mapped[str | None] = mapped_column(String(320), unique=True, nullable=True)
|
||||
password_hash: Mapped[str | None] = mapped_column(String(512), nullable=True)
|
||||
display_name: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
role: Mapped[str] = mapped_column(String(30), nullable=False)
|
||||
active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
|
||||
identity_provider: Mapped[str | None] = mapped_column(String(80), nullable=True)
|
||||
external_subject: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||
@@ -0,0 +1,33 @@
|
||||
from sqlalchemy import Boolean, CheckConstraint, Integer, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.db import Base
|
||||
from app.models.mixins import TimestampMixin, UUIDPrimaryKeyMixin
|
||||
|
||||
OPERATIONAL_STATUSES = ("available", "rented", "cleaning", "maintenance", "blocked")
|
||||
|
||||
|
||||
class Vehicle(UUIDPrimaryKeyMixin, TimestampMixin, Base):
|
||||
__tablename__ = "vehicles"
|
||||
__table_args__ = (
|
||||
CheckConstraint(
|
||||
"operational_status IN ('available','rented','cleaning','maintenance','blocked')",
|
||||
name="ck_vehicles_operational_status",
|
||||
),
|
||||
CheckConstraint("model_year BETWEEN 1900 AND 2100", name="ck_vehicles_model_year"),
|
||||
CheckConstraint("odometer_km >= 0", name="ck_vehicles_odometer"),
|
||||
CheckConstraint("next_service_km >= 0", name="ck_vehicles_next_service"),
|
||||
CheckConstraint("version >= 1", name="ck_vehicles_version"),
|
||||
)
|
||||
|
||||
public_ref: Mapped[str] = mapped_column(String(20), unique=True, nullable=False)
|
||||
make: Mapped[str] = mapped_column(String(80), nullable=False)
|
||||
model: Mapped[str] = mapped_column(String(80), nullable=False)
|
||||
model_year: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
registration_number: Mapped[str] = mapped_column(String(40), unique=True, nullable=False)
|
||||
location: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
operational_status: Mapped[str] = mapped_column(String(20), nullable=False)
|
||||
odometer_km: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
next_service_km: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
|
||||
version: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
|
||||
Reference in new issue
Block a user