diff --git a/django_pgschemas/urlresolvers.py b/django_pgschemas/urlresolvers.py index 5079bc6..8b98173 100644 --- a/django_pgschemas/urlresolvers.py +++ b/django_pgschemas/urlresolvers.py @@ -103,7 +103,7 @@ def __getattr__(self, attr): def get_urlconf_from_schema(schema): """ Returns the proper URLConf depending on the schema. - The schema must come with domain_url and folder members set. + The schema must come with ``domain_url`` and ``folder`` members set. """ assert isinstance(schema, SchemaDescriptor) diff --git a/docs/advanced.rst b/docs/advanced.rst index 2550972..07bc78d 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -1,6 +1,63 @@ Advanced configuration ====================== +Fast dynamic tenant creation +---------------------------- + +Every time a instance of ``settings.TENANTS["public"]["TENANT_MODEL"]`` is +created, by default, the corresponding schema is created and synchronized +automatically. Depending on the number of migrations you already have in place, +or the amount of time these could take, or whether you need to pre-populate the +newly created schema with fixtures, this process could take a considerable +amount of time. + +If you need a faster creation of dynamic schemas, you can do so by provisioning +a "reference" schema that can cloned into new schemas. + +.. code-block:: python + + TENANTS = { + # ... + "default": { + # ... + "CLONE_REFERENCE": "sample", + }, + } + +Once you have this in your settings, you need to prepare your reference schema +with everything a newly created dynamic schema will need. The first step is +actually creating and synchronizing the reference schema. After that, you +can run any command on it, or edit its tables via ``shell``. + +.. code-block:: bash + + python manage.py createrefschema + python runschema loaddata tenant_app.products -s sample + python runschema shell -s sample + +The ``runschema`` command is explained in :ref:`Management commands`. + +You don't need any extra step. As soon as a reference schema is configured, +next time you create an instance of the tenant model, it will clone the +reference schema instead of actually creating and synchronizing the schema. + +Most importantly, by default, migrations will include the reference schema, so +that it is kept up to date for future tenant creation. + +.. attention:: + + The database function for cloning schemas requires PostgreSQL 10 or higher, + due to a change in the way sequence information is stored. + + +.. tip:: + + The reference schema will get apps from + ``settings.TENANTS["default"]["APPS"]`` and may look like any other dynamic + tenant, but it is considered a *static* tenant instead, as there is no + corresponding database entry for it. It's a special case of a static + tenant, and it cannot be routed. + Management commands ------------------- @@ -10,11 +67,13 @@ the public schema. In order to work around this, we provide a ``runschema`` command that accepts any other command to be run on one or multiple schemas. A concise synopsis of the ``runschema`` command is as follows:: - usage: manage.py runschema [--noinput] [-s SCHEMAS [SCHEMAS ...]] - [-as] [-ss] [-ds] [-ts] - [--executor {sequential,parallel}] - [--no-create-schemas] - command_name + usage: manage.py runschema [-s SCHEMAS [SCHEMAS ...]] + [-x EXCLUDED_SCHEMAS [EXCLUDED_SCHEMAS ...]] + [-as] [-ss] [-ds] [-ts] + [--executor {sequential,parallel}] + [--no-create-schemas] + [--noinput] + command_name Wrapper around django commands for use with an individual schema @@ -27,7 +86,8 @@ concise synopsis of the ``runschema`` command is as follows:: Tells Django to NOT prompt the user for input of any kind. - -s SCHEMAS [SCHEMAS ...], --schema SCHEMAS [SCHEMAS ...] + -s SCHEMAS [SCHEMAS ...], + --schema SCHEMAS [SCHEMAS ...] Schema(s) to execute the current command -as, --include-all-schemas Include all schemas when executing the current command @@ -133,40 +193,3 @@ In order to generate tenant aware cache keys, you can use "KEY_FUNCTION": "django_pgschemas.cache.make_key", } } - -Channels (websockets) ---------------------- - -We provide a tenant aware protocol router for using with ``channels``. You can -use it as follows: - -.. code-block:: python - - # routing.py - - from django_pgschemas.contrib.channels import TenantProtocolRouter - - application = TenantProtocolRouter() - - # settings.py - - ASGI_APPLICATION = "routing.application" - -It requires that you also route the websockets requests, at least for the -dynamic tenants. If you don't route websocket requests for static tenants, the -dynamic route will be used: - -.. code-block:: python - - TENANTS = { - # ... - "default": { - # ... - "URLCONF": "tenant_app.urls", - "WS_URLCONF": "tenant_app.ws_urls", - } - } - -You still need to name your channel groups appropriately, taking the -current tenant into account if you want to keep your groups tenant-specific. -You will get the current tenant in ``scope["tenant"]``. diff --git a/docs/basic.rst b/docs/basic.rst index c2c83db..6cabd99 100644 --- a/docs/basic.rst +++ b/docs/basic.rst @@ -3,9 +3,9 @@ Installation This app requires: -* python (3.6+) -* django (2.0+) -* psycopg2 (2.7+) +* Python (3.6+) +* Django (2.0+) +* Psycopg2 (2.7+) You can install ``django-pgschemas`` via ``pip``, ``poetry`` or any other installer. @@ -13,6 +13,7 @@ installer. .. code-block:: bash pip install django-pgschemas + # or poetry add django-pgschemas Basic Configuration @@ -185,60 +186,3 @@ available too. Also, at this point, any request to ``blog.mydomain.com`` or 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. - -Fast dynamic tenant creation ----------------------------- - -Every time a instance of ``settings.TENANTS["public"]["TENANT_MODEL"]`` is -created, by default, the corresponding schema is created and synchronized -automatically. Depending on the number of migrations you already have in place, -or the amount of time these could take, or whether you need to pre-populate the -newly created schema with fixtures, this process could take a considerable -amount of time. - -If you need a faster creation of dynamic schemas, you can do so by provisioning -a "reference" schema that can cloned into new schemas. - -.. code-block:: python - - TENANTS = { - # ... - "default": { - # ... - "CLONE_REFERENCE": "sample", - }, - } - -Once you have this in your settings, you need to prepare your reference schema -with everything a newly created dynamic schema will need. The first step is -actually creating and synchronizing the reference schema. After that, you -can run any command on it, or edit its tables via ``shell``. - -.. code-block:: bash - - python manage.py createrefschema - python runschema loaddata tenant_app.products -s sample - python runschema shell -s sample - -The ``runschema`` command is explained in :ref:`Management commands`. - -You don't need any extra step. As soon as a reference schema is configured, -next time you create an instance of the tenant model, it will clone the -reference schema instead of actually creating and synchronizing the schema. - -Most importantly, by default, migrations will include the reference schema, so -that it is kept up to date for future tenant creation. - -.. attention:: - - The database function for cloning schemas requires PostgreSQL 10 or higher, - due to a change in the way sequence information is stored. - - -.. tip:: - - The reference schema will get apps from - ``settings.TENANTS["default"]["APPS"]`` and may look like any other dynamic - tenant, but it is considered a *static* tenant instead, as there is no - corresponding database entry for it. It's a special case of a static - tenant, and it cannot be routed. diff --git a/docs/contrib.rst b/docs/contrib.rst new file mode 100644 index 0000000..5840ecd --- /dev/null +++ b/docs/contrib.rst @@ -0,0 +1,53 @@ +Contributions +============= + +All contributions and third party integrations live inside +``django_pgschemas.contrib``. + +If you want to implement an integration with other Django packages, please +submit a pull request containing: + +* The code for your integration +* The tests for your integration +* The docs for your integration in this section of the documentation + +We're striving to maintain/increase our code coverage, but please, make sure your +integration is properly tested. Proper tests will always beat meaningless 100% +coverage. + +Channels (websockets) +--------------------- + +We provide a tenant aware protocol router for using with ``channels``. You can +use it as follows: + +.. code-block:: python + + # routing.py + + from django_pgschemas.contrib.channels import TenantProtocolRouter + + application = TenantProtocolRouter() + + # settings.py + + ASGI_APPLICATION = "routing.application" + +It requires that you also route the websockets requests, at least for the +dynamic tenants. If you don't route websocket requests for static tenants, the +dynamic route will be used: + +.. code-block:: python + + TENANTS = { + # ... + "default": { + # ... + "URLCONF": "tenant_app.urls", + "WS_URLCONF": "tenant_app.ws_urls", + } + } + +You still need to name your channel groups appropriately, taking the +current tenant into account if you want to keep your groups tenant-specific. +You will get the current tenant in ``scope["tenant"]``. diff --git a/docs/index.rst b/docs/index.rst index c7e2bc4..33ca6b6 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -45,6 +45,7 @@ django-pgschemas overview basic advanced + contrib troubleshooting reference credits