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))
|