django-multitenant
django-multitenant copied to clipboard
Tenant not filtered in model forms
Suppose I have the following 2 models and a model form
`
class CostCenter(ClientModelMixin, models.Model):
tenant_id = 'client_id'
client = models.ForeignKey('accounts.Client', on_delete=models.CASCADE)
name = models.CharField(max_length=50)
objects = TenantManager()
class Account(ClientModelMixin, models.Model):
tenant_id = 'client_id'
client = models.ForeignKey('accounts.Client', on_delete=models.CASCADE)
name = models.CharField(max_length=50)
cost_center = TenantForeignKey(CostCenter, on_delete=models.PROTECT)
objects = TenantManager()
class CreateForm(forms.ModelForm):
class Meta:
model = Account
fields = ['cost_center']
`
When the model form is rendered it shows all cost centers in the dropdown. I think it happens because the model form is created before the tenant is set and then no tenant filtering is applied.
I can override the init method of every form and set the queryset correctly but this isn't ideal.
How can this be solved?
I think the best option is to subclass ModelForm:
class TenantModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
kwargs.setdefault('label_suffix', '')
super().__init__(*args, **kwargs)
tenant = get_current_tenant()
if tenant:
for field in self.fields.values():
if isinstance(
field,
(forms.ModelChoiceField,
forms.ModelMultipleChoiceField),
) and hasattr(field.queryset.model, 'tenant_id'):
# Add filter restricting queryset to values to this
# tenant only.
kwargs = {field.queryset.model.tenant_id: tenant}
field.queryset = field.queryset.filter(**kwargs)
else:
raise ImproperlyConfigured('Current tenant is not set. You must '
'have a tenant set when calling a '
'subclass of TenantModelForm')
If you use django-simple-multitenant
v3.1.0 or above and define tenant_id
using TenantMeta
, code from comment above will no longer work, you need to replace:
if isinstance(
...
) and hasattr(field.queryset.model, 'tenant_id'):
...
kwargs = {field.queryset.model.tenant_id: tenant}
with something like:
from django_multitenant.utils import get_tenant_filters
from django_multitenant.models import TenantModel
...
if isinstance(
...
) and issubclass(field.queryset.model, TenantModel):
...
kwargs = get_tenant_filters(field.queryset.model)