3
Django==1.11.7
django-tenant-schemas==1.8.0
django-allauth==0.34.0

Multi tenant site using django-tenant-schemas (postgres). On different tenants, different settings are required.

More specifically, different setting is required for ACCOUNT_EMAIL_VERIFICATION

1 tenant needs ACCOUNT_EMAIL_VERIFICATION = "optional" while another one needs ACCOUNT_EMAIL_VERIFICATION ="mandatory"

Looking in the source code, the setting looks not customisable, it is fixed for the whole django site.

-> How can this be done?

Davy
  • 1,720
  • 1
  • 19
  • 42

3 Answers3

4

You can compute the settings at runtime, since it's simply a python code.

Set that specific code programmatically, using your preferred way. One example:

# predefine the settings per tenant
ACCOUNT_EMAIL_VERIFICATION_PER_TENANT = {
    "tenant_x": "mandatory",
    "tenant_y": "optional",
}

# implement get_tenant 

def get_tenant():
    # here be tenant logic
    pass

this_tenant = get_tenant()
ACCOUNT_EMAIL_VERIFICATION = ACCOUNT_EMAIL_VERIFICATION_PER_TENANT[get_tenant()]

Or you can have multiple settings files and join them as you wish. Here's how django does.

Oh and if you want to separate the logic from the settings file and have it run before evaluating the settings perhaps, you can inspect what is the trail of execution when you launch your server (e.g. starting from manage.py and insert your get_tenant logic somewhere in between). Most probably it will be somewhere starting from the wsgi.py file - where the application instance gets created and all the django fun begins.

When it comes to programming, you are always in control.

Adelin
  • 7,809
  • 5
  • 37
  • 65
  • Interesting approach. Testing this, I face issues as the get_tenant logic includes `return connection.schema_name` which needs an `from django.db import connection` at the top of the SETTINGS file. Apparently this causes problems with the settings file as my SECRET KEY is not found anymore while it is clearly there and unchanged, resulting in `django.core.exceptions.ImproperlyConfigured: The SECRET_KEY setting must not be empty` – Davy Jan 17 '18 at 15:02
  • Follow up question: Why would the line `this_tenant = get_tenant()` be required? – Davy Jan 17 '18 at 15:02
  • Tried with `ACCOUNT_EMAIL_VERIFICATION = lambda: get_tenant_email_verification()` where the `get_tenant_email_verification()` function immediately returns `mandatory` or `optional`. The lambda then avoids the error while the settings are being read, but for some reason the lambda doesn't get evaluated when the variable is used. – Davy Jan 17 '18 at 19:02
  • lambda doesn't get evaluated when the variable is used, indeed. it is evaluated when the server is launched. That's when all the settings are evaluated. You need to follow the trail of what is being executed at what time, and insert the `get_tenant` logic at the right time. E.G if you use `manage.py`, you can start from there and figure what's the right place in your context. Or perhaps you use the `wsgi.py` file to handle your server. Start from there then – Adelin Jan 18 '18 at 05:29
  • The lambda approach indeed does not work. Not clear how wsgi.py could be of any use in this. It's rather empty now: `from django.core.wsgi import get_wsgi_application` `application = get_wsgi_application()` – Davy Jan 20 '18 at 17:21
1

I stumbled upon this situation, and my dynamic solution is a middleware as follows without any hardcoding tenant's names

from django.conf import settings
from django.db import connection
from django_tenants.utils import get_public_schema_name, get_tenant_model

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

    def __call__(self, request):
        self.request = request
        self.overload_settings()
        response = self.get_response(request)
        return response

    def overload_settings(self):
        current_schema_obj = get_tenant_model().objects.get(schema_name=connection.schema_name)
        settings.DEFAULT_FROM_EMAIL = 'admin@{}'.format(current_schema_obj.domains.last())


Cheers

Ahmed Shehab
  • 1,657
  • 15
  • 24
0

Solved in following way:

In settings.py:

try:
    ACCOUNT_EMAIL_VERIFICATION = os.environ['ACCOUNT_EMAIL_VERIFICATION_OVERRIDE']
except KeyError:
    ACCOUNT_EMAIL_VERIFICATION = 'mandatory'

In wsgi.py file of the tenant where e-mail verification is optional:

os.environ['ACCOUNT_EMAIL_VERIFICATION_OVERRIDE'] = 'optional'

wsgi files for the other tenants remain unchanged.


Gave the bounty to Adelin as he suggested to look into the wsgi file.

Davy
  • 1,720
  • 1
  • 19
  • 42