mik3y / django-db-multitenant

A simple multi-tenancy solution for Django apps.
Other
155 stars 34 forks source link

dbname not set at cursor create time #16

Open Neamat opened 7 years ago

Neamat commented 7 years ago

I followed all the steps that you mentioned in your docs but I always get the same error "dbname not set at cursor create time" when I run the project. I even sent a static database name inside the mapper just to try it and it returned the same error. It looks like it cannot initiate with the default database that I already added inside the settings. Any suggestions?

stephane commented 7 years ago

Are you sure your requests reach the middleware? https://github.com/stephane/django-db-multitenant/blob/stephane/fork/db_multitenant/middleware.py#L40

Neamat commented 7 years ago

Thank you for your reply. The following is the error trace:

Unhandled exception in thread started by <function wrapper at 0x0000000003F77AC8>
Traceback (most recent call last):
  File "C:\Python27\lib\site-packages\django\utils\autoreload.py", line 226, in wrapper
    fn(*args, **kwargs)
  File "C:\Python27\lib\site-packages\django\core\management\commands\runserver.py", line 116, in inner_run
    self.check(display_num_errors=True)
  File "C:\Python27\lib\site-packages\django\core\management\base.py", line 426, in check
    include_deployment_checks=include_deployment_checks,
  File "C:\Python27\lib\site-packages\django\core\checks\registry.py", line 75, in run_checks
    new_errors = check(app_configs=app_configs)
  File "C:\Python27\lib\site-packages\django\core\checks\model_checks.py", line 28, in check_all_models
    errors.extend(model.check(**kwargs))
  File "C:\Python27\lib\site-packages\django\db\models\base.py", line 1170, in check
    errors.extend(cls._check_fields(**kwargs))
  File "C:\Python27\lib\site-packages\django\db\models\base.py", line 1247, in _check_fields
    errors.extend(field.check(**kwargs))
  File "C:\Python27\lib\site-packages\django\db\models\fields\__init__.py", line 925, in check
    errors = super(AutoField, self).check(**kwargs)
  File "C:\Python27\lib\site-packages\django\db\models\fields\__init__.py", line 208, in check
    errors.extend(self._check_backend_specific_checks(**kwargs))
  File "C:\Python27\lib\site-packages\django\db\models\fields\__init__.py", line 317, in _check_backend_specific_checks
    return connections[db].validation.check_field(self, **kwargs)
  File "C:\Python27\lib\site-packages\django\db\backends\mysql\validation.py", line 18, in check_field
    field_type = field.db_type(connection)
  File "C:\Python27\lib\site-packages\django\db\models\fields\__init__.py", line 625, in db_type
    return connection.data_types[self.get_internal_type()] % data
  File "C:\Python27\lib\site-packages\django\db\__init__.py", line 36, in __getattr__
    return getattr(connections[DEFAULT_DB_ALIAS], item)
  File "C:\Python27\lib\site-packages\django\utils\functional.py", line 33, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "C:\Python27\lib\site-packages\django\db\backends\mysql\base.py", line 184, in data_types
    if self.features.supports_microsecond_precision:
  File "C:\Python27\lib\site-packages\django\utils\functional.py", line 33, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "C:\Python27\lib\site-packages\django\db\backends\mysql\features.py", line 53, in supports_microsecond_precision
    return self.connection.mysql_version >= (5, 6, 4) and Database.version_info >= (1, 2, 5)
  File "C:\Python27\lib\site-packages\django\utils\functional.py", line 33, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "C:\Python27\lib\site-packages\django\db\backends\mysql\base.py", line 359, in mysql_version
    with self.temporary_connection():
  File "C:\Python27\lib\contextlib.py", line 17, in __enter__
    return self.gen.next()
  File "C:\Python27\lib\site-packages\django\db\backends\base\base.py", line 564, in temporary_connection
    cursor = self.cursor()
  File "C:\Python27\lib\site-packages\django\db\backends\base\base.py", line 231, in cursor
    cursor = self.make_debug_cursor(self._cursor())
  File "C:\Python27\lib\site-packages\db_multitenant\db\backends\mysql\base.py", line 56, in _cursor
    cursor = super(DatabaseWrapper, self)._cursor()
  File "C:\Python27\lib\site-packages\django\db\backends\base\base.py", line 204, in _cursor
    self.ensure_connection()
  File "C:\Python27\lib\site-packages\django\db\backends\base\base.py", line 199, in ensure_connection
    self.connect()
  File "C:\Python27\lib\site-packages\django\db\backends\base\base.py", line 173, in connect
    self.init_connection_state()
  File "C:\Python27\lib\site-packages\django\db\backends\mysql\base.py", line 270, in init_connection_state
    with self.cursor() as cursor:
  File "C:\Python27\lib\site-packages\django\db\backends\base\base.py", line 231, in cursor
    cursor = self.make_debug_cursor(self._cursor())
  File "C:\Python27\lib\site-packages\db_multitenant\db\backends\mysql\base.py", line 65, in _cursor
    raise ImproperlyConfigured('dbname not set at cursor create time')
