diff --git a/sqlalchemy_1.6_to_2.0/README.md b/sqlalchemy_1.6_to_2.0/README.md index ad50ddd..886599b 100644 --- a/sqlalchemy_1.6_to_2.0/README.md +++ b/sqlalchemy_1.6_to_2.0/README.md @@ -1,64 +1,61 @@ # SQLAlchemy 1.6 to 2.0 Migration Example -[![Documentation](https://img.shields.io/badge/docs-docs.codegen.com-blue)](https://docs.codegen.com/tutorials/sqlalchemy-1.6-to-2.0) +This codemod demonstrates how to use Codegen to automatically migrate SQLAlchemy 1.6 code to the new 2.0-style query interface. For a complete walkthrough, check out our [tutorial](https://docs.codegen.com/tutorials/sqlalchemy-1.6-to-2.0). -This example demonstrates how to use Codegen to automatically migrate SQLAlchemy 1.6 code to the new 2.0-style query interface. For a complete walkthrough, check out our [tutorial](https://docs.codegen.com/tutorials/sqlalchemy-1.6-to-2.0). +## How the Migration Script Works -## What This Example Does +The codemod script handles four key transformations: -The migration script handles four key transformations: - -1. **Convert Query to Select** +1. **Update Base Class and Imports** ```python # From: - session.query(User).filter_by(name='john').all() + from sqlalchemy.ext.declarative import declarative_base + Base = declarative_base() # To: - session.execute( - select(User).where(User.name == 'john') - ).scalars().all() + from sqlalchemy.orm import DeclarativeBase + class Base(DeclarativeBase): + pass ``` + Updates the Base class to use the new DeclarativeBase style. -2. **Update Session Execution** +2. **Modernize ORM Relationships** ```python # From: - users = session.query(User).all() - first_user = session.query(User).first() + class User(Base): + addresses = relationship("Address", backref="user") # To: - users = session.execute(select(User)).scalars().all() - first_user = session.execute(select(User)).scalars().first() + class User(Base): + addresses = relationship("Address", back_populates="user") ``` + Relationships are modernized to use explicit back_populates instead of backref. If no back reference is specified, it defaults to None. -3. **Modernize ORM Relationships** +3. **Update Query Method Names** ```python # From: - class User(Base): - addresses = relationship("Address", backref="user") + db.session.query(User).filter(User.name == 'john').all() + db.session.query(User).first() # To: - class User(Base): - addresses = relationship("Address", back_populates="user", use_list=True) - class Address(Base): - user = relationship("User", back_populates="addresses") + db.session.select(User).where(User.name == 'john').scalars().all() + db.session.select(User).scalar_one_or_none() ``` + Updates query method names to their 2.0 equivalents: `query` → `select`, `filter` → `where`, `all` → `scalars().all`, and `first` → `scalar_one_or_none`. -4. **Add Type Annotations** +4. **Update Configurations** ```python # From: - class User(Base): - __tablename__ = "users" - id = Column(Integer, primary_key=True) - name = Column(String) - addresses = relationship("Address") + create_engine(url) + sessionmaker(bind=engine) + relationship("Address") # To: - class User(Base): - __tablename__ = "users" - id: Mapped[int] = mapped_column(primary_key=True) - name: Mapped[str] = mapped_column() - addresses: Mapped[List["Address"]] = relationship() + create_engine(url, future=True, pool_pre_ping=True) + sessionmaker(bind=engine, future=True) + relationship("Address", lazy="select") ``` + Adds future-compatible configurations and default lazy loading settings. Also updates Pydantic configs from `orm_mode` to `from_attributes`. ## Running the Example @@ -70,13 +67,7 @@ pip install codegen python run.py ``` -The script will process all Python files in the `repo-before` directory and apply the transformations in the correct order. - -## Understanding the Code - -- `run.py` - The migration script -- `repo-before/` - Sample SQLAlchemy 1.6 application to migrate -- `guide.md` - Additional notes and explanations +The script will process all Python files in the `input_repo` directory and apply the transformations in the correct order. ## Learn More diff --git a/sqlalchemy_1.6_to_2.0/guide.md b/sqlalchemy_1.6_to_2.0/guide.md deleted file mode 100644 index 62b2e61..0000000 --- a/sqlalchemy_1.6_to_2.0/guide.md +++ /dev/null @@ -1,96 +0,0 @@ -# Guide: Migrating from SQLAlchemy 1.6 to 2.0 with Codegen - -This guide walks you through the steps to migrate your codebase from SQLAlchemy 1.6 to 2.0 using Codegen. Follow along to modernize your imports, relationships, and query syntax while ensuring compatibility with SQLAlchemy 2.0. Each step includes a direct link to the appropriate codemod for easy implementation. - ---- - -## šŸŽ‰ Overview of Changes - -The migration focuses on these key updates: - -1. **Import Adjustments** - Aligns your code with the updated SQLAlchemy 2.0 module structure. - [Run the Import Codemod](https://www.codegen.sh/search/6506?skillType=codemod) - -2. **Relationship Updates** - Refines relationship definitions by replacing `backref` with `back_populates` for explicitness and better readability. - [Run the Relationship Codemod](https://www.codegen.sh/search/6510?skillType=codemod) - -3. **Query Syntax Modernization** - Updates queries to leverage the latest syntax like `select()` and `where()`, removing deprecated methods. - [Run the Query Syntax Codemod](https://www.codegen.sh/search/6508?skillType=codemod) - -4. **Relationship Lazy Loading** - SQLAlchemy 2.0 introduces a new `lazy` parameter for relationship definitions. Update your relationships to use the new `lazy` parameter for improved performance. - [Run the Relationship Lazy Loading Codemod](https://www.codegen.sh/search/6512?skillType=codemod) - -5. **Type Annotations** - SQLAlchemy 2.0 has improved type annotation support. Update your models to include type hints for better IDE support and runtime type checking. - - - Add type annotations to model attributes and relationships - - Leverage SQLAlchemy's typing module for proper type hints - - Enable better IDE autocompletion and type checking - - [Run the Type Annotations Codemod](https://www.codegen.sh/search/4645?skillType=codemod) - ---- - -## How to Migrate - -### Step 1: Update Imports - -SQLAlchemy 2.0 introduces a refined import structure. Use the import codemod to: - -- Replace wildcard imports (`*`) with explicit imports for better clarity. -- Update `declarative_base` to `DeclarativeBase`. - -šŸ‘‰ [Run the Import Codemod](https://www.codegen.sh/search/6506?skillType=codemod) - ---- - -### Step 2: Refactor Relationships - -In SQLAlchemy 2.0, relationships require more explicit definitions. This includes: - -- Transitioning from `backref` to `back_populates` for consistency. -- Explicitly specifying `back_populates` for all relationship definitions. - -šŸ‘‰ [Run the Relationship Codemod](https://www.codegen.sh/search/6510?skillType=codemod) - ---- - -### Step 3: Modernize Query Syntax - -The query API has been revamped in SQLAlchemy 2.0. Key updates include: - -- Switching to `select()` and `where()` for query construction. -- Replacing any deprecated methods with their modern equivalents. - -šŸ‘‰ [Run the Query Syntax Codemod](https://www.codegen.sh/search/6508?skillType=codemod) - ---- - -### Step 4: Update Relationship Lazy Loading - -SQLAlchemy 2.0 introduces a new `lazy` parameter for relationship definitions. Update your relationships to use the new `lazy` parameter for improved performance. - -šŸ‘‰ [Run the Relationship Lazy Loading Codemod](https://www.codegen.sh/search/6512?skillType=codemod) - ---- - -### Step 5: Add Type Annotations -SQLAlchemy 2.0 has improved type annotation support. Update your models to include type hints for better IDE support and runtime type checking. - -- Add type annotations to model attributes and relationships -- Leverage SQLAlchemy's typing module for proper type hints -- Enable better IDE autocompletion and type checking - -šŸ‘‰ [Run the Type Annotations Codemod](https://www.codegen.sh/search/4645?skillType=codemod) - ---- - -## Need Help? - -If you encounter issues or have specific edge cases not addressed by the codemods, reach out to the Codegen support team or visit the [Codegen Documentation](https://www.codegen.sh/docs) for detailed guidance. - -Start your SQLAlchemy 2.0 migration today and enjoy the benefits of a cleaner, modern codebase! diff --git a/sqlalchemy_1.6_to_2.0/input_repo/database.py b/sqlalchemy_1.6_to_2.0/input_repo/database.py index c07234d..0bfd0aa 100644 --- a/sqlalchemy_1.6_to_2.0/input_repo/database.py +++ b/sqlalchemy_1.6_to_2.0/input_repo/database.py @@ -3,7 +3,9 @@ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker -SQLALCHEMY_DATABASE_URL = "postgresql://user:password@localhost/dbname" # Change to your database URL +SQLALCHEMY_DATABASE_URL = ( + "postgresql://user:password@localhost/dbname" # Change to your database URL +) engine = create_engine(SQLALCHEMY_DATABASE_URL) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) diff --git a/sqlalchemy_1.6_to_2.0/input_repo/models.py b/sqlalchemy_1.6_to_2.0/input_repo/models.py index cd1a9a8..d87655f 100644 --- a/sqlalchemy_1.6_to_2.0/input_repo/models.py +++ b/sqlalchemy_1.6_to_2.0/input_repo/models.py @@ -1,7 +1,8 @@ -from sqlalchemy import Column, Integer, String, ForeignKey, Index -from sqlalchemy.orm import relationship, backref +from sqlalchemy import Column, Integer, String, ForeignKey +from sqlalchemy.orm import relationship from database import Base + class Publisher(Base): __tablename__ = "publishers" diff --git a/sqlalchemy_1.6_to_2.0/output_repo/database.py b/sqlalchemy_1.6_to_2.0/output_repo/database.py deleted file mode 100644 index c606b3b..0000000 --- a/sqlalchemy_1.6_to_2.0/output_repo/database.py +++ /dev/null @@ -1,11 +0,0 @@ -# database.py -from sqlalchemy import create_engine -from sqlalchemy.orm import DeclarativeBase -from sqlalchemy.orm import sessionmaker - -SQLALCHEMY_DATABASE_URL = "postgresql://user:password@localhost/dbname" # Change to your database URL - -engine = create_engine(SQLALCHEMY_DATABASE_URL) -SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) - -Base = declarative_base() diff --git a/sqlalchemy_1.6_to_2.0/output_repo/main.py b/sqlalchemy_1.6_to_2.0/output_repo/main.py deleted file mode 100644 index 1c62632..0000000 --- a/sqlalchemy_1.6_to_2.0/output_repo/main.py +++ /dev/null @@ -1,101 +0,0 @@ -# main.py -from fastapi import FastAPI, Depends, HTTPException -from sqlalchemy.orm import Session -import models, schemas -from database import SessionLocal, engine -from typing import List - -# Initialize the app and create database tables -app = FastAPI() -models.Base.metadata.create_all(bind=engine) - -# Dependency for the database session -def get_db(): - db = SessionLocal() - try: - yield db - finally: - db.close() - -# CRUD Operations - -@app.post("/books/", response_model=schemas.Book) -def create_book(book: schemas.BookCreate, db: Session = Depends(get_db)): - db_book = models.Book(**book.dict()) - db.add(db_book) - db.commit() - db.refresh(db_book) - return db_book - -@app.get("/books/", response_model=List[schemas.Book]) -def read_books(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)): - books = db.query()(models.Book).offset(skip).limit(limit).scalars().all() - return books - -@app.get("/books/{book_id}", response_model=schemas.Book) -def read_book(book_id: int, db: Session = Depends(get_db)): - book = db.query()(models.Book).where(models.Book.id == book_id).first() - if book is None: - raise HTTPException(status_code=404, detail="Book not found") - return book - -@app.put("/books/{book_id}", response_model=schemas.Book) -def update_book(book_id: int, book: schemas.BookCreate, db: Session = Depends(get_db)): - db_book = db.query()(models.Book).where(models.Book.id == book_id).first() - if db_book is None: - raise HTTPException(status_code=404, detail="Book not found") - for key, value in book.dict().items(): - setattr(db_book, key, value) - db.commit() - db.refresh(db_book) - return db_book - -@app.delete("/books/{book_id}", response_model=schemas.Book) -def delete_book(book_id: int, db: Session = Depends(get_db)): - db_book = db.query()(models.Book).where(models.Book.id == book_id).first() - if db_book is None: - raise HTTPException(status_code=404, detail="Book not found") - db.delete(db_book) - db.commit() - return db_book - -@app.post("/publishers/", response_model=schemas.Publisher) -def create_publisher(publisher: schemas.PublisherCreate, db: Session = Depends(get_db)): - db_publisher = models.Publisher(**publisher.dict()) - db.add(db_publisher) - db.commit() - db.refresh(db_publisher) - return db_publisher - -@app.get("/publishers/", response_model=List[schemas.Publisher]) -def read_publishers(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)): - publishers = db.query()(models.Publisher).offset(skip).limit(limit).scalars().all() - return publishers - -@app.get("/publishers/{publisher_id}", response_model=schemas.Publisher) -def read_publisher(publisher_id: int, db: Session = Depends(get_db)): - publisher = db.query()(models.Publisher).where(models.Publisher.id == publisher_id).first() - if not publisher: - raise HTTPException(status_code=404, detail="Publisher not found") - return publisher - -@app.put("/publishers/{publisher_id}", response_model=schemas.Publisher) -def update_publisher(publisher_id: int, publisher: schemas.PublisherCreate, db: Session = Depends(get_db)): - db_publisher = db.query()(models.Publisher).where(models.Publisher.id == publisher_id).first() - if not db_publisher: - raise HTTPException(status_code=404, detail="Publisher not found") - for key, value in publisher.dict().items(): - setattr(db_publisher, key, value) - db.commit() - db.refresh(db_publisher) - return db_publisher - -@app.delete("/publishers/{publisher_id}", response_model=schemas.Publisher) -def delete_publisher(publisher_id: int, db: Session = Depends(get_db)): - db_publisher = db.query()(models.Publisher).where(models.Publisher.id == publisher_id).first() - if not db_publisher: - raise HTTPException(status_code=404, detail="Publisher not found") - db.delete(db_publisher) - db.commit() - return db_publisher - diff --git a/sqlalchemy_1.6_to_2.0/output_repo/models.py b/sqlalchemy_1.6_to_2.0/output_repo/models.py deleted file mode 100644 index 25ad484..0000000 --- a/sqlalchemy_1.6_to_2.0/output_repo/models.py +++ /dev/null @@ -1,32 +0,0 @@ -from typing import List, Optional -from sqlalchemy import Column, Integer, String, ForeignKey -from sqlalchemy.orm import relationship, Mapped, mapped_column -from database import Base - -class Publisher(Base): - __tablename__ = "publishers" - - id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) - name: Mapped[str] = mapped_column(String, unique=True, index=True) - books: Mapped[List["Book"]] = relationship( - "Book", - back_populates="publisher", - lazy='selectin' - ) - -class Book(Base): - __tablename__ = "books" - - id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) - title: Mapped[str] = mapped_column(String, index=True) - author: Mapped[str] = mapped_column(String, index=True) - description: Mapped[Optional[str]] = mapped_column(String, nullable=True) - publisher_id: Mapped[Optional[int]] = mapped_column( - Integer, - ForeignKey("publishers.id"), - nullable=True - ) - publisher: Mapped[Optional["Publisher"]] = relationship( - "Publisher", - back_populates="books" - ) \ No newline at end of file diff --git a/sqlalchemy_1.6_to_2.0/output_repo/schemas.py b/sqlalchemy_1.6_to_2.0/output_repo/schemas.py deleted file mode 100644 index daf4fb9..0000000 --- a/sqlalchemy_1.6_to_2.0/output_repo/schemas.py +++ /dev/null @@ -1,31 +0,0 @@ -from pydantic import BaseModel -from typing import List, Optional - -class PublisherBase(BaseModel): - name: str - -class PublisherCreate(PublisherBase): - pass - -class Publisher(PublisherBase): - id: int - books: List["Book"] = [] - - class Config: - orm_mode = True - -class BookBase(BaseModel): - title: str - author: str - description: str - publisher_id: Optional[int] - -class BookCreate(BookBase): - pass - -class Book(BookBase): - id: int - publisher: Optional[Publisher] - - class Config: - orm_mode = True diff --git a/sqlalchemy_1.6_to_2.0/run.py b/sqlalchemy_1.6_to_2.0/run.py new file mode 100644 index 0000000..4bdefac --- /dev/null +++ b/sqlalchemy_1.6_to_2.0/run.py @@ -0,0 +1,136 @@ +import codegen +from codegen import Codebase + + +@codegen.function("sqlalchemy-1.6-to-2.0") +def run(codebase: Codebase): + """Migrate SQLAlchemy code from 1.6 to 2.0 style. + + Updates: + 1. Base class inheritance and imports + 2. Relationship definitions (backref -> back_populates) + 3. Query syntax to 2.0 style + 4. Configuration and lazy loading defaults + """ + for file in codebase.files: + print(f"šŸ” Processing {file.filepath}") + + for updater in [ + update_base_class, + update_relationships, + update_query_syntax, + update_configurations, + ]: + updater(file) + + # Print changes using codebase diff + print("\nšŸ“ Changes made:") + print(codebase.get_diff()) + + +def update_base_class(file): + """Update Base class inheritance and imports.""" + # Update imports + if any("Base" in cls.parent_class_names for cls in file.classes): + file.add_import_from_import_string("from sqlalchemy.orm import DeclarativeBase") + + for imp in file.imports: + if imp.symbol_name == "declarative_base": + imp.remove() + + # Update Base classes + for cls in file.classes: + if cls.name == "Base": + cls.set_parent_class("DeclarativeBase") + elif "Base" in cls.parent_class_names and not any(c.name == "Base" for c in file.classes): + cls.insert_before("\nclass Base(DeclarativeBase):\n pass\n") + + +def update_relationships(file): + """Modernize relationship definitions.""" + changes = [] + + def process_relationship(symbol): + for call in symbol.function_calls: + if call.name != "relationship": + continue + + arg_names = [arg.name for arg in call.args if arg.name] + if "backref" in arg_names: + backref_arg = next(arg for arg in call.args if arg.name == "backref") + old_value = f'relationship("{call.args[0].value.source}", backref="{backref_arg.value.source}"' + call.set_kwarg("back_populates", backref_arg.value.source) + backref_arg.remove() + new_value = f'relationship("{call.args[0].value.source}", back_populates="{backref_arg.value.source}"' + changes.append(f"- {old_value}\n+ {new_value}") + elif "back_populates" not in arg_names: + old_value = f'relationship("{call.args[0].value.source}"' + call.set_kwarg("back_populates", "None") + new_value = f'relationship("{call.args[0].value.source}", back_populates=None' + changes.append(f"- {old_value}\n+ {new_value}") + + for item in [ + *file.functions, + *[m for c in file.classes for m in c.methods], + *[a for c in file.classes for a in c.attributes], + ]: + process_relationship(item) + + +def update_query_syntax(file): + """Update to SQLAlchemy 2.0 query style.""" + changes = [] + + query_updates = { + "query": "select", + "filter": "where", + "all": "scalars().all", + "first": "scalar_one_or_none", + } + + def process_queries(function): + for call in function.function_calls: + if new_name := query_updates.get(call.name): + old_name = call.name + call.set_name(new_name) + changes.append(f"- db.session.{old_name}()\n+ db.session.{new_name}()") + + for item in [*file.functions, *[m for c in file.classes for m in c.methods]]: + process_queries(item) + + +def update_configurations(file): + """Update engine, session, and relationship configurations.""" + changes = [] + + # Update engine and session config + for call in file.function_calls: + if call.name == "create_engine" and not any(arg.name == "future" for arg in call.args): + old_call = "create_engine(url)" + call.set_kwarg("future", "True") + call.set_kwarg("pool_pre_ping", "True") + changes.append(f"- {old_call}\n+ create_engine(url, future=True, pool_pre_ping=True)") + elif call.name == "sessionmaker" and not any(arg.name == "future" for arg in call.args): + old_call = "sessionmaker(bind=engine)" + call.set_kwarg("future", "True") + changes.append(f"- {old_call}\n+ sessionmaker(bind=engine, future=True)") + elif call.name == "relationship" and not any(arg.name == "lazy" for arg in call.args): + old_call = f'relationship("{call.args[0].value.source}"' + call.set_kwarg("lazy", '"select"') + changes.append(f'- {old_call}\n+ {old_call}, lazy="select")') + + # Update Pydantic configs + for cls in file.classes: + if cls.name == "Config": + for attr in cls.attributes: + if attr.name == "orm_mode": + old_attr = "orm_mode = True" + attr.set_name("from_attributes") + attr.set_value("True") + changes.append(f"- {old_attr}\n+ from_attributes = True") + + +if __name__ == "__main__": + print("\nInitializing codebase...") + codebase = Codebase("./input_repo") + run(codebase) diff --git a/sqlalchemy_type_annotations/README.md b/sqlalchemy_type_annotations/README.md index 776211e..8481be6 100644 --- a/sqlalchemy_type_annotations/README.md +++ b/sqlalchemy_type_annotations/README.md @@ -1,78 +1,50 @@ # Enhance SQLAlchemy Type Annotations -This codemod demonstrates how to automatically add type annotations to SQLAlchemy models in your Python codebase. The migration script makes this process simple by handling all the tedious manual updates automatically. - -## How the Migration Script Works - -The script automates the entire migration process in a few key steps: - -1. **Model Detection and Analysis** - ```python - codebase = Codebase.from_repo("your/repo") - for file in codebase.files: - if "models" not in file.filepath: - continue - ``` - - Automatically identifies SQLAlchemy model files - - Analyzes model structure and relationships - - Determines required type annotations - -2. **Type Annotation Updates** - ```python - for column in model.columns: - if isinstance(column, Column): - column.edit(to_mapped_column(column)) - ``` - - Converts Column definitions to typed Mapped columns - - Handles nullable fields with Optional types - - Preserves existing column configurations - -3. **Relationship Transformations** - ```python - for rel in model.relationships: - if isinstance(rel, relationship): - rel.edit(to_typed_relationship(rel)) - ``` - - Updates relationship definitions with proper typing - - Converts backref to back_populates - - Adds List/Optional type wrappers as needed - -## Common Migration Patterns - -### Column Definitions -```python -# Before +This codemod demonstrates how to automatically add type annotations to SQLAlchemy models in your Python codebase. This conversion improves code maintainability and IDE support by adding proper type hints to model attributes and relationships. + +This primarily leverages two APIs: +- [`codebase.files`](/api-reference/Codebase#files) for finding relevant files +- [`file.edit(...)`](/api-reference/Codebase#files) for updating model definitions + +## What This Example Does + +The codemod handles three key transformations: + +1. **Convert Column Definitions to Mapped Types** +```python python +# From: id = Column(Integer, primary_key=True) name = Column(String) -# After +# To: id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] = mapped_column() ``` -### Nullable Fields -```python -# Before -description = Column(String, nullable=True) +2. **Update Relationship Definitions** +```python python + # From: +addresses = relationship("Address", backref="user") -# After -description: Mapped[Optional[str]] = mapped_column(nullable=True) +# To: +addresses: Mapped[List["Address"]] = relationship(back_populates="user") ``` -### Relationships -```python -# Before -addresses = relationship("Address", backref="user") +3. **Update Relationship Definitions** +```python python +# From: +description = Column(String, nullable=True) -# After -addresses: Mapped[List["Address"]] = relationship(back_populates="user") +# To: +description: Mapped[Optional[str]] = mapped_column(nullable=True) ``` -## Complete Example +## Example Implementation -### Before Migration -```python -from sqlalchemy import Column, Integer, String, ForeignKey +For a complete example of the transformation, see: + +```python python +from sqlalchemy import Column, Integer, String, ForeignKey, Index from sqlalchemy.orm import relationship, backref from database import Base @@ -83,6 +55,7 @@ class Publisher(Base): name = Column(String, unique=True, index=True) books = relationship("Book", backref="publisher") + class Book(Base): __tablename__ = "books" @@ -93,31 +66,34 @@ class Book(Base): publisher_id = Column(Integer, ForeignKey("publishers.id")) ``` -### After Migration -```python +And its transformed version: + +```python python from typing import List, Optional -from sqlalchemy import ForeignKey -from sqlalchemy.orm import Mapped, mapped_column, relationship +from sqlalchemy import Column, Integer, String, ForeignKey +from sqlalchemy.orm import relationship, Mapped, mapped_column from database import Base class Publisher(Base): __tablename__ = "publishers" - id: Mapped[int] = mapped_column(primary_key=True, index=True) - name: Mapped[str] = mapped_column(unique=True, index=True) + id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) + name: Mapped[str] = mapped_column(String, unique=True, index=True) books: Mapped[List["Book"]] = relationship( "Book", - back_populates="publisher" + back_populates="publisher", + lazy='selectin' ) class Book(Base): __tablename__ = "books" - id: Mapped[int] = mapped_column(primary_key=True, index=True) - title: Mapped[str] = mapped_column(index=True) - author: Mapped[str] = mapped_column(index=True) - description: Mapped[Optional[str]] = mapped_column(nullable=True) + id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) + title: Mapped[str] = mapped_column(String, index=True) + author: Mapped[str] = mapped_column(String, index=True) + description: Mapped[Optional[str]] = mapped_column(String, nullable=True) publisher_id: Mapped[Optional[int]] = mapped_column( + Integer, ForeignKey("publishers.id"), nullable=True ) @@ -127,21 +103,25 @@ class Book(Base): ) ``` -## Running the Migration - -```bash -# Install Codegen -pip install codegen -# Run the migration -python run.py -``` +## Running the Transformation -## Learn More +1. Install the codegen package: + ```bash + pip install codegen + ``` -- [SQLAlchemy 2.0 Documentation](https://docs.sqlalchemy.org/en/20/) -- [SQLAlchemy Type Annotations Guide](https://docs.sqlalchemy.org/en/20/orm/typing.html) -- [Codegen Documentation](https://docs.codegen.com) +2. Run the codemod: + ```bash + python3 run.py + ``` -## Contributing +This will: +1. Initialize the codebase from the target repository +2. Find and process files containing `useSuspenseQuery` +3. Apply the transformations +4. Print detailed information to the terminal, including: + - Files being processed + - Before/after diffs for each transformation + - Summary statistics of modified files and functions -Feel free to submit issues and enhancement requests! \ No newline at end of file +The script will output progress and changes to the terminal as it runs, allowing you to review each transformation in real-time. \ No newline at end of file