JobSetting.updated_at — NOT NULL без Python/server default: ORM-вставка строки даст IntegrityError (латентная ловушка) #1571

Closed
opened 2026-06-16 17:06:27 +00:00 by bot-backend · 0 comments
Collaborator

Severity: low · Категория: default-mismatch · Файл: backend/app/models/job_settings.py:23

app/models/job_settings.py:23 — updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) не имеет ни default=..., ни server_default=text('now()'), хотя все остальные колонки несут default, зеркалящий DDL, а data/sql/81_job_settings.sql:44 объявляет updated_at TIMESTAMPTZ NOT NULL DEFAULT now(). Impact: при создании строки через ORM (db.add(JobSetting(job_type=...))) SQLAlchemy не знает о серверном DEFAULT now() и пошлёт updated_at=NULL на INSERT → NOT NULL violation / IntegrityError; модель не способна создавать строки job_settings. Сейчас замаскировано тем, что весь write-path идёт через raw SQL в app/services/job_settings.py (UPDATE ... SET updated_at = NOW(), строка 219; INSERT полагается на серверный now()), а ORM-модель JobSetting нигде не используется для вставок. Дополнительно: если бы модель попала в alembic-metadata с compare_server_default=True, отсутствие server_default дало бы ложный дрейф против DDL. Fix: добавить server_default=text('now()') (и при желании default=datetime.now(UTC) / onupdate) на updated_at, чтобы ORM-вставки совпадали с DDL.

Почему баг: With no Python default and no server_default on the ORM column, constructing and adding a row via the ORM (db.add(JobSetting(job_type=...))) sends updated_at=NULL on INSERT (SQLAlchemy does not know about the DB-side DEFAULT now() unless server_default is set), producing a NOT NULL violation / IntegrityError. The model therefore cannot create job_settings rows. Currently masked only because the service layer writes via raw SQL ('updated_at = NOW()'), but the ORM model is a latent trap. If this model were registered in alembic metadata with compare_server_default=True, the missing server_default would also be reported as spurious drift vs the DDL's DEFAULT now().

Как чинить: Add server_default to match the DDL: mapped_column(DateTime(timezone=True), server_default=text('now()'), onupdate=lambda: datetime.now(UTC), nullable=False) so ORM inserts/updates behave like the raw-SQL path.


deep-audit backend v2 (после PR #1543) · verify+harden, confidence 0.8 · unit B07

**Severity:** low · **Категория:** default-mismatch · **Файл:** `backend/app/models/job_settings.py:23` app/models/job_settings.py:23 — updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) не имеет ни default=..., ни server_default=text('now()'), хотя все остальные колонки несут default, зеркалящий DDL, а data/sql/81_job_settings.sql:44 объявляет updated_at TIMESTAMPTZ NOT NULL DEFAULT now(). Impact: при создании строки через ORM (db.add(JobSetting(job_type=...))) SQLAlchemy не знает о серверном DEFAULT now() и пошлёт updated_at=NULL на INSERT → NOT NULL violation / IntegrityError; модель не способна создавать строки job_settings. Сейчас замаскировано тем, что весь write-path идёт через raw SQL в app/services/job_settings.py (UPDATE ... SET updated_at = NOW(), строка 219; INSERT полагается на серверный now()), а ORM-модель JobSetting нигде не используется для вставок. Дополнительно: если бы модель попала в alembic-metadata с compare_server_default=True, отсутствие server_default дало бы ложный дрейф против DDL. Fix: добавить server_default=text('now()') (и при желании default=datetime.now(UTC) / onupdate) на updated_at, чтобы ORM-вставки совпадали с DDL. **Почему баг:** With no Python default and no server_default on the ORM column, constructing and adding a row via the ORM (`db.add(JobSetting(job_type=...))`) sends updated_at=NULL on INSERT (SQLAlchemy does not know about the DB-side DEFAULT now() unless server_default is set), producing a NOT NULL violation / IntegrityError. The model therefore cannot create job_settings rows. Currently masked only because the service layer writes via raw SQL ('updated_at = NOW()'), but the ORM model is a latent trap. If this model were registered in alembic metadata with compare_server_default=True, the missing server_default would also be reported as spurious drift vs the DDL's DEFAULT now(). **Как чинить:** Add server_default to match the DDL: mapped_column(DateTime(timezone=True), server_default=text('now()'), onupdate=lambda: datetime.now(UTC), nullable=False) so ORM inserts/updates behave like the raw-SQL path. --- <sub>deep-audit backend v2 (после PR #1543) · verify+harden, confidence 0.8 · unit B07</sub>
bot-backend added the
week ревью 1
label 2026-06-16 17:06:27 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#1571
No description provided.