diff --git a/src/backend/langflow/alembic/versions/7d2162acc8b2_adds_updated_at_and_folder_cols.py b/src/backend/langflow/alembic/versions/7d2162acc8b2_adds_updated_at_and_folder_cols.py new file mode 100644 index 000000000..0138c0506 --- /dev/null +++ b/src/backend/langflow/alembic/versions/7d2162acc8b2_adds_updated_at_and_folder_cols.py @@ -0,0 +1,79 @@ +"""Adds updated_at and folder cols + +Revision ID: 7d2162acc8b2 +Revises: f5ee9749d1a6 +Create Date: 2023-11-21 20:56:53.998781 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import sqlmodel + + +# revision identifiers, used by Alembic. +revision: str = '7d2162acc8b2' +down_revision: Union[str, None] = 'f5ee9749d1a6' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('component', schema=None) as batch_op: + batch_op.drop_index('ix_component_frontend_node_id') + batch_op.drop_index('ix_component_name') + + op.drop_table('component') + op.drop_table('flowstyle') + with op.batch_alter_table('apikey', schema=None) as batch_op: + batch_op.alter_column('name', + existing_type=sa.VARCHAR(), + nullable=False) + + with op.batch_alter_table('flow', schema=None) as batch_op: + batch_op.add_column(sa.Column('updated_at', sa.DateTime(), nullable=True)) + batch_op.add_column(sa.Column('folder', sqlmodel.sql.sqltypes.AutoString(), nullable=True)) + + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('flow', schema=None) as batch_op: + batch_op.drop_column('folder') + batch_op.drop_column('updated_at') + + with op.batch_alter_table('apikey', schema=None) as batch_op: + batch_op.alter_column('name', + existing_type=sa.VARCHAR(), + nullable=True) + + op.create_table('flowstyle', + sa.Column('color', sa.VARCHAR(), nullable=False), + sa.Column('emoji', sa.VARCHAR(), nullable=False), + sa.Column('flow_id', sa.CHAR(length=32), nullable=True), + sa.Column('id', sa.CHAR(length=32), nullable=False), + sa.ForeignKeyConstraint(['flow_id'], ['flow.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('id') + ) + op.create_table('component', + sa.Column('id', sa.CHAR(length=32), nullable=False), + sa.Column('frontend_node_id', sa.CHAR(length=32), nullable=False), + sa.Column('name', sa.VARCHAR(), nullable=False), + sa.Column('description', sa.VARCHAR(), nullable=True), + sa.Column('python_code', sa.VARCHAR(), nullable=True), + sa.Column('return_type', sa.VARCHAR(), nullable=True), + sa.Column('is_disabled', sa.BOOLEAN(), nullable=False), + sa.Column('is_read_only', sa.BOOLEAN(), nullable=False), + sa.Column('create_at', sa.DATETIME(), nullable=False), + sa.Column('update_at', sa.DATETIME(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + with op.batch_alter_table('component', schema=None) as batch_op: + batch_op.create_index('ix_component_name', ['name'], unique=False) + batch_op.create_index('ix_component_frontend_node_id', ['frontend_node_id'], unique=False) + + # ### end Alembic commands ### diff --git a/src/backend/langflow/api/v1/flows.py b/src/backend/langflow/api/v1/flows.py index 5e4dde4e0..d35b2a8d5 100644 --- a/src/backend/langflow/api/v1/flows.py +++ b/src/backend/langflow/api/v1/flows.py @@ -1,3 +1,4 @@ +from datetime import datetime from typing import List from uuid import UUID @@ -9,12 +10,7 @@ from sqlmodel import Session from langflow.api.utils import remove_api_keys from langflow.api.v1.schemas import FlowListCreate, FlowListRead from langflow.services.auth.utils import get_current_active_user -from langflow.services.database.models.flow import ( - Flow, - FlowCreate, - FlowRead, - FlowUpdate, -) +from langflow.services.database.models.flow import Flow, FlowCreate, FlowRead, FlowUpdate from langflow.services.database.models.user.user import User from langflow.services.deps import get_session, get_settings_service @@ -33,7 +29,8 @@ def create_flow( if flow.user_id is None: flow.user_id = current_user.id - db_flow = Flow.from_orm(flow) + db_flow = Flow.model_validate(flow, from_attributes=True) + db_flow.updated_at = datetime.utcnow() session.add(db_flow) session.commit() @@ -82,12 +79,13 @@ def update_flow( db_flow = read_flow(session=session, flow_id=flow_id, current_user=current_user) if not db_flow: raise HTTPException(status_code=404, detail="Flow not found") - flow_data = flow.dict(exclude_unset=True) + flow_data = flow.model_dump(exclude_unset=True) if settings_service.settings.REMOVE_API_KEYS: flow_data = remove_api_keys(flow_data) for key, value in flow_data.items(): if value is not None: setattr(db_flow, key, value) + db_flow.updated_at = datetime.utcnow() session.add(db_flow) session.commit() session.refresh(db_flow) diff --git a/src/backend/langflow/services/database/models/flow/flow.py b/src/backend/langflow/services/database/models/flow/flow.py index 310969364..89cf1fbd4 100644 --- a/src/backend/langflow/services/database/models/flow/flow.py +++ b/src/backend/langflow/services/database/models/flow/flow.py @@ -1,11 +1,12 @@ # Path: src/backend/langflow/database/models/flow.py +from datetime import datetime +from typing import TYPE_CHECKING, Dict, Optional +from uuid import UUID, uuid4 + from langflow.services.database.models.base import SQLModelSerializable from pydantic import field_validator - -from sqlmodel import Field, JSON, Column, Relationship -from uuid import UUID, uuid4 -from typing import Dict, Optional, TYPE_CHECKING +from sqlmodel import JSON, Column, Field, Relationship if TYPE_CHECKING: from langflow.services.database.models.user import User @@ -16,6 +17,8 @@ class FlowBase(SQLModelSerializable): description: Optional[str] = Field(index=True, nullable=True, default=None) data: Optional[Dict] = Field(default=None, nullable=True) is_component: Optional[bool] = Field(default=False, nullable=True) + updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=True) + folder: Optional[str] = Field(default=None, nullable=True) @field_validator("data") def validate_json(v): diff --git a/tests/test_database.py b/tests/test_database.py index 4a1219799..e447a677f 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -182,7 +182,7 @@ def test_download_file( with session_getter(db_manager) as session: for flow in flow_list.flows: flow.user_id = active_user.id - db_flow = Flow.model_validate(flow) + db_flow = Flow.model_validate(flow, from_attributes=True) session.add(db_flow) session.commit() # Make request to endpoint