From d421c90af27224dfe14e971e6bb2cf03fa62d084 Mon Sep 17 00:00:00 2001 From: Esteban Maya Date: Tue, 18 Jun 2024 19:20:39 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=97=83=EF=B8=8F=20Add=20max=5Flength=20va?= =?UTF-8?q?lidation=20for=20database=20models=20and=20input=20data=20(#123?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...4c78_add_max_length_for_string_varchar_.py | 69 +++++++++++++++++++ backend/app/models.py | 41 +++++------ backend/poetry.lock | 20 +++--- backend/pyproject.toml | 2 +- 4 files changed, 99 insertions(+), 33 deletions(-) create mode 100755 backend/app/alembic/versions/9c0a54914c78_add_max_length_for_string_varchar_.py diff --git a/backend/app/alembic/versions/9c0a54914c78_add_max_length_for_string_varchar_.py b/backend/app/alembic/versions/9c0a54914c78_add_max_length_for_string_varchar_.py new file mode 100755 index 0000000..78a4177 --- /dev/null +++ b/backend/app/alembic/versions/9c0a54914c78_add_max_length_for_string_varchar_.py @@ -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) diff --git a/backend/app/models.py b/backend/app/models.py index 8e74fea..be92454 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -1,43 +1,40 @@ +from pydantic import EmailStr from sqlmodel import Field, Relationship, SQLModel # Shared properties -# TODO replace email str with EmailStr when sqlmodel supports it 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_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 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): - email: str - password: str - full_name: str | None = None + email: EmailStr = Field(max_length=255) + password: str = Field(min_length=8, max_length=40) + full_name: str | None = Field(default=None, max_length=255) # Properties to receive via API on update, all are optional -# TODO replace email str with EmailStr when sqlmodel supports it class UserUpdate(UserBase): - email: str | None = None # type: ignore - password: str | None = None + email: EmailStr | None = Field(default=None, max_length=255) # type: ignore + 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): - full_name: str | None = None - email: str | None = None + full_name: str | None = Field(default=None, max_length=255) + email: EmailStr | None = Field(default=None, max_length=255) class UpdatePassword(SQLModel): - current_password: str - new_password: str + current_password: str = Field(min_length=8, max_length=40) + new_password: str = Field(min_length=8, max_length=40) # Database model, database table inferred from class name @@ -59,24 +56,24 @@ class UsersPublic(SQLModel): # Shared properties class ItemBase(SQLModel): - title: str - description: str | None = None + title: str = Field(min_length=1, max_length=255) + description: str | None = Field(default=None, max_length=255) # Properties to receive on item creation class ItemCreate(ItemBase): - title: str + title: str = Field(min_length=1, max_length=255) # Properties to receive on item update 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 class Item(ItemBase, table=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: User | None = Relationship(back_populates="items") @@ -110,4 +107,4 @@ class TokenPayload(SQLModel): class NewPassword(SQLModel): token: str - new_password: str + new_password: str = Field(min_length=8, max_length=40) diff --git a/backend/poetry.lock b/backend/poetry.lock index 9ab5fb0..aa96ea8 100644 --- a/backend/poetry.lock +++ b/backend/poetry.lock @@ -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]] name = "alembic" @@ -1431,13 +1431,13 @@ files = [ [[package]] name = "requests" -version = "2.32.0" +version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" files = [ - {file = "requests-2.32.0-py3-none-any.whl", hash = "sha256:f2c3881dddb70d056c5bd7600a4fae312b2a300e39be6a118d30b90bd27262b5"}, - {file = "requests-2.32.0.tar.gz", hash = "sha256:fa5490319474c82ef1d2c9bc459d3652e3ae4ef4c4ebdd18a21145a47ca4b6b8"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] @@ -1649,18 +1649,18 @@ sqlcipher = ["sqlcipher3_binary"] [[package]] name = "sqlmodel" -version = "0.0.16" +version = "0.0.19" description = "SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness." optional = false -python-versions = ">=3.7,<4.0" +python-versions = ">=3.7" files = [ - {file = "sqlmodel-0.0.16-py3-none-any.whl", hash = "sha256:b972f5d319580d6c37ecc417881f6ec4d1ad3ed3583d0ac0ed43234a28bf605a"}, - {file = "sqlmodel-0.0.16.tar.gz", hash = "sha256:966656f18a8e9a2d159eb215b07fb0cf5222acfae3362707ca611848a8a06bd1"}, + {file = "sqlmodel-0.0.19-py3-none-any.whl", hash = "sha256:6c8125d4101970d031e9aae970b20cbeaf44149989f8366d939f4ab21aab8763"}, + {file = "sqlmodel-0.0.19.tar.gz", hash = "sha256:95449b0b48a40a3eecf0a629fa5735b9dfc8a5574a91090d24ca17f02246ad96"}, ] [package.dependencies] pydantic = ">=1.10.13,<3.0.0" -SQLAlchemy = ">=2.0.0,<2.1.0" +SQLAlchemy = ">=2.0.14,<2.1.0" [[package]] name = "starlette" @@ -2015,4 +2015,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "97e309fdf90731cf04592e72629274453bdfa3b2a001f238098456d6d1ae5a73" +content-hash = "1ce11888d9f96ef6ac10ec5f4409e7ffab06ba648aa720d75079924dbfbb6de5" diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 06944f0..e73475d 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -20,7 +20,7 @@ jinja2 = "^3.1.4" alembic = "^1.12.1" httpx = "^0.25.1" psycopg = {extras = ["binary"], version = "^3.1.13"} -sqlmodel = "^0.0.16" +sqlmodel = "^0.0.19" # Pin bcrypt until passlib supports the latest bcrypt = "4.0.1" pydantic-settings = "^2.2.1"