# Installation

This app requires:

- Python (3.8.1+)

- Django (4.0+)

- Any version of psycopg

You can install `django-pgschemas` via `pip` or any other installer.

    pip install django-pgschemas

# Basic Configuration

Use `django_pgschemas.postgresql_backend` as your database engine. This enables the API for setting PostgreSQL search path

    DATABASES = {
        "default": {
            "ENGINE": "django_pgschemas.postgresql_backend",
            # ...
        }
    }

Add the middleware `django_pgschemas.middleware.TenantMiddleware` to the top of `MIDDLEWARE`, so that each request can be set to use the correct schema.

    MIDDLEWARE = (
        "django_pgschemas.middleware.TenantMiddleware",
        # ...
    )

Add `django_pgschemas.routers.SyncRouter` to your `DATABASE_ROUTERS`, so that the correct apps can be synced, depending on the target schema.

    DATABASE_ROUTERS = (
        "django_pgschemas.routers.SyncRouter",
        # ...
    )

Add the minimal tenant configuration.

    TENANTS = {
        "public": {
            "APPS": [
                "django.contrib.contenttypes",
                "django.contrib.staticfiles",
                # ...
                "django_pgschemas",
                "shared_app",
                # ...
            ],
        },
        # ...
        "default": {
            "TENANT_MODEL": "shared_app.Client",
            "DOMAIN_MODEL": "shared_app.Domain",
            "APPS": [
                "django.contrib.auth",
                "django.contrib.sessions",
                # ...
                "tenant_app",
                # ...
            ],
            "URLCONF": "tenant_app.urls",
        }
    }

Each entry in the `TENANTS` dictionary represents a static tenant, except for `default`, which controls the settings for all dynamic tenants. Notice how each tenant has the relevant `APPS` that will be synced in the corresponding schema.

Tip

`public` is always treated as shared schema and cannot be routed directly. Every other tenant will get its search path set to its schema first, then the public schema.

For Django to function properly, `INSTALLED_APPS` and `ROOT_URLCONF` settings must be defined. Just make them get their information from the `TENANTS` dictionary, for the sake of consistency.

    INSTALLED_APPS = []
    for schema in TENANTS:
        INSTALLED_APPS += [app for app in TENANTS[schema]["APPS"] if app not in INSTALLED_APPS]

    ROOT_URLCONF = TENANTS["default"]["URLCONF"]

## Creating tenants

More static tenants can be added and routed.

    TENANTS = {
        # ...
        "www": {
            "APPS": [
                "django.contrib.auth",
                "django.contrib.sessions",
                # ...
                "main_app",
            ],
            "DOMAINS": ["mydomain.com"],
            "URLCONF": "main_app.urls",
        },
        "blog": {
            "APPS": [
                "django.contrib.auth",
                "django.contrib.sessions",
                # ...
                "blog_app",
            ],
            "DOMAINS": ["blog.mydomain.com", "help.mydomain.com"],
            "URLCONF": "blog_app.urls",
        },
        # ...
    }

Dynamic tenants need to be created through instances of `TENANTS["default"]["TENANT_MODEL"]` and routed through instances of `TENANTS["default"]["DOMAIN_MODEL"]`.

    # shared_app/models.py

    from django.db import models
    from django_pgschemas.models import TenantMixin, DomainMixin

    class Client(TenantMixin):
        name = models.CharField(max_length=100)
        paid_until =  models.DateField(blank=True, null=True)
        on_trial = models.BooleanField(default=True)
        created_on = models.DateField(auto_now_add=True)

    class Domain(DomainMixin):
        pass

## Synchronizing tenants

As a first step, you must always synchronize the public schema in order to get the tenant and domain models created. You can then synchronize the rest of the schemas.

    python manage.py migrate -s public
    python manage.py migrate

Now you are ready to create your first dynamic tenant. In the example, the tenant is created through a `python manage.py shell` session.

    >>> from shared_app.models import Client, Domain
    >>> client1 = Client.objects.create(schema_name="client1")
    >>> Domain.objects.create(domain="client1.mydomain.com", tenant=client1, is_primary=True)
    >>> Domain.objects.create(domain="clients.mydomain.com", folder="client1", tenant=client1)

Now any request made to `client1.mydomain.com` or `clients.mydomain.com/client1/` will automatically set PostgreSQL’s search path to `client1` and `public`, making shared apps available too. Also, at this point, any request to `blog.mydomain.com` or `help.mydomain.com` will set search path to `blog` and `public`.

This means that any call to the methods `filter`, `get`, `save`, `delete` or any other function involving a database connection will be done at the correct schema, be it static or dynamic.