#34165: migrate management command does not respect database parameter for all 
DB
calls
----------------------------+--------------------------------------
     Reporter:  Vasanth     |                    Owner:  nobody
         Type:  Bug         |                   Status:  new
    Component:  Migrations  |                  Version:  4.1
     Severity:  Normal      |               Resolution:
     Keywords:              |             Triage Stage:  Unreviewed
    Has patch:  0           |      Needs documentation:  0
  Needs tests:  0           |  Patch needs improvement:  0
Easy pickings:  0           |                    UI/UX:  0
----------------------------+--------------------------------------
Description changed by Vasanth:

Old description:

> When invoking migrate with a database parameter, the migration runs
> successfully. However, there seems to be a DB read request that runs
> after the migration. This call does not respect the db param and invokes
> the db router .
>
> When naming the db as a parameter, all DB calls in the context of the
> migrate command are expected to use the database specified.
>
> I came across this as I am currently using a thread-local variable to get
> the active DB with a custom DB router for a multi-tenant service .
>
> **''Minimal example''**
>
> Setup the custom middleware and custom DB Router as show below. Then run
> any DB migration. Ideally none of this code must be called as the DB was
> specified during management command.
>

> {{{
>
> from threading import local
> from django.conf import settings
>
> local_state = local()
>

> class InvalidTenantException(Exception):
>     pass
>

> class TenantSubdomainMiddleware:
>     def __init__(self, get_response):
>         self.get_response = get_response
>
>     def __call__(self, request):
>         ## Get Subdomain
>         host = request.get_host().split(":")[0]
>         local_state.subdomain = (
>             # We assume single level of subdomain : app.service.com
>             # HOST_IP : used to for local dev.
>             host if host in settings.HOST_IP else host.split(".")[0]
>         )
>         response = self.get_response(request)
>         return response
>

> class TenantDatabaseRouter:
>     def _default_db(self):
>         subdomain = getattr(local_state, "subdomain", None)
>         if subdomain is not None and subdomain in settings.TENANT_MAP:
>             db_name = settings.TENANT_MAP[local_state.subdomain]
>             return db_name
>         else:
>             raise InvalidTenantException()
>
>     def db_for_read(self, model, **hints):
>         return self._default_db()
>
>     def db_for_write(self, model, **hints):
>         return self._default_db()
>
>     def allow_relation(self, obj1, obj2, **hints):
>         return True
>
>     def allow_migrate(self, db, app_label, model_name=None, **hints):
>         return True
>
> ## settings.py
>

>
> MIDDLEWARE = [
>     "utils.tenant_db_router.TenantSubdomainMiddleware",
>     "django.middleware.security.SecurityMiddleware",
>     ...
> ]
>
> TENANT_MAP = {"localhost":"default", "tenant_1":"default"}
> DATABASE_ROUTERS = ["utils.tenant_db_router.TenantDatabaseRouter"]
> }}}

New description:

 When invoking migrate with a database parameter, the migration runs
 successfully. However, there seems to be a DB read request that runs after
 the migration. This call does not respect the db param and invokes the db
 router .

 When naming the db as a parameter, all DB calls in the context of the
 migrate command are expected to use the database specified.

 I came across this as I am currently using a thread-local variable to get
 the active DB with a custom DB router for a multi-tenant service .

 **''Minimal example''**

 Setup the custom middleware and custom DB Router as show below. Then run
 any DB migration. We see that "read {}" is being printed before the
 exception message.

 Ideally none of this code must be called as the DB was specified during
 management command.


 {{{

 from threading import local
 from django.conf import settings

 local_state = local()


 class InvalidTenantException(Exception):
     pass


 class TenantSubdomainMiddleware:
     def __init__(self, get_response):
         self.get_response = get_response

     def __call__(self, request):
         ## Get Subdomain
         host = request.get_host().split(":")[0]
         local_state.subdomain = (
             # We assume single level of subdomain : app.service.com
             # HOST_IP : used to for local dev.
             host if host in settings.HOST_IP else host.split(".")[0]
         )
         response = self.get_response(request)
         return response


 class TenantDatabaseRouter:
     def _default_db(self):
         subdomain = getattr(local_state, "subdomain", None)
         if subdomain is not None and subdomain in settings.TENANT_MAP:
             db_name = settings.TENANT_MAP[local_state.subdomain]
             return db_name
         else:
             raise InvalidTenantException()

     def db_for_read(self, model, **hints):
         print("read", hints)
         return self._default_db()

     def db_for_write(self, model, **hints):
         print("write", hints)
         return self._default_db()

     def allow_relation(self, obj1, obj2, **hints):
         return None

     def allow_migrate(self, db, app_label, model_name=None, **hints):
         return None

 ## settings.py



 MIDDLEWARE = [
     "utils.tenant_db_router.TenantSubdomainMiddleware",
     "django.middleware.security.SecurityMiddleware",
     ...
 ]

 TENANT_MAP = {"localhost":"default", "tenant_1":"default"}
 DATABASE_ROUTERS = ["utils.tenant_db_router.TenantDatabaseRouter"]
 }}}

--

-- 
Ticket URL: <https://code.djangoproject.com/ticket/34165#comment:2>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/01070184800342c7-34ad015a-93de-490b-adce-43fdb4efc0dd-000000%40eu-central-1.amazonses.com.

Reply via email to