Relational Data Generation

New in version 20.0.0.

Schema is ideal for independent documents (see Structured Data Generation). When you need related collections — users and posts, orders and line items, etc. — use SchemaBuilder.

SchemaBuilder lets you:

  • Define multiple named schemas that share one locale and seed

  • Link records with foreign keys (sb.ref(users).id)

  • Embed a whole related record (sb.ref(users))

  • Nest child documents inline (addresses(count=2))

  • Generate everything in one create() call with automatic dependency resolution (generation order does not depend on kwarg order)

Quick Start

Create users and posts where each post references a user:

from mimesis import SchemaBuilder
from mimesis.locales import Locale

sb = SchemaBuilder(Locale.EN, seed=0xFF)

users = sb.schema("users", {
    "id": sb.f("increment", accumulator="user"),
    "username": sb.f("username"),
    "email": sb.f("email"),
})

posts = sb.schema("posts", {
    "id": sb.f("increment", accumulator="post"),
    "title": sb.f("sentence"),
    "user_id": sb.ref(users).id,  # Foreign key to users
})

# Order of kwargs does not matter — dependencies are resolved automatically
data = sb.create(posts=20, users=5)

print(len(data["users"]))  # 5
print(len(data["posts"]))  # 20
print(data["posts"][0]["user_id"] in {u["id"] for u in data["users"]})
# True

Defining Fields with f()

f() creates a lazy field that is evaluated when data is generated. The first argument is a provider method name (the same names you pass to Field). Extra keyword arguments are forwarded to that method:

sb.f("username")
sb.f("email", domains=["example.com"])
sb.f("increment", accumulator="user")
sb.f("timestamp", fmt=TimestampFormat.POSIX)

You can also pick from fixed or weighted lists without a provider method:

sb.choice(["active", "archived", "draft"])
sb.weighted_choice(["common", "rare"], [0.9, 0.1])

Key Functions with f()

The optional key parameter accepts the same callables as Field, including everything in mimesis.keys (see Key Functions and Transformations):

from mimesis import SchemaBuilder
from mimesis.keys import maybe, pipe, slugify, prefix
from mimesis.locales import Locale

sb = SchemaBuilder(Locale.EN, seed=0xFF)

users = sb.schema("users", {
    "id": sb.f("increment", accumulator="user"),
    "username": sb.f(
        "username",
        key=pipe(slugify, prefix("user-")),
    ),
    "email": sb.f("email", key=maybe(None, probability=0.2)),
    "display_name": sb.f("full_name", key=str.upper),
})

Foreign Keys and Whole Records

sb.schema() returns a SchemaRef. Pass that reference to ref() to link schemas:

  • Foreign keysb.ref(users).id picks a random value of the id field from already generated users rows

  • Whole recordsb.ref(users) embeds a random full user dict

users = sb.schema("users", {
    "id": sb.f("increment", accumulator="user"),
    "username": sb.f("username"),
})

posts = sb.schema("posts", {
    "id": sb.f("increment", accumulator="post"),
    "user_id": sb.ref(users).id,   # scalar FK
    "author": sb.ref(users),       # embedded user document
    "title": sb.f("sentence"),
})

data = sb.create(users=3, posts=10)

Note

Every schema referenced with sb.ref() must be included in the same create() call (for example users=3), so parent rows exist before foreign keys are resolved.

Nested Documents

Call a SchemaRef with count=N to embed N generated child documents inside a parent field. Nested schemas used only this way do not need to be passed to create():

addresses = sb.schema("addresses", {
    "city": sb.f("city"),
    "street": sb.f("street_name"),
    "zip_code": sb.f("zip_code"),
})

customers = sb.schema("customers", {
    "id": sb.f("increment", accumulator="customer"),
    "name": sb.f("full_name"),
    "addresses": addresses(count=2),
})

data = sb.create(customers=10)

Dependency Resolution

create() topologically sorts schemas from foreign-key references. You can pass counts in any order:

data = sb.create(api_keys=10, projects=5, users=3)
# users → projects → api_keys

Lifecycle Helpers

SchemaBuilder provides helpers for controlling state between runs:

  • reseed() — change the random seed

  • clear() — drop generated data, keep schema definitions

  • reset() — clear schemas and generated data

data_a = sb.create(users=5)
sb.clear()
data_b = sb.create(users=5)  # same schemas, fresh data

sb.reseed(0xAB)
data_c = sb.create(users=5)

sb.reset()  # definitions are gone; call sb.schema() again

Multi-level Example

A small graph with users, projects, and API keys:

from mimesis import SchemaBuilder
from mimesis.enums import TimestampFormat
from mimesis.locales import Locale

sb = SchemaBuilder(Locale.EN, seed=0xFF)

users = sb.schema("users", {
    "id": sb.f("increment", accumulator="user"),
    "username": sb.f("username"),
    "email": sb.f("email"),
    "created_at": sb.f("timestamp", fmt=TimestampFormat.POSIX),
})

projects = sb.schema("projects", {
    "id": sb.f("increment", accumulator="project"),
    "name": sb.f("word"),
    "version": sb.f("version"),
    "owner_id": sb.ref(users).id,
    "status": sb.choice(["active", "archived", "draft"]),
})

api_keys = sb.schema("api_keys", {
    "id": sb.f("uuid"),
    "key": sb.f("token_hex"),
    "project_id": sb.ref(projects).id,
    "created_at": sb.f("timestamp", fmt=TimestampFormat.POSIX),
})

data = sb.create(
    api_keys=10,
    projects=5,
    users=3,
)

This generates:

  • 3 users

  • 5 projects (each with a valid owner_id referencing a user)

  • 10 API keys (each with a valid project_id referencing a project)

See also: SchemaBuilder API reference.