django.core.exceptions.ImproperlyConfigured: dbname not set at cursor create time

I guess it doesn't go to the middleware but I added the middleware as the first one in the middleware section. The following is the middleware section:


MIDDLEWARE_CLASSES = [
    'db_multitenant.middleware.MultiTenantMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

The following also is the database:

DATABASES = {

    'default': {
        #'ENGINE': 'django.db.backends.mysql',
        'ENGINE': 'db_multitenant.db.backends.mysql', 
        'NAME': 'tenant',
        'USER': 'tenantuser',
        'PASSWORD': 'tenantpwd',
        'HOST': 'localhost',
        'PORT': '3306',
    }
}

Please advise what would be the issue.. Thanks alot @stephane

stephane commented 7 years ago

How do you access your application (hostname)?

Neamat commented 7 years ago

Thank you for replying Stephane and please help me because I can't access it yet. I just try to run "runserver" just to open the server and I get that error so I can't even make it work to start accessing it from the browser. I also added a fixed name for the database inside the mapper so that I remove the possibility that the mapper would be the issue. But apparently it doesn't even reach the mapper. What do you think I should do to know the problem? My initial thought is that it can't even connect to the default database however I already have other projects that can connect without any issues so it's not MySQL connection.

On 27 April 2017 at 12:28, Stéphane Raimbault notifications@github.com wrote:

How do you access your application (hostname)?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/mik3y/django-db-multitenant/issues/16#issuecomment-297676050, or mute the thread https://github.com/notifications/unsubscribe-auth/AJlKQDxU41lE7igqQOnONL98KTUW6zlyks5r0G3PgaJpZM4M4h6a .

-- Neamat El Tazi Assistant Professor, Faculty of Computers and Information, Cairo University, Egypt Email: n.eltazi@fci.edu.eg

--

andyhasit commented 7 years ago

I have been tearing my hair out with this issue too... Adding print statements I can see that the DatabaseWrapper._cursor() method gets called several times at 'bootup', i.e. before any requests are sent, during which time the middleware is not even loaded (and so db_name is not set on thread_local, hence the exception). Funnily enough DatabaseWrapper._cursor() doesn't seem to be called at all on requests, so I have just removed the exception raising and it seems to works fine (in debug on local, not tried on prod), which makes me wonder if there's any point it being there at all?

Edit: sorry, the above is incorrect. DatabaseWrapper._cursor() does get called in requests (that's what placing print statements when pulling an all-nighter will do to you!) but during 'bootup' it won't find the db_name on threadlocal because the middleware only kicks in and sets that when it receives requests.

The db queries which run at boot up prior to any requests are SET SQL_AUTO_IS_NULL = 0 and such. And it seems that these are only called on MYSQL and people have asked for them to be removed as they are aparently unecessary, and this may be the case on newer versions of django (see this post) which would explain why some people see this error, but others don't.

If my understanding is correct:

I'm wondering if we should raise the ImproperlyConfigured() exception in MultiTenantMiddleware.process_request() instead, which would resolve this issue. And replace the exception in DatabaseWrapper._cursor() with a printed warning because that will only happen if we haven't set in ENV, which infers we're using it from a manage.py command. But perhaps I'm missing something?

Using django 1.9.5

jsep commented 7 years ago

I've solve this issue setting the TENANT_DATABASE_NAME var to my main db. Nevertheless I'm not sure either why is not getting the correct database name.

dinesh829269 commented 7 years ago

I am also getting the same issue raise ImproperlyConfigured('dbname not set at cursor create time'). Is there any solution.

andyhasit commented 7 years ago

I created a fork which I'm using as a workaround: https://github.com/andyhasit/django-db-multitenant

esclkm commented 7 years ago

image having same error on django 1.9

stephane commented 6 years ago

@andyhasit that means you want to fallback to the default DB when not set?

andyhasit commented 6 years ago

@stephane Yes, and that's what my fork does.

stephane commented 6 years ago

@andyhasit could you propose a PR against master, please?

rg2019 commented 5 years ago

I solve this problem adding:

'db_multitenant.middleware.MultiTenantMiddleware',

To the first MIDDLEWARE, as say the documentation:

MIDDLEWARE

Don't forget add in settings.py at the end file :

from db_multitenant.utils import update_from_env
update_from_env(database_settings=DATABASES['default'],
    cache_settings=CACHES['default'])