citusdata / django-multitenant

Python/Django support for distributed multi-tenant databases like Postgres+Citus
MIT License
712 stars 117 forks source link

Is recommended practice to get_current_tenant for every view? #78

Closed rob101 closed 4 years ago

rob101 commented 4 years ago

The docs state that:

Set the tenant using set_current_tenant(t) api in all the views which you want to be scoped based on tenant.

Does this mean that the recommended approach is to store the current tenant in a cookie (session variable) and then call it for each and every view that is rendered?

When simply setting the variable it appears to persist between views in thread local storage. Is this discouraged? Why otherwise incur the extra overhead of accessing session variables in every view?

louiseGrandjonc commented 4 years ago

Hi @rob101,

Are you using django-multitenant with citus, or with single node postgres? You could store it in a session variable, or if you have a logged in user with the tenant column, you could use that instead of the cookie.

In general we tend to recommend using a Middleware that would set the current tenant, rather than actually set it in all views you want it to be. But it would still be called for every call. What is your thought around this? Did you want to set it only in a login view>

rob101 commented 4 years ago

Hi @louiseGrandjonc, sorry for the late reply (I missed this). I am using postgres. My use case actually allows users to switch tenants, so my thought is to set it in a session variable via middleware as you suggest:

class TenantMiddleware:
    """
    If tenant_pk is stored in the current session, for each view set the
    issuer object as the tenant, and store the object in request.tenant.
    """

    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        # Get the issuer stored in the session (set via issuer_select)
        session_tenant = get_tenant_from_session(request)

        # Get the current issuer object that is set as current tenant
        tenant= get_current_tenant()

        # Make the current tenant available as request.issuer
        request.tenant = tenant

        # If the issuer has changed, update the tenant object
        if session_tenant != tenant:
            set_current_tenant(tenant)
            request.tenant = session_tenant

        return self.get_response(request)

I also defined a session tenant helper:

    def get_issuer_from_session(request):
    """
    Return the Tenant object from the current session
    """
    tenant_pk = request.session.get('tenant_pk', None)

    try:
        tenant= MyTenantModel.objects.get(pk=tenant_pk)
    except MyTenantModel.DoesNotExist:
        tenant = None

    return tenant

What do you think?