Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90cae15551 | ||
|
|
16ba09f073 | ||
|
|
53fe03fb2d | ||
|
|
c414708b32 | ||
|
|
443a128ad7 |
No files matched your search
@@ -0,0 +1,37 @@
|
|||||||
|
"""manufacturers_url_nullable
|
||||||
|
|
||||||
|
Revision ID: 0fec09fb6ae6
|
||||||
|
Revises: 9a1c2f6e0b3d
|
||||||
|
Create Date: 2026-09-04 23:05:18.990806
|
||||||
|
"""
|
||||||
|
|
||||||
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = "0fec09fb6ae6"
|
||||||
|
down_revision: str | None = "9a1c2f6e0b3d"
|
||||||
|
branch_labels: str | Sequence[str] | None = None
|
||||||
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
with op.batch_alter_table("manufacturers", schema=None) as batch_op:
|
||||||
|
batch_op.alter_column(
|
||||||
|
"url",
|
||||||
|
existing_type=sa.String(),
|
||||||
|
nullable=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
op.execute("UPDATE manufacturers SET url = '' WHERE url IS NULL")
|
||||||
|
with op.batch_alter_table("manufacturers", schema=None) as batch_op:
|
||||||
|
batch_op.alter_column(
|
||||||
|
"url",
|
||||||
|
existing_type=sa.String(),
|
||||||
|
nullable=False,
|
||||||
|
)
|
||||||
@@ -28,12 +28,19 @@ def upgrade() -> None:
|
|||||||
sa.Column("logo_url", sa.String(), nullable=True),
|
sa.Column("logo_url", sa.String(), nullable=True),
|
||||||
sa.PrimaryKeyConstraint("id"),
|
sa.PrimaryKeyConstraint("id"),
|
||||||
)
|
)
|
||||||
|
# batch_alter_table's alter_column doesn't emit CREATE TYPE for the new
|
||||||
|
# Enum on its own (unlike create_table, which ties enum creation to the
|
||||||
|
# table-create DDL event) - create it explicitly first, or Postgres fails
|
||||||
|
# with `type "uncseries" does not exist`. checkfirst / no-op on SQLite.
|
||||||
|
uncseries = sa.Enum("UNC", "UNF", "UNEF", "UN", "UNS", name="uncseries")
|
||||||
|
uncseries.create(op.get_bind(), checkfirst=True)
|
||||||
with op.batch_alter_table("unc_threads", schema=None) as batch_op:
|
with op.batch_alter_table("unc_threads", schema=None) as batch_op:
|
||||||
batch_op.alter_column(
|
batch_op.alter_column(
|
||||||
"series",
|
"series",
|
||||||
existing_type=sa.VARCHAR(length=8),
|
existing_type=sa.VARCHAR(length=8),
|
||||||
type_=sa.Enum("UNC", "UNF", "UNEF", "UN", "UNS", name="uncseries"),
|
type_=uncseries,
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
|
postgresql_using="series::uncseries",
|
||||||
)
|
)
|
||||||
|
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
@@ -47,7 +54,9 @@ def downgrade() -> None:
|
|||||||
existing_type=sa.Enum("UNC", "UNF", "UNEF", "UN", "UNS", name="uncseries"),
|
existing_type=sa.Enum("UNC", "UNF", "UNEF", "UN", "UNS", name="uncseries"),
|
||||||
type_=sa.VARCHAR(length=8),
|
type_=sa.VARCHAR(length=8),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
|
postgresql_using="series::varchar(8)",
|
||||||
)
|
)
|
||||||
|
sa.Enum(name="uncseries").drop(op.get_bind(), checkfirst=True)
|
||||||
|
|
||||||
op.drop_table("manufacturers")
|
op.drop_table("manufacturers")
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
@@ -26,7 +26,7 @@ async def create(
|
|||||||
db: AsyncSession,
|
db: AsyncSession,
|
||||||
*,
|
*,
|
||||||
name: str,
|
name: str,
|
||||||
url: str,
|
url: str | None = None,
|
||||||
logo_url: str | None = None,
|
logo_url: str | None = None,
|
||||||
) -> Manufacturer:
|
) -> Manufacturer:
|
||||||
m = Manufacturer(name=name, url=url, logo_url=logo_url)
|
m = Manufacturer(name=name, url=url, logo_url=logo_url)
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ class Manufacturer(Base):
|
|||||||
|
|
||||||
id: Mapped[int] = mapped_column(primary_key=True)
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
name: Mapped[str] = mapped_column(nullable=False)
|
name: Mapped[str] = mapped_column(nullable=False)
|
||||||
url: Mapped[str] = mapped_column(nullable=False)
|
url: Mapped[str | None] = mapped_column(nullable=True)
|
||||||
logo_url: Mapped[str | None] = mapped_column(nullable=True, default=None)
|
logo_url: Mapped[str | None] = mapped_column(nullable=True, default=None)
|
||||||
@@ -1,11 +1,21 @@
|
|||||||
from pydantic import BaseModel, ConfigDict, HttpUrl
|
from pydantic import BaseModel, ConfigDict, HttpUrl, field_validator
|
||||||
|
|
||||||
|
|
||||||
|
def _blank_to_none(value: str | None) -> str | None:
|
||||||
|
if isinstance(value, str) and not value.strip():
|
||||||
|
return None
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerBase(BaseModel):
|
class ManufacturerBase(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
url: HttpUrl
|
url: HttpUrl | None = None
|
||||||
logo_url: HttpUrl | None = None
|
logo_url: HttpUrl | None = None
|
||||||
|
|
||||||
|
_normalize_url = field_validator("url", "logo_url", mode="before")(
|
||||||
|
_blank_to_none
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerCreate(ManufacturerBase):
|
class ManufacturerCreate(ManufacturerBase):
|
||||||
pass
|
pass
|
||||||
@@ -16,6 +26,10 @@ class ManufacturerUpdate(BaseModel):
|
|||||||
url: HttpUrl | None = None
|
url: HttpUrl | None = None
|
||||||
logo_url: HttpUrl | None = None
|
logo_url: HttpUrl | None = None
|
||||||
|
|
||||||
|
_normalize_url = field_validator("url", "logo_url", mode="before")(
|
||||||
|
_blank_to_none
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerRead(ManufacturerBase):
|
class ManufacturerRead(ManufacturerBase):
|
||||||
model_config = ConfigDict(from_attributes=True)
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|||||||
+6
-2
@@ -728,7 +728,7 @@ async def create_manufacturer(
|
|||||||
created = await manufacturer_crud.create(
|
created = await manufacturer_crud.create(
|
||||||
db,
|
db,
|
||||||
name=name,
|
name=name,
|
||||||
url=url,
|
url=url.strip() or None,
|
||||||
logo_url=logo_url.strip() or None,
|
logo_url=logo_url.strip() or None,
|
||||||
)
|
)
|
||||||
await audit_crud.log(
|
await audit_crud.log(
|
||||||
@@ -789,7 +789,11 @@ async def update_manufacturer(
|
|||||||
m = await manufacturer_crud.get(db, manufacturer_id)
|
m = await manufacturer_crud.get(db, manufacturer_id)
|
||||||
if m is None:
|
if m is None:
|
||||||
raise HTTPException(status_code=404, detail="Manufacturer not found")
|
raise HTTPException(status_code=404, detail="Manufacturer not found")
|
||||||
changes = {"name": name, "url": url, "logo_url": logo_url.strip() or None}
|
changes = {
|
||||||
|
"name": name,
|
||||||
|
"url": url.strip() or None,
|
||||||
|
"logo_url": logo_url.strip() or None,
|
||||||
|
}
|
||||||
await manufacturer_crud.update(db, m, changes)
|
await manufacturer_crud.update(db, m, changes)
|
||||||
await audit_crud.log(
|
await audit_crud.log(
|
||||||
db,
|
db,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Idempotent: creates perms/roles/superuser only if missing.
|
# Idempotent: creates perms/roles/superuser only if missing.
|
||||||
|
uv run alembic upgrade head
|
||||||
uv run python -m scripts.seed
|
uv run python -m scripts.seed
|
||||||
|
|
||||||
exec uv run uvicorn app.main:app --host 0.0.0.0 --port 8000
|
exec uv run uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||||||
Reference in new issue
Block a user