I am building a multi-tenant project using third-party package (django-tenants) which makes me derive the Tenant and Domain models from it's own classes:

from django_tenants.models import TenantMixin, DomainMixin

class Tenant(TenantMixin):
    name = models.CharField(max_length=100)
    email = models.CharField(max_length=100)
    created_on = models.DateField(auto_now_add=True)
    auto_create_schema = True
    auto_drop_shema = True

class Domain(DomainMixin):
    pass;

Long story short, I want to create an API view to register tenants but I couldn't understand package code and failed when trying to create a TenantManager; But I did figure out how to call the CLI management commands provided by django-tenants (python manage.py create_tenant/create_tenant_superuser) from inside the code using django.core.management.call_commands:

management.call_command(
    'create_tenant', 
    domain_domain = 'tenant1.mysite.com', 
    schema_name = 'tenant1Schema',
    name ='tenant1Name',
    email = '[email protected]', 
    domain_is_primary = True,
    )

So I figured the temporary simplified solution - another model (Founder) made with regular models.Model and with it's own genericAPIview which I know how to create. Upon posting this model I want to execute the signal to my custom management.call_command.

As far as I understand, there are three parts to django signals:

  • sender - this is supposed to be a "notify_about_sender_creation" method in my Founder model
  • receiver - this is supposed to be a callable object, in my case it's this custom management.call_command
  • signal - I don't know whether this should be a post_save or custom signal ? Can I only feed variables to a custom one ?

Based on this understanding I got:

class Founder(models.Model):
    first_name = models.CharField(max_length=200)
    last_name = models.CharField(max_length=200)
    email = models.CharField(max_length=200, unique=True)
    tenant_name = models.CharField(max_length=200)
    domain_name = models.CharField(max_length=200)

    def generate(self):
        data = (Founder.first_name, Founder.last_name, Founder.email, Founder.tenant_name, Founder.domain_name)
        save_founder(sender=self, data=data)

save_founder = Signal(providing_args=['first_name','last_name','email','tenant_name','domain_name'])

@receiver(save_founder) def generate_tenant(sender, first_name, last_name, email, tenant_name, domain_name , **kwargs):
    management.call_command(
        'create_tenant', 
        domain_domain = sender.domain_name, 
        schema_name = sender.tenant_name,
        name = sender.tenant_name,
        email = sender.email, 
        domain_is_primary = True,
        ) save_founder.connect(generate_tenant)

but in my console I get:

python manage.py shell
from tenant.models import Founder
f = Founder()
f.generate()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/app/tenant/models.py", line 26, in generate
TypeError: 'Signal' object is not callable
1

There are 1 answers

0
illevens On

What was needed was to pass instance to receiver function. Signal doesn't wasn't ought to be custom. I don't know whether I had to make my receiver function a method of Founder class but it doesn't hurt. Here's what worked:

class Founder(models.Model):
    tenant_name = models.CharField(max_length=200)
    domain_name = models.CharField(max_length=200)
    ...
    def generate(sender, instance, **kwargs):
        founder=instance
        management.call_command(
            'create_tenant', 
            domain_domain = instance.domain_name + ".mysite.com", 
            name = instance.tenant_name,
            ...
            )
post_save.connect(Founder.generate, sender=Founder)