Django and multi-tenancy issue

When there is a will there’s (often) a way :slight_smile:

According to my understanding, this is always the case:
“In Django’s admin, instances are not reused between requests. Each request is completely separate and stateless, which is a fundamental part of how HTTP and Django’s request-response cycle is designed.”

This means that you can do things with the instances without worrying that it will be reused for other tenants.

I needed to get the tenant schema name into a ModelForm init method. I then have a thread safe singleton from which I can look up all tenant data, using it’s schema name. This is how I did it. It is a kind of a hack but it uses only documented Django methods.

Can you see any problems with this?


class PlaceNameListForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        # Get the schema name from the kwargs. We must remove it before calling super(), otherwise an error will occur
        tenant_schema_name = kwargs.pop('tenant_schema_name', None)
        super().__init__(*args, **kwargs)

class PlaceNameListFormSet(BaseModelFormSet):
    """ We used this FormSet to transfer the tenant schema name over to the PlaceNameListForm"""

    def __init__(self, *args, **kwargs):
        self.tenant_schema_name = ''

        # Get the tenant schema name from our class name, see PlaceNameAdmin.get_changelist_formset()
        class_name = self.__class__.__name__
        if "_" in class_name:
            prefix, tenant_schema_name = class_name.split("_", 1)
            self.tenant_schema_name = tenant_schema_name
        super().__init__(*args, **kwargs)

    def _construct_form(self, i, **kwargs):
        # Pass the tenant schema name to the form by using the kwargs
        kwargs['tenant_schema_name'] = self.tenant_schema_name
        return super()._construct_form(i, **kwargs)


class PlaceNameAdmin(admin.ModelAdmin):
    def get_changelist_form(self, request, **kwargs):
        return PlaceNameListForm

    def get_changelist_formset(self, request, **kwargs):
        # This is a hack to get the tenant schema name into PlaceNameListFormSet. From there it is passed to
        # the PlaceNameListForm. The schema name is used to fetch correct settings for this tenant.
        # I tried many other ways but this was the only one that worked. (You can't use kwargs because
        # the factory methods in the Django code raises an exception if you pass in an unknown kwarg)

        # Override the usual formset class with PlaceNameListFormSet and then have the super() create it
        kwargs['formset'] = PlaceNameListFormSet
        FormSet = super().get_changelist_formset(request, **kwargs)

        # Dynamically inherit from PlaceNameListFormSet and create a new class with the schema name in the class name
        tenant_schema_name = request.tenant.schema_name
        class_name = f"PlaceNameListFormSet_{tenant_schema_name}"
        formset_class = type(class_name, (FormSet,), {})
        return formset_class

ps.
I asked ChatGPT 4 in several ways about ways to solve this, but it was unable to help me.