Project

General

Profile

Download (6.55 KB) Statistics
| Branch: | Tag: | Revision:

root / entrouvert / djommon / multitenant / management / commands / __init__.py @ 7e82bd8e

1
# this file derive from django-tenant-schemas
2
#   Author: Bernardo Pires Carneiro
3
#   Email: carneiro.be@gmail.com
4
#   License: MIT license
5
#   Home-page: http://github.com/bcarneiro/django-tenant-schemas
6
from optparse import make_option
7
from django.conf import settings
8
from django.core.management import call_command, get_commands, load_command_class
9
from django.core.management.base import BaseCommand, CommandError
10
from django.db import connection
11
try:
12
    from django.utils.six.moves import input
13
except ImportError:
14
    input = raw_input
15
from tenant_schemas.utils import get_public_schema_name
16
from entrouvert.djommon.multitenant.middleware import TenantMiddleware
17

    
18

    
19
class BaseTenantCommand(BaseCommand):
20
    """
21
    Generic command class useful for iterating any existing command
22
    over all schemata. The actual command name is expected in the
23
    class variable COMMAND_NAME of the subclass.
24
    """
25
    def __new__(cls, *args, **kwargs):
26
        """
27
        Sets option_list and help dynamically.
28
        """
29
        obj = super(BaseTenantCommand, cls).__new__(cls, *args, **kwargs)
30

    
31
        app_name = get_commands()[obj.COMMAND_NAME]
32
        if isinstance(app_name, BaseCommand):
33
            # If the command is already loaded, use it directly.
34
            cmdclass = app_name
35
        else:
36
            cmdclass = load_command_class(app_name, obj.COMMAND_NAME)
37

    
38
        # inherit the options from the original command
39
        obj.option_list = cmdclass.option_list
40
        obj.option_list += (
41
            make_option("-d", "--domain", dest="domain"),
42
        )
43
        obj.option_list += (
44
            make_option("-p", "--skip-public", dest="skip_public", action="store_true", default=False),
45
        )
46

    
47
        # prepend the command's original help with the info about schemata iteration
48
        obj.help = "Calls %s for all registered schemata. You can use regular %s options. "\
49
                   "Original help for %s: %s" % (obj.COMMAND_NAME, obj.COMMAND_NAME, obj.COMMAND_NAME,
50
                                                 getattr(cmdclass, 'help', 'none'))
51
        return obj
52

    
53
    def execute_command(self, tenant, command_name, *args, **options):
54
        verbosity = int(options.get('verbosity'))
55

    
56
        if verbosity >= 1:
57
            print()
58
            print(self.style.NOTICE("=== Switching to schema '") \
59
                + self.style.SQL_TABLE(tenant.schema_name)\
60
                + self.style.NOTICE("' then calling %s:" % command_name))
61

    
62
        connection.set_tenant(tenant)
63

    
64
        # call the original command with the args it knows
65
        call_command(command_name, *args, **options)
66

    
67
    def handle(self, *args, **options):
68
        """
69
        Iterates a command over all registered schemata.
70
        """
71
        if options['domain']:
72
            # only run on a particular schema
73
            connection.set_schema_to_public()
74
            self.execute_command(TenantMiddleware.get_tenant_by_hostname(options['domain']), self.COMMAND_NAME, *args, **options)
75
        else:
76
            for tenant in TenantMiddleware.get_tenants():
77
                if not(options['skip_public'] and tenant.schema_name == get_public_schema_name()):
78
                    self.execute_command(tenant, self.COMMAND_NAME, *args, **options)
79

    
80

    
81
class InteractiveTenantOption(object):
82
    def __init__(self, *args, **kwargs):
83
        super(InteractiveTenantOption, self).__init__(*args, **kwargs)
84
        self.option_list += (
85
            make_option("-d", "--domain", dest="domain", help="specify tenant domain"),
86
        )
87

    
88
    def get_tenant_from_options_or_interactive(self, **options):
89
        all_tenants = list(TenantMiddleware.get_tenants())
90

    
91
        if not all_tenants:
92
            raise CommandError("""There are no tenants in the system.
93
To learn how create a tenant, see:
94
https://django-tenant-schemas.readthedocs.org/en/latest/use.html#creating-a-tenant""")
95

    
96
        if options.get('domain'):
97
            tenant_schema = options['domain']
98
        else:
99
            while True:
100
                tenant_schema = input("Enter Tenant Domain ('?' to list schemas): ")
101
                if tenant_schema == '?':
102
                    print('\n'.join(["%s - %s" % (t.schema_name, t.domain_url,) for t in all_tenants]))
103
                else:
104
                    break
105

    
106
        if tenant_schema not in [t.schema_name for t in all_tenants]:
107
            raise CommandError("Invalid tenant schema, '%s'" % (tenant_schema,))
108

    
109
        return TenantMiddleware.get_tenant_by_hostname(tenant_schema)
110

    
111

    
112
class TenantWrappedCommand(InteractiveTenantOption, BaseCommand):
113
    """
114
    Generic command class useful for running any existing command
115
    on a particular tenant. The actual command name is expected in the
116
    class variable COMMAND_NAME of the subclass.
117
    """
118
    def __new__(cls, *args, **kwargs):
119
        obj = super(TenantWrappedCommand, cls).__new__(cls, *args, **kwargs)
120
        obj.command_instance = obj.COMMAND()
121
        obj.option_list = obj.command_instance.option_list
122
        return obj
123

    
124
    def handle(self, *args, **options):
125
        tenant = self.get_tenant_from_options_or_interactive(**options)
126
        connection.set_tenant(tenant)
127

    
128
        self.command_instance.execute(*args, **options)
129

    
130

    
131
class SyncCommon(BaseCommand):
132
    option_list = (
133
        make_option('--tenant', action='store_true', dest='tenant', default=False,
134
                    help='Tells Django to populate only tenant applications.'),
135
        make_option('--shared', action='store_true', dest='shared', default=False,
136
                    help='Tells Django to populate only shared applications.'),
137
        make_option("-d", "--domain", dest="domain"),
138
    )
139

    
140
    def handle(self, *args, **options):
141
        self.sync_tenant = options.get('tenant')
142
        self.sync_public = options.get('shared')
143
        self.domain = options.get('domain')
144
        self.installed_apps = settings.INSTALLED_APPS
145
        self.args = args
146
        self.options = options
147

    
148
        if self.domain:
149
            if self.sync_public:
150
                raise CommandError("schema should only be used with the --tenant switch.")
151
            elif self.domain == get_public_schema_name():
152
                self.sync_public = True
153
            else:
154
                self.sync_tenant = True
155
        elif not self.sync_public and not self.sync_tenant:
156
            # no options set, sync both
157
            self.sync_tenant = True
158
            self.sync_public = True
159

    
160
        if hasattr(settings, 'TENANT_APPS'):
161
            self.tenant_apps = settings.TENANT_APPS
162
        if hasattr(settings, 'SHARED_APPS'):
163
            self.shared_apps = settings.SHARED_APPS
164

    
165
    def _notice(self, output):
166
        self.stdout.write(self.style.NOTICE(output))
(1-1/11)