🗃️ Add max_length validation for database models and input data (#1233)
This commit is contained in:
@@ -0,0 +1,69 @@
|
|||||||
|
"""Add max length for string(varchar) fields in User and Items models
|
||||||
|
|
||||||
|
Revision ID: 9c0a54914c78
|
||||||
|
Revises: e2412789c190
|
||||||
|
Create Date: 2024-06-17 14:42:44.639457
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
import sqlmodel.sql.sqltypes
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '9c0a54914c78'
|
||||||
|
down_revision = 'e2412789c190'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# Adjust the length of the email field in the User table
|
||||||
|
op.alter_column('user', 'email',
|
||||||
|
existing_type=sa.String(),
|
||||||
|
type_=sa.String(length=255),
|
||||||
|
existing_nullable=False)
|
||||||
|
|
||||||
|
# Adjust the length of the full_name field in the User table
|
||||||
|
op.alter_column('user', 'full_name',
|
||||||
|
existing_type=sa.String(),
|
||||||
|
type_=sa.String(length=255),
|
||||||
|
existing_nullable=True)
|
||||||
|
|
||||||
|
# Adjust the length of the title field in the Item table
|
||||||
|
op.alter_column('item', 'title',
|
||||||
|
existing_type=sa.String(),
|
||||||
|
type_=sa.String(length=255),
|
||||||
|
existing_nullable=False)
|
||||||
|
|
||||||
|
# Adjust the length of the description field in the Item table
|
||||||
|
op.alter_column('item', 'description',
|
||||||
|
existing_type=sa.String(),
|
||||||
|
type_=sa.String(length=255),
|
||||||
|
existing_nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# Revert the length of the email field in the User table
|
||||||
|
op.alter_column('user', 'email',
|
||||||
|
existing_type=sa.String(length=255),
|
||||||
|
type_=sa.String(),
|
||||||
|
existing_nullable=False)
|
||||||
|
|
||||||
|
# Revert the length of the full_name field in the User table
|
||||||
|
op.alter_column('user', 'full_name',
|
||||||
|
existing_type=sa.String(length=255),
|
||||||
|
type_=sa.String(),
|
||||||
|
existing_nullable=True)
|
||||||
|
|
||||||
|
# Revert the length of the title field in the Item table
|
||||||
|
op.alter_column('item', 'title',
|
||||||
|
existing_type=sa.String(length=255),
|
||||||
|
type_=sa.String(),
|
||||||
|
existing_nullable=False)
|
||||||
|
|
||||||
|
# Revert the length of the description field in the Item table
|
||||||
|
op.alter_column('item', 'description',
|
||||||
|
existing_type=sa.String(length=255),
|
||||||
|
type_=sa.String(),
|
||||||
|
existing_nullable=True)
|
@@ -1,43 +1,40 @@
|
|||||||
|
from pydantic import EmailStr
|
||||||
from sqlmodel import Field, Relationship, SQLModel
|
from sqlmodel import Field, Relationship, SQLModel
|
||||||
|
|
||||||
|
|
||||||
# Shared properties
|
# Shared properties
|
||||||
# TODO replace email str with EmailStr when sqlmodel supports it
|
|
||||||
class UserBase(SQLModel):
|
class UserBase(SQLModel):
|
||||||
email: str = Field(unique=True, index=True)
|
email: EmailStr = Field(unique=True, index=True, max_length=255)
|
||||||
is_active: bool = True
|
is_active: bool = True
|
||||||
is_superuser: bool = False
|
is_superuser: bool = False
|
||||||
full_name: str | None = None
|
full_name: str | None = Field(default=None, max_length=255)
|
||||||
|
|
||||||
|
|
||||||
# Properties to receive via API on creation
|
# Properties to receive via API on creation
|
||||||
class UserCreate(UserBase):
|
class UserCreate(UserBase):
|
||||||
password: str
|
password: str = Field(min_length=8, max_length=40)
|
||||||
|
|
||||||
|
|
||||||
# TODO replace email str with EmailStr when sqlmodel supports it
|
|
||||||
class UserRegister(SQLModel):
|
class UserRegister(SQLModel):
|
||||||
email: str
|
email: EmailStr = Field(max_length=255)
|
||||||
password: str
|
password: str = Field(min_length=8, max_length=40)
|
||||||
full_name: str | None = None
|
full_name: str | None = Field(default=None, max_length=255)
|
||||||
|
|
||||||
|
|
||||||
# Properties to receive via API on update, all are optional
|
# Properties to receive via API on update, all are optional
|
||||||
# TODO replace email str with EmailStr when sqlmodel supports it
|
|
||||||
class UserUpdate(UserBase):
|
class UserUpdate(UserBase):
|
||||||
email: str | None = None # type: ignore
|
email: EmailStr | None = Field(default=None, max_length=255) # type: ignore
|
||||||
password: str | None = None
|
password: str | None = Field(default=None, min_length=8, max_length=40)
|
||||||
|
|
||||||
|
|
||||||
# TODO replace email str with EmailStr when sqlmodel supports it
|
|
||||||
class UserUpdateMe(SQLModel):
|
class UserUpdateMe(SQLModel):
|
||||||
full_name: str | None = None
|
full_name: str | None = Field(default=None, max_length=255)
|
||||||
email: str | None = None
|
email: EmailStr | None = Field(default=None, max_length=255)
|
||||||
|
|
||||||
|
|
||||||
class UpdatePassword(SQLModel):
|
class UpdatePassword(SQLModel):
|
||||||
current_password: str
|
current_password: str = Field(min_length=8, max_length=40)
|
||||||
new_password: str
|
new_password: str = Field(min_length=8, max_length=40)
|
||||||
|
|
||||||
|
|
||||||
# Database model, database table inferred from class name
|
# Database model, database table inferred from class name
|
||||||
@@ -59,24 +56,24 @@ class UsersPublic(SQLModel):
|
|||||||
|
|
||||||
# Shared properties
|
# Shared properties
|
||||||
class ItemBase(SQLModel):
|
class ItemBase(SQLModel):
|
||||||
title: str
|
title: str = Field(min_length=1, max_length=255)
|
||||||
description: str | None = None
|
description: str | None = Field(default=None, max_length=255)
|
||||||
|
|
||||||
|
|
||||||
# Properties to receive on item creation
|
# Properties to receive on item creation
|
||||||
class ItemCreate(ItemBase):
|
class ItemCreate(ItemBase):
|
||||||
title: str
|
title: str = Field(min_length=1, max_length=255)
|
||||||
|
|
||||||
|
|
||||||
# Properties to receive on item update
|
# Properties to receive on item update
|
||||||
class ItemUpdate(ItemBase):
|
class ItemUpdate(ItemBase):
|
||||||
title: str | None = None # type: ignore
|
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
# Database model, database table inferred from class name
|
# Database model, database table inferred from class name
|
||||||
class Item(ItemBase, table=True):
|
class Item(ItemBase, table=True):
|
||||||
id: int | None = Field(default=None, primary_key=True)
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
title: str
|
title: str = Field(max_length=255)
|
||||||
owner_id: int | None = Field(default=None, foreign_key="user.id", nullable=False)
|
owner_id: int | None = Field(default=None, foreign_key="user.id", nullable=False)
|
||||||
owner: User | None = Relationship(back_populates="items")
|
owner: User | None = Relationship(back_populates="items")
|
||||||
|
|
||||||
@@ -110,4 +107,4 @@ class TokenPayload(SQLModel):
|
|||||||
|
|
||||||
class NewPassword(SQLModel):
|
class NewPassword(SQLModel):
|
||||||
token: str
|
token: str
|
||||||
new_password: str
|
new_password: str = Field(min_length=8, max_length=40)
|
||||||
|
20
backend/poetry.lock
generated
20
backend/poetry.lock
generated
@@ -1,4 +1,4 @@
|
|||||||
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alembic"
|
name = "alembic"
|
||||||
@@ -1431,13 +1431,13 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "requests"
|
name = "requests"
|
||||||
version = "2.32.0"
|
version = "2.32.3"
|
||||||
description = "Python HTTP for Humans."
|
description = "Python HTTP for Humans."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "requests-2.32.0-py3-none-any.whl", hash = "sha256:f2c3881dddb70d056c5bd7600a4fae312b2a300e39be6a118d30b90bd27262b5"},
|
{file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
|
||||||
{file = "requests-2.32.0.tar.gz", hash = "sha256:fa5490319474c82ef1d2c9bc459d3652e3ae4ef4c4ebdd18a21145a47ca4b6b8"},
|
{file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -1649,18 +1649,18 @@ sqlcipher = ["sqlcipher3_binary"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlmodel"
|
name = "sqlmodel"
|
||||||
version = "0.0.16"
|
version = "0.0.19"
|
||||||
description = "SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness."
|
description = "SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7,<4.0"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "sqlmodel-0.0.16-py3-none-any.whl", hash = "sha256:b972f5d319580d6c37ecc417881f6ec4d1ad3ed3583d0ac0ed43234a28bf605a"},
|
{file = "sqlmodel-0.0.19-py3-none-any.whl", hash = "sha256:6c8125d4101970d031e9aae970b20cbeaf44149989f8366d939f4ab21aab8763"},
|
||||||
{file = "sqlmodel-0.0.16.tar.gz", hash = "sha256:966656f18a8e9a2d159eb215b07fb0cf5222acfae3362707ca611848a8a06bd1"},
|
{file = "sqlmodel-0.0.19.tar.gz", hash = "sha256:95449b0b48a40a3eecf0a629fa5735b9dfc8a5574a91090d24ca17f02246ad96"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
pydantic = ">=1.10.13,<3.0.0"
|
pydantic = ">=1.10.13,<3.0.0"
|
||||||
SQLAlchemy = ">=2.0.0,<2.1.0"
|
SQLAlchemy = ">=2.0.14,<2.1.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "starlette"
|
name = "starlette"
|
||||||
@@ -2015,4 +2015,4 @@ files = [
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.10"
|
python-versions = "^3.10"
|
||||||
content-hash = "97e309fdf90731cf04592e72629274453bdfa3b2a001f238098456d6d1ae5a73"
|
content-hash = "1ce11888d9f96ef6ac10ec5f4409e7ffab06ba648aa720d75079924dbfbb6de5"
|
||||||
|
@@ -20,7 +20,7 @@ jinja2 = "^3.1.4"
|
|||||||
alembic = "^1.12.1"
|
alembic = "^1.12.1"
|
||||||
httpx = "^0.25.1"
|
httpx = "^0.25.1"
|
||||||
psycopg = {extras = ["binary"], version = "^3.1.13"}
|
psycopg = {extras = ["binary"], version = "^3.1.13"}
|
||||||
sqlmodel = "^0.0.16"
|
sqlmodel = "^0.0.19"
|
||||||
# Pin bcrypt until passlib supports the latest
|
# Pin bcrypt until passlib supports the latest
|
||||||
bcrypt = "4.0.1"
|
bcrypt = "4.0.1"
|
||||||
pydantic-settings = "^2.2.1"
|
pydantic-settings = "^2.2.1"
|
||||||
|
Reference in New Issue
Block a user