Factories
pre_save
is a method decorator that will always run before the instance is saved.post_save
is a method decorator that will always run after the instance is saved.trait
decorator runs the code if set to True in factory constructor.PreSave
is like thepre_save
decorator of theModelFactory
, but you can pass arguments to it and have a lot of flexibility. See a working example (below) of how set a user password in Django.PostSave
is like thepost_save
decorator of theModelFactory
, but you can pass arguments to it and have a lot of flexibility. See a working example (below) of how to assign a user to a Group after user creation.LazyAttribute
expects a callable, will take the instance as a first argument, runs it with extra arguments specified and sets the value as an attribute name.LazyFunction
expects a callable, runs it (without any arguments) and sets the value as an attribute name.SubFactory
is for specifying relations (typically - ForeignKeys).
Django example
Filename: article/models.py
from django.conf import settings
from django.db import models
from django.utils import timezone
class Article(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField(unique=True)
content = models.TextField()
headline = models.TextField()
category = models.CharField(max_length=255)
image = models.ImageField(null=True, blank=True)
pub_date = models.DateField(default=timezone.now)
safe_for_work = models.BooleanField(default=False)
minutes_to_read = models.IntegerField(default=5)
author = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE
)
See the full example
here
Filename: article/factories.py
import random
from functools import partial
from django.conf import settings
from django.contrib.auth.models import Group, User
from django.utils import timezone
from fake import (
FACTORY,
DjangoModelFactory,
FileSystemStorage,
LazyAttribute,
LazyFunction,
PostSave,
PreSave,
SubFactory,
post_save,
pre_save,
trait,
)
from article.models import Article
# For Django, all files shall be placed inside `MEDIA_ROOT` directory.
# That's why you need to apply this trick - define a
# custom `FileSystemStorage` class and pass it to the file factory as
# `storage` argument.
STORAGE = FileSystemStorage(root_path=settings.MEDIA_ROOT, rel_path="tmp")
CATEGORIES = (
"art",
"technology",
"literature",
)
class GroupFactory(DjangoModelFactory):
"""Group factory."""
name = FACTORY.word()
class Meta:
model = Group
get_or_create = ("name",)
def set_password(user: User, password: str) -> None:
user.set_password(password)
def add_to_group(user: User, name: str) -> None:
group = GroupFactory(name=name)
user.groups.add(group)
class UserFactory(DjangoModelFactory):
username = FACTORY.username()
first_name = FACTORY.first_name()
last_name = FACTORY.last_name()
email = FACTORY.email()
date_joined = FACTORY.date_time(tzinfo=timezone.get_current_timezone())
last_login = FACTORY.date_time(tzinfo=timezone.get_current_timezone())
is_superuser = False
is_staff = False
is_active = FACTORY.pybool()
password = PreSave(set_password, password="test1234")
group = PostSave(add_to_group, name="TestGroup1234")
class Meta:
model = User
get_or_create = ("username",)
@post_save
def _send_registration_email(self, instance):
"""Send an email with registration information."""
# Your code here
@trait
def is_admin_user(self, instance: User) -> None:
instance.is_superuser = True
instance.is_staff = True
instance.is_active = True
class ArticleFactory(DjangoModelFactory):
title = FACTORY.sentence()
slug = FACTORY.slug()
content = FACTORY.text()
headline = LazyAttribute(lambda o: o.content[:25])
category = LazyFunction(partial(random.choice, CATEGORIES))
image = FACTORY.png_file(storage=STORAGE)
pub_date = FACTORY.date(tzinfo=timezone.get_current_timezone())
safe_for_work = FACTORY.pybool()
minutes_to_read = FACTORY.pyint(min_value=1, max_value=10)
author = SubFactory(UserFactory)
class Meta:
model = Article
See the full example
here
Usage example
# Create one article
article = ArticleFactory()
# Create 5 articles
articles = ArticleFactory.create_batch(5)
# Create one article with authors username set to admin.
article = ArticleFactory(author__username="admin")
# Using trait
user = UserFactory(is_admin_user=True)
# Using trait in SubFactory
article = ArticleFactory(author__is_admin_user=True)
# Create a user. Created user will automatically have his password
# set to "test1234" and will be added to the group "Test group".
user = UserFactory()
# Create a user with custom password
user = UserFactory(
password=PreSave(set_password, password="another-pass"),
)
# Add a user to another group
user = UserFactory(
group=PostSave(add_to_group, name="Another group"),
)
# Or even add user to multiple groups at once
user = UserFactory(
group_1=PostSave(add_to_group, name="Another group"),
group_2=PostSave(add_to_group, name="Yet another group"),
)
Pydantic example
Filename: article/models.py
from datetime import date, datetime
from typing import Optional, Set
from fake import xor_transform
from pydantic import BaseModel, Field
class Group(BaseModel):
id: int
name: str
class Config:
allow_mutation = False
def __hash__(self):
return hash((self.id, self.name))
def __eq__(self, other):
if isinstance(other, Group):
return self.id == other.id and self.name == other.name
return False
class User(BaseModel):
id: int
username: str = Field(..., max_length=255)
first_name: str = Field(..., max_length=255)
last_name: str = Field(..., max_length=255)
email: str = Field(..., max_length=255)
date_joined: datetime = Field(default_factory=datetime.utcnow)
last_login: Optional[datetime] = None
password: Optional[str] = Field("", max_length=255)
is_superuser: bool = Field(default=False)
is_staff: bool = Field(default=False)
is_active: bool = Field(default=True)
groups: Set[Group] = Field(default_factory=set)
class Config:
extra = "allow" # For testing purposes only
def __str__(self):
return self.username
def set_password(self, password: str) -> None:
self.password = xor_transform(password)
class Article(BaseModel):
id: int
title: str = Field(..., max_length=255)
slug: str = Field(..., max_length=255, unique=True)
content: str
image: Optional[str] = None # Use str to represent the image path or URL
pub_date: date = Field(default_factory=date.today)
safe_for_work: bool = False
minutes_to_read: int = 5
author: User
class Config:
extra = "allow" # For testing purposes only
def __str__(self):
return self.title
See the full example
here
Filename: article/factories.py
from pathlib import Path
from fake import (
FACTORY,
FileSystemStorage,
ModelFactory,
PostSave,
PreSave,
SubFactory,
post_save,
pre_save,
trait,
)
from article.models import Article, Group, User
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
MEDIA_ROOT = BASE_DIR / "media"
STORAGE = FileSystemStorage(root_path=MEDIA_ROOT, rel_path="tmp")
class GroupFactory(ModelFactory):
id = FACTORY.pyint()
name = FACTORY.word()
class Meta:
model = Group
get_or_create = ("name",)
def set_password(user: User, password: str) -> None:
user.set_password(password)
def add_to_group(user: User, name: str) -> None:
group = GroupFactory(name=name)
user.groups.add(group)
class UserFactory(ModelFactory):
id = FACTORY.pyint()
username = FACTORY.username()
first_name = FACTORY.first_name()
last_name = FACTORY.last_name()
email = FACTORY.email()
date_joined = FACTORY.date_time()
last_login = FACTORY.date_time()
is_superuser = False
is_staff = False
is_active = FACTORY.pybool()
password = PreSave(set_password, password="test1234")
group = PostSave(add_to_group, name="TestGroup1234")
class Meta:
model = User
@trait
def is_admin_user(self, instance: User) -> None:
instance.is_superuser = True
instance.is_staff = True
instance.is_active = True
class ArticleFactory(ModelFactory):
id = FACTORY.pyint()
title = FACTORY.sentence()
slug = FACTORY.slug()
content = FACTORY.text()
image = FACTORY.png_file(storage=STORAGE)
pub_date = FACTORY.date()
safe_for_work = FACTORY.pybool()
minutes_to_read = FACTORY.pyint(min_value=1, max_value=10)
author = SubFactory(UserFactory)
class Meta:
model = Article
See the full example
here
Used just like in previous example.
TortoiseORM example
Filename: article/models.py
from datetime import date
from fake import xor_transform
from tortoise import fields
from tortoise.models import Model
class Group(Model):
"""Group model."""
id = fields.IntField(pk=True)
name = fields.CharField(max_length=255, unique=True)
class User(Model):
"""User model."""
id = fields.IntField(pk=True)
username = fields.CharField(max_length=255, unique=True)
first_name = fields.CharField(max_length=255)
last_name = fields.CharField(max_length=255)
email = fields.CharField(max_length=255)
password = fields.CharField(max_length=255, null=True, blank=True)
last_login = fields.DatetimeField(null=True, blank=True)
is_superuser = fields.BooleanField(default=False)
is_staff = fields.BooleanField(default=False)
is_active = fields.BooleanField(default=True)
date_joined = fields.DatetimeField(null=True, blank=True)
groups = fields.ManyToManyField("models.Group", on_delete=fields.CASCADE)
def set_password(self, password: str) -> None:
self.password = xor_transform(password)
class Article(Model):
"""Article model."""
id = fields.IntField(pk=True)
title = fields.CharField(max_length=255)
slug = fields.CharField(max_length=255, unique=True)
content = fields.TextField()
image = fields.TextField(null=True, blank=True)
pub_date = fields.DateField(default=date.today)
safe_for_work = fields.BooleanField(default=False)
minutes_to_read = fields.IntField(default=5)
author = fields.ForeignKeyField("models.User", on_delete=fields.CASCADE)
See the full example
here
Filename: article/factories.py
from pathlib import Path
from fake import (
FACTORY,
FileSystemStorage,
PostSave,
PreSave,
SubFactory,
TortoiseModelFactory,
post_save,
pre_save,
run_async_in_thread,
trait,
)
from article.models import Article, Group, User
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
MEDIA_ROOT = BASE_DIR / "media"
STORAGE = FileSystemStorage(root_path=MEDIA_ROOT, rel_path="tmp")
class GroupFactory(TortoiseModelFactory):
"""Group factory."""
name = FACTORY.word()
class Meta:
model = Group
get_or_create = ("name",)
def set_password(user: User, password: str) -> None:
user.set_password(password)
def add_to_group(user: User, name: str) -> None:
group = GroupFactory(name=name)
async def _add_to_group():
await user.groups.add(group)
await user.save()
run_async_in_thread(_add_to_group())
class UserFactory(TortoiseModelFactory):
"""User factory."""
username = FACTORY.username()
first_name = FACTORY.first_name()
last_name = FACTORY.last_name()
email = FACTORY.email()
date_joined = FACTORY.date_time()
last_login = FACTORY.date_time()
is_superuser = False
is_staff = False
is_active = FACTORY.pybool()
password = PreSave(set_password, password="test1234")
group = PostSave(add_to_group, name="TestGroup1234")
class Meta:
model = User
get_or_create = ("username",)
@trait
def is_admin_user(self, instance: User) -> None:
instance.is_superuser = True
instance.is_staff = True
instance.is_active = True
class ArticleFactory(TortoiseModelFactory):
"""Article factory."""
title = FACTORY.sentence()
slug = FACTORY.slug()
content = FACTORY.text()
image = FACTORY.png_file(storage=STORAGE)
pub_date = FACTORY.date()
safe_for_work = FACTORY.pybool()
minutes_to_read = FACTORY.pyint(min_value=1, max_value=10)
author = SubFactory(UserFactory)
class Meta:
model = Article
See the full example
here
Used just like in previous example.
Dataclasses example
Filename: article/models.py
from dataclasses import dataclass, field
from datetime import date, datetime
from typing import Optional, Set
from fake import xor_transform
@dataclass(frozen=True)
class Group:
id: int
name: str
@dataclass
class User:
id: int
username: str
first_name: str
last_name: str
email: str
date_joined: datetime = field(default_factory=datetime.utcnow)
last_login: Optional[datetime] = None
password: Optional[str] = None
is_superuser: bool = False
is_staff: bool = False
is_active: bool = True
groups: Set[Group] = field(default_factory=set)
def __str__(self):
return self.username
def set_password(self, password: str) -> None:
self.password = xor_transform(password)
@dataclass
class Article:
id: int
title: str
slug: str
content: str
author: User
image: Optional[str] = None # Use str to represent the image path or URL
pub_date: date = field(default_factory=date.today)
safe_for_work: bool = False
minutes_to_read: int = 5
def __str__(self):
return self.title
See the full example
here
Filename: article/factories.py
from pathlib import Path
from fake import (
FACTORY,
FileSystemStorage,
ModelFactory,
PostSave,
PreSave,
SubFactory,
post_save,
pre_save,
trait,
)
from article.models import Article, Group, User
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
MEDIA_ROOT = BASE_DIR / "media"
STORAGE = FileSystemStorage(root_path=MEDIA_ROOT, rel_path="tmp")
class GroupFactory(ModelFactory):
id = FACTORY.pyint()
name = FACTORY.word()
class Meta:
model = Group
get_or_create = ("name",)
def set_password(user: User, password: str) -> None:
user.set_password(password)
def add_to_group(user: User, name: str) -> None:
group = GroupFactory(name=name)
user.groups.add(group)
class UserFactory(ModelFactory):
id = FACTORY.pyint()
username = FACTORY.username()
first_name = FACTORY.first_name()
last_name = FACTORY.last_name()
email = FACTORY.email()
date_joined = FACTORY.date_time()
last_login = FACTORY.date_time()
is_superuser = False
is_staff = False
is_active = FACTORY.pybool()
password = PreSave(set_password, password="test1234")
group = PostSave(add_to_group, name="TestGroup1234")
class Meta:
model = User
@trait
def is_admin_user(self, instance: User) -> None:
instance.is_superuser = True
instance.is_staff = True
instance.is_active = True
class ArticleFactory(ModelFactory):
id = FACTORY.pyint()
title = FACTORY.sentence()
slug = FACTORY.slug()
content = FACTORY.text()
image = FACTORY.png_file(storage=STORAGE)
pub_date = FACTORY.date()
safe_for_work = FACTORY.pybool()
minutes_to_read = FACTORY.pyint(min_value=1, max_value=10)
author = SubFactory(UserFactory)
class Meta:
model = Article
See the full example
here
Used just like in previous example.
SQLAlchemy example
Filename: config.py
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
# SQLAlchemy
DATABASE_URL = "sqlite:///test_database.db"
ENGINE = create_engine(DATABASE_URL)
SESSION = scoped_session(sessionmaker(bind=ENGINE))
See the full example
here
Filename: article/models.py
from datetime import datetime
from fake import xor_transform
from sqlalchemy import (
Boolean,
Column,
DateTime,
ForeignKey,
Integer,
String,
Table,
Text,
)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
Base = declarative_base()
# Association table for the many-to-many relationship
user_group_association = Table(
"user_group",
Base.metadata,
Column("user_id", Integer, ForeignKey("users.id")),
Column("group_id", Integer, ForeignKey("groups.id")),
)
class Group(Base):
"""Group model."""
__tablename__ = "groups"
id = Column(Integer, primary_key=True)
name = Column(String(255), unique=True)
class User(Base):
"""User model."""
__tablename__ = "users"
id = Column(Integer, primary_key=True)
username = Column(String(255), unique=True)
first_name = Column(String(255))
last_name = Column(String(255))
email = Column(String(255))
date_joined = Column(DateTime, default=datetime.utcnow)
last_login = Column(DateTime, nullable=True)
password = Column(String(255), nullable=True)
is_superuser = Column(Boolean, default=False)
is_staff = Column(Boolean, default=False)
is_active = Column(Boolean, default=True)
# Many-to-many relationship
groups = relationship(
"Group", secondary=user_group_association, backref="users"
)
# One-to-many relationship
articles = relationship("Article", back_populates="author")
def set_password(self, password: str) -> None:
self.password = xor_transform(password)
class Article(Base):
"""Article model."""
__tablename__ = "articles"
id = Column(Integer, primary_key=True)
title = Column(String(255))
slug = Column(String(255), unique=True)
content = Column(Text)
image = Column(Text, nullable=True)
pub_date = Column(DateTime, default=datetime.utcnow)
safe_for_work = Column(Boolean, default=False)
minutes_to_read = Column(Integer, default=5)
author_id = Column(Integer, ForeignKey("users.id"))
author = relationship("User", back_populates="articles")
See the full example
here
Filename: article/factories.py
from pathlib import Path
from fake import (
FACTORY,
FileSystemStorage,
PostSave,
PreSave,
SQLAlchemyModelFactory,
SubFactory,
post_save,
pre_save,
trait,
)
from article.models import Article, Group, User
from config import SESSION
# Storage config. Build paths inside the project like this: BASE_DIR / 'subdir'
BASE_DIR = Path(__file__).resolve().parent.parent
MEDIA_ROOT = BASE_DIR / "media"
STORAGE = FileSystemStorage(root_path=MEDIA_ROOT, rel_path="tmp")
def get_session():
return SESSION()
class GroupFactory(SQLAlchemyModelFactory):
"""User factory."""
name = FACTORY.word()
class Meta:
model = Group
get_or_create = ("name",)
class MetaSQLAlchemy:
get_session = get_session
def set_password(user: User, password: str) -> None:
user.set_password(password)
def add_to_group(user: User, name: str) -> None:
session = get_session()
# Check if the group already exists
group = session.query(Group).filter_by(name=name).first()
# If the group doesn't exist, create a new one
if not group:
group = Group(name=name)
session.add(group)
session.commit() # Commit to assign an ID to the new group
# Add the group to the user's groups using append
if group not in user.groups:
user.groups.append(group)
session.commit() # Commit the changes
class UserFactory(SQLAlchemyModelFactory):
"""User factory."""
username = FACTORY.username()
first_name = FACTORY.first_name()
last_name = FACTORY.last_name()
email = FACTORY.email()
date_joined = FACTORY.date_time()
last_login = FACTORY.date_time()
is_superuser = False
is_staff = False
is_active = FACTORY.pybool()
password = PreSave(set_password, password="test1234")
group = PostSave(add_to_group, name="TestGroup1234")
class Meta:
model = User
get_or_create = ("username",)
class MetaSQLAlchemy:
get_session = get_session
@trait
def is_admin_user(self, instance: User) -> None:
instance.is_superuser = True
instance.is_staff = True
instance.is_active = True
class ArticleFactory(SQLAlchemyModelFactory):
"""Article factory."""
title = FACTORY.sentence()
slug = FACTORY.slug()
content = FACTORY.text()
image = FACTORY.png_file(storage=STORAGE)
pub_date = FACTORY.date()
safe_for_work = FACTORY.pybool()
minutes_to_read = FACTORY.pyint(min_value=1, max_value=10)
author = SubFactory(UserFactory)
class Meta:
model = Article
class MetaSQLAlchemy:
get_session = get_session
See the full example
here
Used just like in previous example.