python-3.xpostgresqlsqlalchemyfactory

How can I batch create a SQL Alchemy model using factory boy?


Here is my database model (User):

from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column

class SqlAlchemyBase(DeclarativeBase):
    """Base SQL alchemy model."""

class User(SqlAlchemyBase):
    """User database model."""

    __tablename__ = "users"

    id_: Mapped[uuid] = mapped_column(primary_key=True, index=True, default=uuid.uuid4)
    first_name: Mapped[str]
    last_name: Mapped[str]
    email: Mapped[str] = mapped_column(unique=True, index=True)
    hashed_password: Mapped[str]
    is_superuser: Mapped[bool]

Here is my factory for the above model:

import factory    
import factory.fuzzy

class UserFactory(factory.alchemy.SQLAlchemyModelFactory):

    class Meta:    
        model = User

    id_ = factory.Faker("uuid4")
    first_name = factory.Faker("first_name")
    last_name = factory.Faker("last_name")
    email = factory.LazyAttribute(lambda obj: f"{obj.first_name}.{obj.last_name}@gmail.com")
    hashed_password = "HashedPwd"
    is_superuser = False

How do I create 200 users?
Here are some things I have tried...

Attempt 1:
As per the factory boy documentation here, inside a test function, I tried doing the following:

users = UserFactory().create_batch(200)

But this fails miserably with the following error:

AttributeError: 'User' object has no attribute 'create_batch'

Attempt 2:
I also tried UserFactory().build_batch(200) but get the same error.

Attempt 3:

Following the docs here

import factory
factory.create_batch(UserFactory, size=200, FACTORY_CLASS=factory.alchemy.SQLAlchemyModelFactory)

This gave me the error:

TypeError: <class 'v1.database.models.test_factories.users.UserFactory'> is already a Factory

Here is what I am using:
Python 3.11
SQLAlchemy~=2.0
factory-boy~=3.3
pytest~=7.4


Solution

  • users = UserFactory().create_batch(200) should be users = UserFactory.create_batch(200)

    And your email address is not unique, you need to add some random string for your email as well. The following code works for me:

    from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
    from sqlalchemy import create_engine, String
    from sqlalchemy.orm import sessionmaker, scoped_session
    import uuid
    import factory.fuzzy
    
    # Define the database URL and create the engine
    DATABASE_URL = "sqlite:///./test.db"  # Example database URL
    engine = create_engine(DATABASE_URL)
    SessionLocal = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
    
    
    # Define the base model class
    class SqlAlchemyBase(DeclarativeBase):
        """Base SQLAlchemy model."""
        pass
    
    
    # Define the User model
    class User(SqlAlchemyBase):
        """User database model."""
    
        __tablename__ = "users"
    
        id_: Mapped[uuid.UUID] = mapped_column(primary_key=True, index=True, default=uuid.uuid4, type_=String(36))
        first_name: Mapped[str]
        last_name: Mapped[str]
        email: Mapped[str] = mapped_column(unique=True, index=True)
        hashed_password: Mapped[str]
        is_superuser: Mapped[bool]
    
    
    # Create the tables in the database
    SqlAlchemyBase.metadata.create_all(bind=engine)
    
    
    # Define the UserFactory
    class UserFactory(factory.alchemy.SQLAlchemyModelFactory):
    
        class Meta:
            model = User
            sqlalchemy_session = SessionLocal()  # Use the session we created
    
        id_ = factory.LazyFunction(lambda: str(uuid.uuid4()))
        first_name = factory.Faker("first_name")
        last_name = factory.Faker("last_name")
        email = factory.LazyAttribute(lambda obj: f"{obj.id_}{obj.first_name}.{obj.last_name}@gmail.com")
        hashed_password = "HashedPwd"
        is_superuser = False
    
    
    # Create a batch of users and commit them to the database
    session = SessionLocal()
    users = UserFactory.create_batch(200)
    session.commit()  # Commit the changes to the database
    session.close()  # Close the session