From 54ab03723979d14cae729a1a0ca32ea9ab5ea5cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenzo=20Pe=C3=B1a?= Date: Sat, 1 Jul 2023 14:33:35 +0200 Subject: [PATCH] Applied linters --- django_pgschemas/__init__.py | 16 ++++- django_pgschemas/apps.py | 26 +++++-- django_pgschemas/checks.py | 22 ++++-- django_pgschemas/contrib/channels2/auth.py | 4 +- django_pgschemas/contrib/channels2/router.py | 14 +++- django_pgschemas/contrib/channels3/auth.py | 4 +- django_pgschemas/contrib/channels3/router.py | 14 +++- .../management/commands/__init__.py | 43 +++++++++--- .../management/commands/_executors.py | 8 ++- .../management/commands/cloneschema.py | 24 +++++-- .../management/commands/createrefschema.py | 11 ++- .../management/commands/whowill.py | 4 +- django_pgschemas/middleware.py | 8 ++- django_pgschemas/models.py | 5 +- django_pgschemas/postgresql_backend/base.py | 10 ++- django_pgschemas/signals.py | 8 ++- django_pgschemas/test/cases.py | 3 +- django_pgschemas/utils.py | 8 ++- docs/conf.py | 4 +- .../app_blog/migrations/0001_initial.py | 11 ++- .../app_main/migrations/0001_initial.py | 7 +- .../app_tenants/migrations/0001_initial.py | 12 +++- dpgs_sandbox/app_tenants/models.py | 8 ++- dpgs_sandbox/settings.py | 7 +- .../shared_common/migrations/0001_initial.py | 12 +++- .../shared_public/migrations/0001_initial.py | 33 +++++++-- dpgs_sandbox/tests/test_apps.py | 70 ++++++++++++++----- .../test_bug_migrations_in_base_models.py | 8 ++- dpgs_sandbox/tests/test_bug_url_cache.py | 16 +++-- dpgs_sandbox/tests/test_checks.py | 40 ++++++++--- dpgs_sandbox/tests/test_executors.py | 4 +- dpgs_sandbox/tests/test_file_storage.py | 16 +++-- dpgs_sandbox/tests/test_log.py | 4 +- .../tests/test_middleware_redirection.py | 18 +++-- .../tests/test_schema_creation_commands.py | 9 ++- dpgs_sandbox/tests/test_tenant_commands.py | 33 ++++++--- dpgs_sandbox/tests/test_tenants.py | 32 ++++++--- dpgs_sandbox/tests/test_test_client.py | 20 ++++-- dpgs_sandbox/tests/test_urlresolvers.py | 27 +++++-- dpgs_sandbox/tests/test_utils.py | 4 +- dpgs_sandbox/tests/test_whowill_command.py | 36 +++++++--- 41 files changed, 517 insertions(+), 146 deletions(-) diff --git a/django_pgschemas/__init__.py b/django_pgschemas/__init__.py index 4a5e5d0..6d93775 100644 --- a/django_pgschemas/__init__.py +++ b/django_pgschemas/__init__.py @@ -1 +1,15 @@ -from .schema import SchemaDescriptor, activate, activate_public, deactivate, get_current_schema # noqa +from .schema import ( + SchemaDescriptor, + activate, + activate_public, + deactivate, + get_current_schema, +) + +__all__ = [ + "SchemaDescriptor", + "activate", + "activate_public", + "deactivate", + "get_current_schema", +] diff --git a/django_pgschemas/apps.py b/django_pgschemas/apps.py index 99c9dfa..368ec29 100644 --- a/django_pgschemas/apps.py +++ b/django_pgschemas/apps.py @@ -38,12 +38,16 @@ def _check_default_schemas(self): if "DOMAINS" in settings.TENANTS["default"]: raise ImproperlyConfigured("TENANTS['default'] cannot contain a 'DOMAINS' key.") if "FALLBACK_DOMAINS" in settings.TENANTS["default"]: - raise ImproperlyConfigured("TENANTS['default'] cannot contain a 'FALLBACK_DOMAINS' key.") + raise ImproperlyConfigured( + "TENANTS['default'] cannot contain a 'FALLBACK_DOMAINS' key." + ) if ( "CLONE_REFERENCE" in settings.TENANTS["default"] and settings.TENANTS["default"]["CLONE_REFERENCE"] in settings.TENANTS ): - raise ImproperlyConfigured("TENANTS['default']['CLONE_REFERENCE'] must be a unique schema name.") + raise ImproperlyConfigured( + "TENANTS['default']['CLONE_REFERENCE'] must be a unique schema name." + ) def _check_overall_schemas(self): for schema in settings.TENANTS: @@ -51,31 +55,39 @@ def _check_overall_schemas(self): if not is_valid_schema_name(schema): raise ImproperlyConfigured("'%s' is not a valid schema name." % schema) if not isinstance(settings.TENANTS[schema].get("DOMAINS"), list): - raise ImproperlyConfigured("TENANTS['%s'] must contain a 'DOMAINS' list." % schema) + raise ImproperlyConfigured( + "TENANTS['%s'] must contain a 'DOMAINS' list." % schema + ) def _check_complementary_settings(self): if "django_pgschemas.routers.SyncRouter" not in settings.DATABASE_ROUTERS: - raise ImproperlyConfigured("DATABASE_ROUTERS setting must contain 'django_pgschemas.routers.SyncRouter'.") + raise ImproperlyConfigured( + "DATABASE_ROUTERS setting must contain 'django_pgschemas.routers.SyncRouter'." + ) def _check_extra_search_paths(self): if hasattr(settings, "PGSCHEMAS_EXTRA_SEARCH_PATHS"): TenantModel = get_tenant_model() cursor = connection.cursor() cursor.execute( - "SELECT 1 FROM information_schema.tables WHERE table_name = %s;", [TenantModel._meta.db_table] + "SELECT 1 FROM information_schema.tables WHERE table_name = %s;", + [TenantModel._meta.db_table], ) dynamic_tenants = [] if "CLONE_REFERENCE" in settings.TENANTS["default"]: dynamic_tenants.append(settings.TENANTS["default"]["CLONE_REFERENCE"]) if cursor.fetchone(): - dynamic_tenants += list(TenantModel.objects.all().values_list("schema_name", flat=True)) + dynamic_tenants += list( + TenantModel.objects.all().values_list("schema_name", flat=True) + ) cursor.close() invalid_schemas = set(settings.PGSCHEMAS_EXTRA_SEARCH_PATHS).intersection( set(settings.TENANTS.keys()).union(dynamic_tenants) ) if invalid_schemas: raise ImproperlyConfigured( - "Do not include '%s' on PGSCHEMAS_EXTRA_SEARCH_PATHS." % ", ".join(invalid_schemas) + "Do not include '%s' on PGSCHEMAS_EXTRA_SEARCH_PATHS." + % ", ".join(invalid_schemas) ) def ready(self): diff --git a/django_pgschemas/checks.py b/django_pgschemas/checks.py index 0d628d3..d068e9b 100644 --- a/django_pgschemas/checks.py +++ b/django_pgschemas/checks.py @@ -41,11 +41,17 @@ def check_principal_apps(app_configs, **kwargs): domain_app = get_domain_app() if tenant_app not in settings.TENANTS["public"].get("APPS", []): errors.append( - checks.Error("Your tenant app '%s' must be on the 'public' schema." % tenant_app, id="pgschemas.W001") + checks.Error( + "Your tenant app '%s' must be on the 'public' schema." % tenant_app, + id="pgschemas.W001", + ) ) if domain_app not in settings.TENANTS["public"].get("APPS", []): errors.append( - checks.Error("Your domain app '%s' must be on the 'public' schema." % domain_app, id="pgschemas.W001") + checks.Error( + "Your domain app '%s' must be on the 'public' schema." % domain_app, + id="pgschemas.W001", + ) ) for schema in settings.TENANTS: schema_apps = settings.TENANTS[schema].get("APPS", []) @@ -97,14 +103,20 @@ def check_other_apps(app_configs, **kwargs): if session_app in schema_apps and user_app not in schema_apps: errors.append( checks.Warning( - "'%s' must be together with '%s' in TENANTS['%s']['APPS']." % (user_app, session_app, schema), + "'%s' must be together with '%s' in TENANTS['%s']['APPS']." + % (user_app, session_app, schema), id="pgschemas.W003", ) ) - elif user_app in schema_apps and session_app not in schema_apps and session_app in settings.INSTALLED_APPS: + elif ( + user_app in schema_apps + and session_app not in schema_apps + and session_app in settings.INSTALLED_APPS + ): errors.append( checks.Warning( - "'%s' must be together with '%s' in TENANTS['%s']['APPS']." % (session_app, user_app, schema), + "'%s' must be together with '%s' in TENANTS['%s']['APPS']." + % (session_app, user_app, schema), id="pgschemas.W003", ) ) diff --git a/django_pgschemas/contrib/channels2/auth.py b/django_pgschemas/contrib/channels2/auth.py index 2e4041a..80221b7 100644 --- a/django_pgschemas/contrib/channels2/auth.py +++ b/django_pgschemas/contrib/channels2/auth.py @@ -13,7 +13,9 @@ def get_user(scope): If no user is retrieved, return an instance of `AnonymousUser`. """ if "session" not in scope: - raise ValueError("Cannot find session in scope. You should wrap your consumer in SessionMiddleware.") + raise ValueError( + "Cannot find session in scope. You should wrap your consumer in SessionMiddleware." + ) user = None session = scope["session"] with scope["tenant"]: diff --git a/django_pgschemas/contrib/channels2/router.py b/django_pgschemas/contrib/channels2/router.py index 416c43b..e620c03 100644 --- a/django_pgschemas/contrib/channels2/router.py +++ b/django_pgschemas/contrib/channels2/router.py @@ -57,16 +57,24 @@ def get_tenant_scope(self, scope): DomainModel = get_domain_model() prefix = scope["path"].split("/")[1] try: - domain = DomainModel.objects.select_related("tenant").get(domain=hostname, folder=prefix) + domain = DomainModel.objects.select_related("tenant").get( + domain=hostname, folder=prefix + ) except DomainModel.DoesNotExist: try: - domain = DomainModel.objects.select_related("tenant").get(domain=hostname, folder="") + domain = DomainModel.objects.select_related("tenant").get( + domain=hostname, folder="" + ) except DomainModel.DoesNotExist: return None, "", [] tenant = domain.tenant tenant.domain_url = hostname ws_urlconf = settings.TENANTS["default"]["WS_URLCONF"] - return tenant, prefix if prefix == domain.folder else "", import_string(ws_urlconf + ".urlpatterns") + return ( + tenant, + prefix if prefix == domain.folder else "", + import_string(ws_urlconf + ".urlpatterns"), + ) def get_protocol_type_router(self, tenant_prefix, ws_urlconf): """ diff --git a/django_pgschemas/contrib/channels3/auth.py b/django_pgschemas/contrib/channels3/auth.py index 2e4041a..80221b7 100644 --- a/django_pgschemas/contrib/channels3/auth.py +++ b/django_pgschemas/contrib/channels3/auth.py @@ -13,7 +13,9 @@ def get_user(scope): If no user is retrieved, return an instance of `AnonymousUser`. """ if "session" not in scope: - raise ValueError("Cannot find session in scope. You should wrap your consumer in SessionMiddleware.") + raise ValueError( + "Cannot find session in scope. You should wrap your consumer in SessionMiddleware." + ) user = None session = scope["session"] with scope["tenant"]: diff --git a/django_pgschemas/contrib/channels3/router.py b/django_pgschemas/contrib/channels3/router.py index 2e26802..9b48059 100644 --- a/django_pgschemas/contrib/channels3/router.py +++ b/django_pgschemas/contrib/channels3/router.py @@ -59,16 +59,24 @@ def get_tenant_scope(self, scope): DomainModel = get_domain_model() prefix = scope["path"].split("/")[1] try: - domain = DomainModel.objects.select_related("tenant").get(domain=hostname, folder=prefix) + domain = DomainModel.objects.select_related("tenant").get( + domain=hostname, folder=prefix + ) except DomainModel.DoesNotExist: try: - domain = DomainModel.objects.select_related("tenant").get(domain=hostname, folder="") + domain = DomainModel.objects.select_related("tenant").get( + domain=hostname, folder="" + ) except DomainModel.DoesNotExist: return None, "", [] tenant = domain.tenant tenant.domain_url = hostname ws_urlconf = settings.TENANTS["default"]["WS_URLCONF"] - return tenant, prefix if prefix == domain.folder else "", import_string(ws_urlconf + ".urlpatterns") + return ( + tenant, + prefix if prefix == domain.folder else "", + import_string(ws_urlconf + ".urlpatterns"), + ) def get_protocol_type_router(self, tenant_prefix, ws_urlconf): """ diff --git a/django_pgschemas/management/commands/__init__.py b/django_pgschemas/management/commands/__init__.py index 17ab4f5..a87917d 100644 --- a/django_pgschemas/management/commands/__init__.py +++ b/django_pgschemas/management/commands/__init__.py @@ -2,8 +2,7 @@ from django.conf import settings from django.core.management.base import BaseCommand, CommandError -from django.db.models import CharField, Q -from django.db.models import Value as V +from django.db.models import CharField, Q, Value as V from django.db.models.functions import Concat from django.db.utils import ProgrammingError @@ -49,7 +48,11 @@ def add_arguments(self, parser): help="Tells Django to NOT prompt the user for input of any kind.", ) parser.add_argument( - "-s", "--schema", nargs="+", dest="schemas", help="Schema(s) to execute the current command" + "-s", + "--schema", + nargs="+", + dest="schemas", + help="Schema(s) to execute the current command", ) parser.add_argument( "-x", @@ -151,7 +154,8 @@ def _get_schemas_from_options(self, **options): include_all_schemas = True elif options.get("interactive", True): schema = input( - "Enter schema to run command (leave blank for running on '%s' schemas): " % self.get_scope_display() + "Enter schema to run command (leave blank for running on '%s' schemas): " + % self.get_scope_display() ).strip() if schema: @@ -162,9 +166,13 @@ def _get_schemas_from_options(self, **options): raise CommandError("No schema provided") TenantModel = get_tenant_model() - static_schemas = [x for x in settings.TENANTS.keys() if x != "default"] if allow_static else [] + static_schemas = ( + [x for x in settings.TENANTS.keys() if x != "default"] if allow_static else [] + ) dynamic_schemas = ( - TenantModel.objects.values_list("schema_name", flat=True) if dynamic_ready and allow_dynamic else [] + TenantModel.objects.values_list("schema_name", flat=True) + if dynamic_ready and allow_dynamic + else [] ) if clone_reference and allow_static: static_schemas.append(clone_reference) @@ -195,7 +203,11 @@ def find_schema_by_reference(reference, as_excluded=False): return reference elif reference == clone_reference: return reference - elif dynamic_ready and TenantModel.objects.filter(schema_name=reference).exists() and allow_dynamic: + elif ( + dynamic_ready + and TenantModel.objects.filter(schema_name=reference).exists() + and allow_dynamic + ): return reference else: local = [] @@ -209,16 +221,27 @@ def find_schema_by_reference(reference, as_excluded=False): if dynamic_ready and allow_dynamic: local += ( TenantModel.objects.annotate( - route=Concat("domains__domain", V("/"), "domains__folder", output_field=CharField()) + route=Concat( + "domains__domain", + V("/"), + "domains__folder", + output_field=CharField(), + ) ) .filter( - Q(schema_name=reference) | Q(domains__domain__istartswith=reference) | Q(route=reference) + Q(schema_name=reference) + | Q(domains__domain__istartswith=reference) + | Q(route=reference) ) .distinct() .values_list("schema_name", flat=True) ) if not local: - message = "No schema found for '%s' (excluded)" if as_excluded else "No schema found for '%s'" + message = ( + "No schema found for '%s' (excluded)" + if as_excluded + else "No schema found for '%s'" + ) raise CommandError(message % reference) if len(local) > 1: message = ( diff --git a/django_pgschemas/management/commands/_executors.py b/django_pgschemas/management/commands/_executors.py index 99f7dc3..e056c5e 100644 --- a/django_pgschemas/management/commands/_executors.py +++ b/django_pgschemas/management/commands/_executors.py @@ -63,7 +63,9 @@ def __call__(self, message): if schema_name in settings.TENANTS: domains = settings.TENANTS[schema_name].get("DOMAINS", []) - schema = SchemaDescriptor.create(schema_name=schema_name, domain_url=domains[0] if domains else None) + schema = SchemaDescriptor.create( + schema_name=schema_name, domain_url=domains[0] if domains else None + ) elif schema_name == get_clone_reference(): schema = SchemaDescriptor.create(schema_name=schema_name) else: @@ -89,7 +91,9 @@ def __call__(self, message): return schema_name -def sequential(schemas, command, function_name, args=None, kwargs=None, pass_schema_in_kwargs=False): +def sequential( + schemas, command, function_name, args=None, kwargs=None, pass_schema_in_kwargs=False +): runner = functools.partial( run_on_schema, executor_codename="sequential", diff --git a/django_pgschemas/management/commands/cloneschema.py b/django_pgschemas/management/commands/cloneschema.py index 2197708..051ee5f 100644 --- a/django_pgschemas/management/commands/cloneschema.py +++ b/django_pgschemas/management/commands/cloneschema.py @@ -16,8 +16,14 @@ def _run_checks(self, **kwargs): # pragma: no cover def add_arguments(self, parser): super().add_arguments(parser) - parser.add_argument("source", help="The name of the schema you want to clone") - parser.add_argument("destination", help="The name of the schema you want to create as clone") + parser.add_argument( + "source", + help="The name of the schema you want to clone", + ) + parser.add_argument( + "destination", + help="The name of the schema you want to create as clone", + ) parser.add_argument( "--noinput", "--no-input", @@ -61,10 +67,16 @@ def _check_required_field(self, field, exclude=None): ) def _get_constructed_instance(self, model_class, data): - fields = [field for field in model_class._meta.fields if self._check_required_field(field, data.keys())] + fields = [ + field + for field in model_class._meta.fields + if self._check_required_field(field, data.keys()) + ] instance = model_class(**data) if fields: - self.stdout.write(self.style.WARNING(f"We need some data for model '{model_class._meta.model_name}':")) + self.stdout.write( + self.style.WARNING(f"We need some data for model '{model_class._meta.model_name}':") + ) for field in fields: while field.name not in data: raw_value = input(f"Value for field '{field.name}': ") @@ -88,7 +100,9 @@ def get_dynamic_tenant(self, **options): if self._ask( "You are cloning a schema for a dynamic tenant. Would you like to create a database entry for it?" ): - tenant = self._get_constructed_instance(get_tenant_model(), {"schema_name": options["destination"]}) + tenant = self._get_constructed_instance( + get_tenant_model(), {"schema_name": options["destination"]} + ) domain = self._get_constructed_instance(get_domain_model(), {"is_primary": True}) if options["verbosity"] >= 1: self.stdout.write(self.style.WARNING("Looks good! Let's get to it!")) diff --git a/django_pgschemas/management/commands/createrefschema.py b/django_pgschemas/management/commands/createrefschema.py index 94cfa37..f06d0a5 100644 --- a/django_pgschemas/management/commands/createrefschema.py +++ b/django_pgschemas/management/commands/createrefschema.py @@ -14,7 +14,12 @@ def _run_checks(self, **kwargs): # pragma: no cover def add_arguments(self, parser): super().add_arguments(parser) - parser.add_argument("--recreate", action="store_true", dest="recreate", help="Recreate reference schema.") + parser.add_argument( + "--recreate", + action="store_true", + dest="recreate", + help="Recreate reference schema.", + ) def handle(self, *args, **options): clone_reference = get_clone_reference() @@ -24,7 +29,9 @@ def handle(self, *args, **options): drop_schema(clone_reference, check_if_exists=True, verbosity=options["verbosity"]) if options["verbosity"] >= 1: self.stdout.write("Destroyed existing reference schema.") - created = create_schema(clone_reference, check_if_exists=True, verbosity=options["verbosity"]) + created = create_schema( + clone_reference, check_if_exists=True, verbosity=options["verbosity"] + ) if options["verbosity"] >= 1: if created: self.stdout.write("Reference schema successfully created!") diff --git a/django_pgschemas/management/commands/whowill.py b/django_pgschemas/management/commands/whowill.py index 7bc862a..519cd6d 100644 --- a/django_pgschemas/management/commands/whowill.py +++ b/django_pgschemas/management/commands/whowill.py @@ -7,5 +7,7 @@ class Command(TenantCommand): def handle_tenant(self, tenant, *args, **options): if options["verbosity"] >= 1: self.stdout.write( - str(tenant.get_primary_domain()) if tenant.is_dynamic else tenant.domain_url or tenant.schema_name + str(tenant.get_primary_domain()) + if tenant.is_dynamic + else tenant.domain_url or tenant.schema_name ) diff --git a/django_pgschemas/middleware.py b/django_pgschemas/middleware.py index 40c4b5c..dd83186 100644 --- a/django_pgschemas/middleware.py +++ b/django_pgschemas/middleware.py @@ -42,10 +42,14 @@ def middleware(request): DomainModel = get_domain_model() prefix = request.path.split("/")[1] try: - domain = DomainModel.objects.select_related("tenant").get(domain=hostname, folder=prefix) + domain = DomainModel.objects.select_related("tenant").get( + domain=hostname, folder=prefix + ) except DomainModel.DoesNotExist: try: - domain = DomainModel.objects.select_related("tenant").get(domain=hostname, folder="") + domain = DomainModel.objects.select_related("tenant").get( + domain=hostname, folder="" + ) except DomainModel.DoesNotExist: domain = None if domain: diff --git a/django_pgschemas/models.py b/django_pgschemas/models.py index e922f57..10e03aa 100644 --- a/django_pgschemas/models.py +++ b/django_pgschemas/models.py @@ -105,7 +105,10 @@ class DomainMixin(models.Model): """ tenant = models.ForeignKey( - settings.TENANTS["default"]["TENANT_MODEL"], db_index=True, related_name="domains", on_delete=models.CASCADE + settings.TENANTS["default"]["TENANT_MODEL"], + db_index=True, + related_name="domains", + on_delete=models.CASCADE, ) domain = models.CharField(max_length=253, db_index=True) diff --git a/django_pgschemas/postgresql_backend/base.py b/django_pgschemas/postgresql_backend/base.py index 93befe2..6ac99b4 100644 --- a/django_pgschemas/postgresql_backend/base.py +++ b/django_pgschemas/postgresql_backend/base.py @@ -47,14 +47,20 @@ def close(self): def _handle_search_path(self, cursor=None): search_path_for_current_schema = get_search_path(get_current_schema()) - skip = self._setting_search_path or self._search_path == search_path_for_current_schema or get_limit_set_calls() + skip = ( + self._setting_search_path + or self._search_path == search_path_for_current_schema + or get_limit_set_calls() + ) if not skip: self._setting_search_path = True cursor_for_search_path = self.connection.cursor() if cursor is None else cursor try: - cursor_for_search_path.execute(f"SET search_path = {search_path_for_current_schema}") + cursor_for_search_path.execute( + f"SET search_path = {search_path_for_current_schema}" + ) except (DatabaseError, _psycopg.InternalError): self._search_path = None else: diff --git a/django_pgschemas/signals.py b/django_pgschemas/signals.py index 0d30c96..90aa705 100644 --- a/django_pgschemas/signals.py +++ b/django_pgschemas/signals.py @@ -10,7 +10,9 @@ dynamic_tenant_needs_sync.__doc__ = "Sent when a schema from a dynamic tenant needs to be synced" dynamic_tenant_post_sync = Signal() -dynamic_tenant_post_sync.__doc__ = "Sent after a tenant has been saved, its schema created and synced" +dynamic_tenant_post_sync.__doc__ = ( + "Sent after a tenant has been saved, its schema created and synced" +) dynamic_tenant_pre_drop = Signal() dynamic_tenant_pre_drop.__doc__ = "Sent when a schema from a dynamic tenant is about to be dropped" @@ -21,5 +23,7 @@ def tenant_delete_callback(sender, instance, **kwargs): if not isinstance(instance, get_tenant_model()): return if instance.auto_drop_schema and schema_exists(instance.schema_name): - dynamic_tenant_pre_drop.send(sender=get_tenant_model(), tenant=instance.serializable_fields()) + dynamic_tenant_pre_drop.send( + sender=get_tenant_model(), tenant=instance.serializable_fields() + ) instance.drop_schema() diff --git a/django_pgschemas/test/cases.py b/django_pgschemas/test/cases.py index 43acbbe..4632897 100644 --- a/django_pgschemas/test/cases.py +++ b/django_pgschemas/test/cases.py @@ -39,7 +39,8 @@ def setUpClass(cls): cls.schema_name in settings.TENANTS ), f"{cls.__name__}.schema_name must be defined to a valid static tenant" assert ( - cls.schema_name not in ["public", "default"] and cls.schema_name != get_clone_reference() + cls.schema_name not in ["public", "default"] + and cls.schema_name != get_clone_reference() ), f"{cls.__name__}.schema_name must be defined to a valid static tenant" super(TestCase, cls).setUpClass() cls.sync_public() diff --git a/django_pgschemas/utils.py b/django_pgschemas/utils.py index 1b96458..41da6b8 100644 --- a/django_pgschemas/utils.py +++ b/django_pgschemas/utils.py @@ -192,7 +192,9 @@ def clone_schema(base_schema_name: str, new_schema_name: str, dry_run: bool = Fa # check if the clone_schema function already exists in the db try: - cursor.execute("SELECT 'public.clone_schema(text, text, public.cloneparms[])'::regprocedure") + cursor.execute( + "SELECT 'public.clone_schema(text, text, public.cloneparms[])'::regprocedure" + ) except ProgrammingError: # pragma: no cover _create_clone_schema_function() transaction.commit() @@ -217,7 +219,9 @@ def create_or_clone_schema(schema_name: str, sync_schema: bool = True, verbosity if schema_exists(schema_name): return False clone_reference = get_clone_reference() - if clone_reference and schema_exists(clone_reference) and not django_is_in_test_mode(): # pragma: no cover + if ( + clone_reference and schema_exists(clone_reference) and not django_is_in_test_mode() + ): # pragma: no cover clone_schema(clone_reference, schema_name) return True return create_schema(schema_name, sync_schema=sync_schema, verbosity=verbosity) diff --git a/docs/conf.py b/docs/conf.py index 7530194..0f2041d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -132,7 +132,9 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). -latex_documents = [(master_doc, "django-pgschemas.tex", "django-pgschemas Documentation", "Lorenzo Peña", "manual")] +latex_documents = [ + (master_doc, "django-pgschemas.tex", "django-pgschemas Documentation", "Lorenzo Peña", "manual") +] # -- Options for manual page output ------------------------------------------ diff --git a/dpgs_sandbox/app_blog/migrations/0001_initial.py b/dpgs_sandbox/app_blog/migrations/0001_initial.py index 0b557cb..6466e71 100644 --- a/dpgs_sandbox/app_blog/migrations/0001_initial.py +++ b/dpgs_sandbox/app_blog/migrations/0001_initial.py @@ -16,11 +16,18 @@ class Migration(migrations.Migration): migrations.CreateModel( name="BlogEntry", fields=[ - ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "id", + models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), ( "user", models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, related_name="blogs", to=settings.AUTH_USER_MODEL + on_delete=django.db.models.deletion.CASCADE, + related_name="blogs", + to=settings.AUTH_USER_MODEL, ), ), ], diff --git a/dpgs_sandbox/app_main/migrations/0001_initial.py b/dpgs_sandbox/app_main/migrations/0001_initial.py index e59d077..0f197b4 100644 --- a/dpgs_sandbox/app_main/migrations/0001_initial.py +++ b/dpgs_sandbox/app_main/migrations/0001_initial.py @@ -12,7 +12,12 @@ class Migration(migrations.Migration): migrations.CreateModel( name="MainData", fields=[ - ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "id", + models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), ], ), ] diff --git a/dpgs_sandbox/app_tenants/migrations/0001_initial.py b/dpgs_sandbox/app_tenants/migrations/0001_initial.py index 2dd21ce..87a7d3f 100644 --- a/dpgs_sandbox/app_tenants/migrations/0001_initial.py +++ b/dpgs_sandbox/app_tenants/migrations/0001_initial.py @@ -8,13 +8,21 @@ class Migration(migrations.Migration): initial = True - dependencies = [("shared_public", "0001_initial"), migrations.swappable_dependency(settings.AUTH_USER_MODEL)] + dependencies = [ + ("shared_public", "0001_initial"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] operations = [ migrations.CreateModel( name="TenantData", fields=[ - ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "id", + models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), ( "catalog", models.ForeignKey( diff --git a/dpgs_sandbox/app_tenants/models.py b/dpgs_sandbox/app_tenants/models.py index b4c8d43..dc82b69 100644 --- a/dpgs_sandbox/app_tenants/models.py +++ b/dpgs_sandbox/app_tenants/models.py @@ -3,7 +3,11 @@ class TenantData(models.Model): - catalog = models.ForeignKey("shared_public.Catalog", on_delete=models.CASCADE, related_name="tenant_objects") - user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name="tenant_objects") + catalog = models.ForeignKey( + "shared_public.Catalog", on_delete=models.CASCADE, related_name="tenant_objects" + ) + user = models.ForeignKey( + get_user_model(), on_delete=models.CASCADE, related_name="tenant_objects" + ) active = models.BooleanField(default=True) diff --git a/dpgs_sandbox/settings.py b/dpgs_sandbox/settings.py index 9386d1c..729c196 100644 --- a/dpgs_sandbox/settings.py +++ b/dpgs_sandbox/settings.py @@ -29,7 +29,12 @@ TENANTS = { "public": { - "APPS": ["shared_public", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.staticfiles"], + "APPS": [ + "shared_public", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.staticfiles", + ], }, "www": { "APPS": ["shared_common", "app_main", "django.contrib.sessions"], diff --git a/dpgs_sandbox/shared_common/migrations/0001_initial.py b/dpgs_sandbox/shared_common/migrations/0001_initial.py index c8f61d3..bc7a316 100644 --- a/dpgs_sandbox/shared_common/migrations/0001_initial.py +++ b/dpgs_sandbox/shared_common/migrations/0001_initial.py @@ -12,9 +12,17 @@ class Migration(migrations.Migration): migrations.CreateModel( name="User", fields=[ - ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "id", + models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), ("password", models.CharField(max_length=128, verbose_name="password")), - ("last_login", models.DateTimeField(blank=True, null=True, verbose_name="last login")), + ( + "last_login", + models.DateTimeField(blank=True, null=True, verbose_name="last login"), + ), ("email", models.EmailField(max_length=254, unique=True)), ("display_name", models.CharField(max_length=50)), ], diff --git a/dpgs_sandbox/shared_public/migrations/0001_initial.py b/dpgs_sandbox/shared_public/migrations/0001_initial.py index f3dbc62..59246ce 100644 --- a/dpgs_sandbox/shared_public/migrations/0001_initial.py +++ b/dpgs_sandbox/shared_public/migrations/0001_initial.py @@ -16,7 +16,12 @@ class Migration(migrations.Migration): migrations.CreateModel( name="Domain", fields=[ - ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "id", + models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), ("domain", models.CharField(db_index=True, max_length=253)), ("folder", models.SlugField(blank=True, max_length=253)), ("is_primary", models.BooleanField(default=True)), @@ -25,15 +30,31 @@ class Migration(migrations.Migration): ), migrations.CreateModel( name="Catalog", - fields=[("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID"))], + fields=[ + ( + "id", + models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ) + ], ), migrations.CreateModel( name="Tenant", fields=[ - ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "id", + models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), ( "schema_name", - models.CharField(max_length=63, unique=True, validators=[django_pgschemas.utils.check_schema_name]), + models.CharField( + max_length=63, + unique=True, + validators=[django_pgschemas.utils.check_schema_name], + ), ), ], options={"abstract": False}, @@ -43,7 +64,9 @@ class Migration(migrations.Migration): model_name="domain", name="tenant", field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, related_name="domains", to="shared_public.Tenant" + on_delete=django.db.models.deletion.CASCADE, + related_name="domains", + to="shared_public.Tenant", ), ), migrations.AlterUniqueTogether(name="domain", unique_together={("domain", "folder")}), diff --git a/dpgs_sandbox/tests/test_apps.py b/dpgs_sandbox/tests/test_apps.py index 263dc43..4cc5c63 100644 --- a/dpgs_sandbox/tests/test_apps.py +++ b/dpgs_sandbox/tests/test_apps.py @@ -3,7 +3,11 @@ from django.core.exceptions import ImproperlyConfigured from django.test import TestCase, override_settings -BASE_DEFAULT = {"TENANT_MODEL": "shared_public.Tenant", "DOMAIN_MODEL": "shared_public.Domain", "URLCONF": ""} +BASE_DEFAULT = { + "TENANT_MODEL": "shared_public.Tenant", + "DOMAIN_MODEL": "shared_public.Domain", + "URLCONF": "", +} class AppConfigTestCase(TestCase): @@ -67,7 +71,9 @@ def test_domains_on_public(self): def test_fallback_domains_on_public(self): with self.assertRaises(ImproperlyConfigured) as ctx: self.app_config._check_public_schema() - self.assertEqual(str(ctx.exception), "TENANTS['public'] cannot contain a 'FALLBACK_DOMAINS' key.") + self.assertEqual( + str(ctx.exception), "TENANTS['public'] cannot contain a 'FALLBACK_DOMAINS' key." + ) @override_settings(TENANTS={}) def test_no_default(self): @@ -91,13 +97,17 @@ def test_other_type_default(self): def test_no_tenant_model_default(self): with self.assertRaises(ImproperlyConfigured) as ctx: self.app_config._check_default_schemas() - self.assertEqual(str(ctx.exception), "TENANTS['default'] must contain a 'TENANT_MODEL' key.") + self.assertEqual( + str(ctx.exception), "TENANTS['default'] must contain a 'TENANT_MODEL' key." + ) @override_settings(TENANTS={"default": {"TENANT_MODEL": ""}}) def test_no_domain_model_default(self): with self.assertRaises(ImproperlyConfigured) as ctx: self.app_config._check_default_schemas() - self.assertEqual(str(ctx.exception), "TENANTS['default'] must contain a 'DOMAIN_MODEL' key.") + self.assertEqual( + str(ctx.exception), "TENANTS['default'] must contain a 'DOMAIN_MODEL' key." + ) @override_settings(TENANTS={"default": {"TENANT_MODEL": None, "DOMAIN_MODEL": None}}) def test_no_urlconf_default(self): @@ -115,17 +125,27 @@ def test_domains_on_default(self): def test_fallback_domains_on_default(self): with self.assertRaises(ImproperlyConfigured) as ctx: self.app_config._check_default_schemas() - self.assertEqual(str(ctx.exception), "TENANTS['default'] cannot contain a 'FALLBACK_DOMAINS' key.") + self.assertEqual( + str(ctx.exception), "TENANTS['default'] cannot contain a 'FALLBACK_DOMAINS' key." + ) def test_repeated_clone_reference(self): - with override_settings(TENANTS={"public": {}, "default": {**BASE_DEFAULT, "CLONE_REFERENCE": "public"}}): + with override_settings( + TENANTS={"public": {}, "default": {**BASE_DEFAULT, "CLONE_REFERENCE": "public"}} + ): with self.assertRaises(ImproperlyConfigured) as ctx: self.app_config._check_default_schemas() - self.assertEqual(str(ctx.exception), "TENANTS['default']['CLONE_REFERENCE'] must be a unique schema name.") + self.assertEqual( + str(ctx.exception), + "TENANTS['default']['CLONE_REFERENCE'] must be a unique schema name.", + ) with override_settings(TENANTS={"default": {**BASE_DEFAULT, "CLONE_REFERENCE": "default"}}): with self.assertRaises(ImproperlyConfigured) as ctx: self.app_config._check_default_schemas() - self.assertEqual(str(ctx.exception), "TENANTS['default']['CLONE_REFERENCE'] must be a unique schema name.") + self.assertEqual( + str(ctx.exception), + "TENANTS['default']['CLONE_REFERENCE'] must be a unique schema name.", + ) def test_valid_schema_name(self): with override_settings(TENANTS={"pg_whatever": {}}): @@ -148,35 +168,51 @@ def test_database_routers(self): with self.assertRaises(ImproperlyConfigured) as ctx: self.app_config._check_complementary_settings() self.assertEqual( - str(ctx.exception), "DATABASE_ROUTERS setting must contain 'django_pgschemas.routers.SyncRouter'." + str(ctx.exception), + "DATABASE_ROUTERS setting must contain 'django_pgschemas.routers.SyncRouter'.", ) def test_extra_search_paths(self): with override_settings( - TENANTS={"public": {}, "default": BASE_DEFAULT, "www": {}}, PGSCHEMAS_EXTRA_SEARCH_PATHS=["public"] + TENANTS={"public": {}, "default": BASE_DEFAULT, "www": {}}, + PGSCHEMAS_EXTRA_SEARCH_PATHS=["public"], ): with self.assertRaises(ImproperlyConfigured) as ctx: self.app_config._check_extra_search_paths() - self.assertEqual(str(ctx.exception), "Do not include 'public' on PGSCHEMAS_EXTRA_SEARCH_PATHS.") + self.assertEqual( + str(ctx.exception), "Do not include 'public' on PGSCHEMAS_EXTRA_SEARCH_PATHS." + ) with override_settings( - TENANTS={"public": {}, "default": BASE_DEFAULT, "www": {}}, PGSCHEMAS_EXTRA_SEARCH_PATHS=["default"] + TENANTS={"public": {}, "default": BASE_DEFAULT, "www": {}}, + PGSCHEMAS_EXTRA_SEARCH_PATHS=["default"], ): with self.assertRaises(ImproperlyConfigured) as ctx: self.app_config._check_extra_search_paths() - self.assertEqual(str(ctx.exception), "Do not include 'default' on PGSCHEMAS_EXTRA_SEARCH_PATHS.") + self.assertEqual( + str(ctx.exception), "Do not include 'default' on PGSCHEMAS_EXTRA_SEARCH_PATHS." + ) with override_settings( - TENANTS={"public": {}, "default": BASE_DEFAULT, "www": {}}, PGSCHEMAS_EXTRA_SEARCH_PATHS=["www"] + TENANTS={"public": {}, "default": BASE_DEFAULT, "www": {}}, + PGSCHEMAS_EXTRA_SEARCH_PATHS=["www"], ): with self.assertRaises(ImproperlyConfigured) as ctx: self.app_config._check_extra_search_paths() - self.assertEqual(str(ctx.exception), "Do not include 'www' on PGSCHEMAS_EXTRA_SEARCH_PATHS.") + self.assertEqual( + str(ctx.exception), "Do not include 'www' on PGSCHEMAS_EXTRA_SEARCH_PATHS." + ) with override_settings( - TENANTS={"public": {}, "default": {**BASE_DEFAULT, "CLONE_REFERENCE": "sample"}, "www": {}}, + TENANTS={ + "public": {}, + "default": {**BASE_DEFAULT, "CLONE_REFERENCE": "sample"}, + "www": {}, + }, PGSCHEMAS_EXTRA_SEARCH_PATHS=["sample"], ): with self.assertRaises(ImproperlyConfigured) as ctx: self.app_config._check_extra_search_paths() - self.assertEqual(str(ctx.exception), "Do not include 'sample' on PGSCHEMAS_EXTRA_SEARCH_PATHS.") + self.assertEqual( + str(ctx.exception), "Do not include 'sample' on PGSCHEMAS_EXTRA_SEARCH_PATHS." + ) @override_settings(TENANTS={"public": {}, "default": BASE_DEFAULT}) def test_all_good_here(self): diff --git a/dpgs_sandbox/tests/test_bug_migrations_in_base_models.py b/dpgs_sandbox/tests/test_bug_migrations_in_base_models.py index 518fea0..a549721 100644 --- a/dpgs_sandbox/tests/test_bug_migrations_in_base_models.py +++ b/dpgs_sandbox/tests/test_bug_migrations_in_base_models.py @@ -82,10 +82,14 @@ def tearDownClass(cls): def test_migrate_with_exclusions(self): # We first unapply a migration with fake so we can reapply it without fake # This should work without errors - management.call_command("migrate", "app_tenants", "0001_initial", fake=True, schemas=["tenant1"], verbosity=0) + management.call_command( + "migrate", "app_tenants", "0001_initial", fake=True, schemas=["tenant1"], verbosity=0 + ) # We then migrate on all schemas except for tenant1, THIS IS THE CASE WE WANT TO TEST # This should work without errors - management.call_command("migrate", all_schemas=True, excluded_schemas=["tenant1"], verbosity=0) + management.call_command( + "migrate", all_schemas=True, excluded_schemas=["tenant1"], verbosity=0 + ) # If we try to global migrate now, we should get a ProgrammingError with self.assertRaises(ProgrammingError): management.call_command("migrate", all_schemas=True, verbosity=0) diff --git a/dpgs_sandbox/tests/test_bug_url_cache.py b/dpgs_sandbox/tests/test_bug_url_cache.py index 9ca1f9b..fd1106b 100644 --- a/dpgs_sandbox/tests/test_bug_url_cache.py +++ b/dpgs_sandbox/tests/test_bug_url_cache.py @@ -22,8 +22,12 @@ def setUpClass(cls): tenant1.save(verbosity=0) tenant2 = TenantModel(schema_name="tenant2") tenant2.save(verbosity=0) - DomainModel.objects.create(tenant=tenant1, domain="everyone.localhost", folder="tenant1", is_primary=True) - DomainModel.objects.create(tenant=tenant2, domain="everyone.localhost", folder="tenant2", is_primary=True) + DomainModel.objects.create( + tenant=tenant1, domain="everyone.localhost", folder="tenant1", is_primary=True + ) + DomainModel.objects.create( + tenant=tenant2, domain="everyone.localhost", folder="tenant2", is_primary=True + ) with tenant1: cls.user1 = User.objects.create(email="user1@localhost", display_name="Admin") with tenant2: @@ -38,12 +42,16 @@ def tearDownClass(cls): def test_bug_in_cached_urls_1(self): self.client1.get("/tenant2/profile/advanced/") # Provoke redirect to login on tenant2 - buggy_response = self.client2.get("/tenant1/profile/advanced/") # Provoke redirect to login on tenant1 + buggy_response = self.client2.get( + "/tenant1/profile/advanced/" + ) # Provoke redirect to login on tenant1 self.assertEqual(buggy_response.status_code, 302) self.assertEqual(buggy_response.url, "/tenant1/login/?next=/tenant1/profile/advanced/") def test_bug_in_cached_urls_2(self): self.client1.get("/tenant1/profile/advanced/") # Provoke redirect to login on tenant1 - buggy_response = self.client2.get("/tenant2/profile/advanced/") # Provoke redirect to login on tenant2 + buggy_response = self.client2.get( + "/tenant2/profile/advanced/" + ) # Provoke redirect to login on tenant2 self.assertEqual(buggy_response.status_code, 302) self.assertEqual(buggy_response.url, "/tenant2/login/?next=/tenant2/profile/advanced/") diff --git a/dpgs_sandbox/tests/test_checks.py b/dpgs_sandbox/tests/test_checks.py index 19dafc8..c3809b7 100644 --- a/dpgs_sandbox/tests/test_checks.py +++ b/dpgs_sandbox/tests/test_checks.py @@ -2,7 +2,12 @@ from django.core import checks from django.test import TestCase, override_settings -from django_pgschemas.checks import check_other_apps, check_principal_apps, check_schema_names, get_user_app +from django_pgschemas.checks import ( + check_other_apps, + check_principal_apps, + check_schema_names, + get_user_app, +) from django_pgschemas.utils import get_tenant_model TenantModel = get_tenant_model() @@ -21,12 +26,21 @@ def test_core_apps_location(self): with override_settings(TENANTS={"public": {"APPS": []}, "default": BASE_DEFAULT}): errors = check_principal_apps(self.app_config) expected_errors = [ - checks.Error("Your tenant app 'shared_public' must be on the 'public' schema.", id="pgschemas.W001"), - checks.Error("Your domain app 'shared_public' must be on the 'public' schema.", id="pgschemas.W001"), + checks.Error( + "Your tenant app 'shared_public' must be on the 'public' schema.", + id="pgschemas.W001", + ), + checks.Error( + "Your domain app 'shared_public' must be on the 'public' schema.", + id="pgschemas.W001", + ), ] self.assertEqual(errors, expected_errors) with override_settings( - TENANTS={"public": {"APPS": ["shared_public"]}, "default": {**BASE_DEFAULT, "APPS": ["shared_public"]}} + TENANTS={ + "public": {"APPS": ["shared_public"]}, + "default": {**BASE_DEFAULT, "APPS": ["shared_public"]}, + } ): errors = check_principal_apps(self.app_config) expected_errors = [ @@ -53,7 +67,9 @@ def test_contenttypes_location(self): ) ] self.assertEqual(errors, expected_errors) - with override_settings(TENANTS={"default": {}, "www": {"APPS": ["django.contrib.contenttypes"]}}): + with override_settings( + TENANTS={"default": {}, "www": {"APPS": ["django.contrib.contenttypes"]}} + ): errors = check_other_apps(self.app_config) expected_errors = [ checks.Warning( @@ -108,7 +124,10 @@ def test_name_clash(self): TenantModel.objects.create(schema_name="public") errors = check_schema_names(self.app_config) expected_errors = [ - checks.Critical("Name clash found between static and dynamic tenants: {'public'}", id="pgschemas.W004"), + checks.Critical( + "Name clash found between static and dynamic tenants: {'public'}", + id="pgschemas.W004", + ), ] self.assertEqual(errors, expected_errors) TenantModel.objects.all().delete() @@ -116,7 +135,9 @@ def test_name_clash(self): TenantModel.objects.create(schema_name="www") errors = check_schema_names(self.app_config) expected_errors = [ - checks.Critical("Name clash found between static and dynamic tenants: {'www'}", id="pgschemas.W004"), + checks.Critical( + "Name clash found between static and dynamic tenants: {'www'}", id="pgschemas.W004" + ), ] self.assertEqual(errors, expected_errors) TenantModel.objects.all().delete() @@ -124,7 +145,10 @@ def test_name_clash(self): TenantModel.objects.create(schema_name="sample") errors = check_schema_names(self.app_config) expected_errors = [ - checks.Critical("Name clash found between static and dynamic tenants: {'sample'}", id="pgschemas.W004"), + checks.Critical( + "Name clash found between static and dynamic tenants: {'sample'}", + id="pgschemas.W004", + ), ] self.assertEqual(errors, expected_errors) TenantModel.objects.all().delete() diff --git a/dpgs_sandbox/tests/test_executors.py b/dpgs_sandbox/tests/test_executors.py index 949184f..8beea12 100644 --- a/dpgs_sandbox/tests/test_executors.py +++ b/dpgs_sandbox/tests/test_executors.py @@ -18,7 +18,9 @@ def setUpClass(cls): for i in range(10): tenant = TenantModel(schema_name=f"tenant{i + 1}") tenant.save(verbosity=0) - DomainModel.objects.create(tenant=tenant, domain=f"tenant{i + 1}.localhost", is_primary=True) + DomainModel.objects.create( + tenant=tenant, domain=f"tenant{i + 1}.localhost", is_primary=True + ) @classmethod def tearDownClass(cls): diff --git a/dpgs_sandbox/tests/test_file_storage.py b/dpgs_sandbox/tests/test_file_storage.py index 216881b..5589f0f 100644 --- a/dpgs_sandbox/tests/test_file_storage.py +++ b/dpgs_sandbox/tests/test_file_storage.py @@ -45,9 +45,13 @@ def test_path_identifier_method_in_tenant(self): del TenantModel.schema_pathname def test_path_identifier_function_in_settings(self): - with override_settings(PGSCHEMAS_PATHNAME_FUNCTION=lambda tenant: tenant.schema_name + "-custom-pathname"): + with override_settings( + PGSCHEMAS_PATHNAME_FUNCTION=lambda tenant: tenant.schema_name + "-custom-pathname" + ): with TenantModel(schema_name="tenant"): - self.assertEquals(self.storage.get_schema_path_identifier(), "tenant-custom-pathname") + self.assertEquals( + self.storage.get_schema_path_identifier(), "tenant-custom-pathname" + ) def test_base_location(self): with SchemaDescriptor.create(schema_name=""): @@ -74,7 +78,9 @@ def test_file_path(self): with SchemaDescriptor.create(schema_name="tenant1"): f = ContentFile("random content") f_name = self.storage.save("test.file", f) - self.assertEqual(os.path.join(self.temp_dir, "tenant1", f_name), self.storage.path(f_name)) + self.assertEqual( + os.path.join(self.temp_dir, "tenant1", f_name), self.storage.path(f_name) + ) self.storage.delete(f_name) self.assertFalse(self.storage.exists("test.file")) @@ -85,7 +91,9 @@ def test_file_save_with_path(self): self.assertTrue(self.storage.exists("path/to")) with self.storage.open("path/to/test.file") as f: self.assertEqual(f.read(), b"file saved with path") - self.assertTrue(os.path.exists(os.path.join(self.temp_dir, "tenant1", "path", "to", "test.file"))) + self.assertTrue( + os.path.exists(os.path.join(self.temp_dir, "tenant1", "path", "to", "test.file")) + ) self.storage.delete("path/to/test.file") self.assertFalse(self.storage.exists("test.file")) diff --git a/dpgs_sandbox/tests/test_log.py b/dpgs_sandbox/tests/test_log.py index c0c6d0c..afb36fd 100644 --- a/dpgs_sandbox/tests/test_log.py +++ b/dpgs_sandbox/tests/test_log.py @@ -15,7 +15,9 @@ class FakeRecord: record = FakeRecord() scf = SchemaContextFilter() - with SchemaDescriptor.create(schema_name="some-tenant", domain_url="some-tenant.some-url.com"): + with SchemaDescriptor.create( + schema_name="some-tenant", domain_url="some-tenant.some-url.com" + ): scf.filter(record) self.assertEqual(record.schema_name, "some-tenant") self.assertEqual(record.domain_url, "some-tenant.some-url.com") diff --git a/dpgs_sandbox/tests/test_middleware_redirection.py b/dpgs_sandbox/tests/test_middleware_redirection.py index 2276224..620a184 100644 --- a/dpgs_sandbox/tests/test_middleware_redirection.py +++ b/dpgs_sandbox/tests/test_middleware_redirection.py @@ -23,7 +23,10 @@ def setUpTestData(cls): DomainModel(domain="tenant1.localhost", tenant=tenant1).save() DomainModel( - domain="tenant1redirect.localhost", tenant=tenant1, is_primary=False, redirect_to_primary=True + domain="tenant1redirect.localhost", + tenant=tenant1, + is_primary=False, + redirect_to_primary=True, ).save() DomainModel( domain="everyone.localhost", @@ -35,7 +38,10 @@ def setUpTestData(cls): DomainModel(domain="everyone.localhost", folder="tenant2", tenant=tenant2).save() DomainModel( - domain="tenant2redirect.localhost", tenant=tenant2, is_primary=False, redirect_to_primary=True + domain="tenant2redirect.localhost", + tenant=tenant2, + is_primary=False, + redirect_to_primary=True, ).save() DomainModel( domain="everyone.localhost", @@ -59,7 +65,9 @@ def test_domain_redirect_to_primary_domain(self): self.assertEqual(response["Location"], "//tenant1.localhost/some/random/url/") def test_folder_redirect_to_primary_domain(self): - request = self.factory.get("/tenant1redirect/some/random/url/", HTTP_HOST="everyone.localhost") + request = self.factory.get( + "/tenant1redirect/some/random/url/", HTTP_HOST="everyone.localhost" + ) response = self.middleware(request) self.assertEqual(response.status_code, 301) self.assertEqual(response.url, "//tenant1.localhost/some/random/url/") @@ -73,7 +81,9 @@ def test_domain_redirect_to_primary_folder(self): self.assertEqual(response["Location"], "//everyone.localhost/tenant2/some/random/url/") def test_folder_redirect_to_primary_folder(self): - request = self.factory.get("/tenant2redirect/some/random/url/", HTTP_HOST="everyone.localhost") + request = self.factory.get( + "/tenant2redirect/some/random/url/", HTTP_HOST="everyone.localhost" + ) response = self.middleware(request) self.assertEqual(response.status_code, 301) self.assertEqual(response.url, "//everyone.localhost/tenant2/some/random/url/") diff --git a/dpgs_sandbox/tests/test_schema_creation_commands.py b/dpgs_sandbox/tests/test_schema_creation_commands.py index f134e6b..bd057fa 100644 --- a/dpgs_sandbox/tests/test_schema_creation_commands.py +++ b/dpgs_sandbox/tests/test_schema_creation_commands.py @@ -74,5 +74,12 @@ def patched_input(*args, **kwargs): with patch("builtins.input", patched_input): with StringIO() as stdout: with StringIO() as stderr: - call_command("cloneschema", "tenant1", "tenant2", verbosity=1, stdout=stdout, stderr=stderr) + call_command( + "cloneschema", + "tenant1", + "tenant2", + verbosity=1, + stdout=stdout, + stderr=stderr, + ) self.assertTrue(utils.schema_exists("tenant2")) diff --git a/dpgs_sandbox/tests/test_tenant_commands.py b/dpgs_sandbox/tests/test_tenant_commands.py index 0f29ee6..abb3334 100644 --- a/dpgs_sandbox/tests/test_tenant_commands.py +++ b/dpgs_sandbox/tests/test_tenant_commands.py @@ -22,11 +22,15 @@ def setUpClass(cls): tenant1 = TenantModel(schema_name="tenant1") tenant1.save(verbosity=0) DomainModel.objects.create(tenant=tenant1, domain="tenant1.localhost", is_primary=True) - DomainModel.objects.create(tenant=tenant1, domain="everyone.localhost", folder="tenant1", is_primary=False) + DomainModel.objects.create( + tenant=tenant1, domain="everyone.localhost", folder="tenant1", is_primary=False + ) tenant2 = TenantModel(schema_name="tenant2") tenant2.save(verbosity=0) DomainModel.objects.create(tenant=tenant2, domain="tenant2.localhost", is_primary=True) - DomainModel.objects.create(tenant=tenant2, domain="everyone.localhost", folder="tenant2", is_primary=False) + DomainModel.objects.create( + tenant=tenant2, domain="everyone.localhost", folder="tenant2", is_primary=False + ) @classmethod def tearDownClass(cls): @@ -100,19 +104,25 @@ def test_specific_schemas(self): def test_nonexisting_schema_excluded(self): with self.assertRaises(CommandError) as ctx: - management.call_command("whowill", all_schemas=True, excluded_schemas=["unknown"], verbosity=0) + management.call_command( + "whowill", all_schemas=True, excluded_schemas=["unknown"], verbosity=0 + ) self.assertEqual(str(ctx.exception), "No schema found for 'unknown' (excluded)") def test_ambiguous_schema_excluded(self): with self.assertRaises(CommandError) as ctx: - management.call_command("whowill", all_schemas=True, excluded_schemas=["tenant"], verbosity=0) + management.call_command( + "whowill", all_schemas=True, excluded_schemas=["tenant"], verbosity=0 + ) self.assertEqual( str(ctx.exception), "More than one tenant found for schema 'tenant' by domain (excluded), please, narrow down the filter", ) def test_existing_schema_excluded_ok(self): - management.call_command("whowill", all_schemas=True, excluded_schemas=["tenant1"], verbosity=0) + management.call_command( + "whowill", all_schemas=True, excluded_schemas=["tenant1"], verbosity=0 + ) def test_interactive_ok(self): def patched_input(*args, **kwargs): @@ -145,10 +155,17 @@ def test_mixed_ok(self): schemas=["public", "sample"], verbosity=0, ) - management.call_command("whowill", all_schemas=True, excluded_schemas=["public", "sample"], verbosity=0) + management.call_command( + "whowill", all_schemas=True, excluded_schemas=["public", "sample"], verbosity=0 + ) management.call_command("whowill", schemas=["everyone.localhost/tenant1"], verbosity=0) management.call_command("whowill", schemas=["tenant1"], verbosity=0) management.call_command( - "whowill", all_schemas=True, excluded_schemas=["everyone.localhost/tenant1"], verbosity=0 + "whowill", + all_schemas=True, + excluded_schemas=["everyone.localhost/tenant1"], + verbosity=0, + ) + management.call_command( + "whowill", all_schemas=True, excluded_schemas=["tenant1"], verbosity=0 ) - management.call_command("whowill", all_schemas=True, excluded_schemas=["tenant1"], verbosity=0) diff --git a/dpgs_sandbox/tests/test_tenants.py b/dpgs_sandbox/tests/test_tenants.py index a4d38cd..294935c 100644 --- a/dpgs_sandbox/tests/test_tenants.py +++ b/dpgs_sandbox/tests/test_tenants.py @@ -189,9 +189,13 @@ def test_synced_tenant_apps(self): self.assertEqual(1, User.objects.count()) # Direct and reverse relations self.assertEqual(User.objects.first(), TenantData.objects.first().user) - self.assertEqual(User.objects.first().tenant_objects.first(), TenantData.objects.first()) + self.assertEqual( + User.objects.first().tenant_objects.first(), TenantData.objects.first() + ) self.assertEqual(Catalog.objects.first(), TenantData.objects.first().catalog) - self.assertEqual(Catalog.objects.first().tenant_objects.first(), TenantData.objects.first()) + self.assertEqual( + Catalog.objects.first().tenant_objects.first(), TenantData.objects.first() + ) # Not expected synced apps with self.assertRaises(ProgrammingError): list(MainData.objects.all()) @@ -228,7 +232,9 @@ def test_primary_domain(self): tenant1.save(verbosity=0) tenant2.save(verbosity=0) domain1 = DomainModel.objects.create(domain="tenant1.localhost", tenant=tenant1) - DomainModel.objects.create(domain="tenant1-other.localhost", tenant=tenant1, is_primary=False) + DomainModel.objects.create( + domain="tenant1-other.localhost", tenant=tenant1, is_primary=False + ) self.assertEqual(tenant1.get_primary_domain(), domain1) self.assertEqual(tenant2.get_primary_domain(), None) for tenant in TenantModel.objects.all(): @@ -238,7 +244,9 @@ def test_domain_string(self): tenant = TenantModel(schema_name="tenant") tenant.save(verbosity=0) domain1 = DomainModel.objects.create(domain="tenant.localhost", tenant=tenant) - domain2 = DomainModel.objects.create(domain="everyone.localhost", folder="tenant", tenant=tenant) + domain2 = DomainModel.objects.create( + domain="everyone.localhost", folder="tenant", tenant=tenant + ) self.assertEqual(str(domain1), "tenant.localhost") self.assertEqual(str(domain2), "everyone.localhost/tenant") tenant.delete(force_drop=True) @@ -247,19 +255,27 @@ def test_domain_absolute_url(self): tenant = TenantModel(schema_name="tenant") tenant.save(verbosity=0) subdomain = DomainModel.objects.create(domain="tenant.localhost", tenant=tenant) - subfolder = DomainModel.objects.create(domain="everyone.localhost", folder="tenant", tenant=tenant) + subfolder = DomainModel.objects.create( + domain="everyone.localhost", folder="tenant", tenant=tenant + ) self.assertEqual(subdomain.absolute_url(""), "//tenant.localhost/") self.assertEqual(subdomain.absolute_url("/some/path/"), "//tenant.localhost/some/path/") self.assertEqual(subdomain.absolute_url("some/path"), "//tenant.localhost/some/path") self.assertEqual(subfolder.absolute_url(""), "//everyone.localhost/tenant/") - self.assertEqual(subfolder.absolute_url("/some/path/"), "//everyone.localhost/tenant/some/path/") - self.assertEqual(subfolder.absolute_url("some/path"), "//everyone.localhost/tenant/some/path") + self.assertEqual( + subfolder.absolute_url("/some/path/"), "//everyone.localhost/tenant/some/path/" + ) + self.assertEqual( + subfolder.absolute_url("some/path"), "//everyone.localhost/tenant/some/path" + ) tenant.delete(force_drop=True) def test_domain_redirect_save(self): tenant = TenantModel(schema_name="tenant") tenant.save(verbosity=0) - domain = DomainModel.objects.create(domain="tenant.localhost", tenant=tenant, redirect_to_primary=True) + domain = DomainModel.objects.create( + domain="tenant.localhost", tenant=tenant, redirect_to_primary=True + ) self.assertTrue(domain.is_primary) self.assertFalse(domain.redirect_to_primary) tenant.delete(force_drop=True) diff --git a/dpgs_sandbox/tests/test_test_client.py b/dpgs_sandbox/tests/test_test_client.py index af177fb..d531c9f 100644 --- a/dpgs_sandbox/tests/test_test_client.py +++ b/dpgs_sandbox/tests/test_test_client.py @@ -27,23 +27,33 @@ def tearDownClass(cls): def test_get(self): request = self.request.get("/not/important/") - self.assertEqual(request.build_absolute_uri("/whatever/"), "http://tenant1.localhost/whatever/") + self.assertEqual( + request.build_absolute_uri("/whatever/"), "http://tenant1.localhost/whatever/" + ) def test_post(self): request = self.request.post("/not/important/") - self.assertEqual(request.build_absolute_uri("/whatever/"), "http://tenant1.localhost/whatever/") + self.assertEqual( + request.build_absolute_uri("/whatever/"), "http://tenant1.localhost/whatever/" + ) def test_put(self): request = self.request.put("/not/important/") - self.assertEqual(request.build_absolute_uri("/whatever/"), "http://tenant1.localhost/whatever/") + self.assertEqual( + request.build_absolute_uri("/whatever/"), "http://tenant1.localhost/whatever/" + ) def test_patch(self): request = self.request.patch("/not/important/") - self.assertEqual(request.build_absolute_uri("/whatever/"), "http://tenant1.localhost/whatever/") + self.assertEqual( + request.build_absolute_uri("/whatever/"), "http://tenant1.localhost/whatever/" + ) def test_delete(self): request = self.request.delete("/not/important/") - self.assertEqual(request.build_absolute_uri("/whatever/"), "http://tenant1.localhost/whatever/") + self.assertEqual( + request.build_absolute_uri("/whatever/"), "http://tenant1.localhost/whatever/" + ) class DynamicTenantClientTestCase(TestCase): diff --git a/dpgs_sandbox/tests/test_urlresolvers.py b/dpgs_sandbox/tests/test_urlresolvers.py index 18feffc..f2efab6 100644 --- a/dpgs_sandbox/tests/test_urlresolvers.py +++ b/dpgs_sandbox/tests/test_urlresolvers.py @@ -39,14 +39,20 @@ def fake_get_response(request): cls.reverser = reverser_func # This comes from app_tenants/urls.py - cls.paths = {"tenant-home": "/", "profile": "/profile/", "advanced-profile": "/profile/advanced/"} + cls.paths = { + "tenant-home": "/", + "profile": "/profile/", + "advanced-profile": "/profile/advanced/", + } for i in range(1, 4): schema_name = f"tenant{i}" tenant = TenantModel(schema_name=schema_name) tenant.save(verbosity=0) DomainModel.objects.create(tenant=tenant, domain=f"{schema_name}.localhost") - DomainModel.objects.create(tenant=tenant, domain="everyone.localhost", folder=schema_name) # primary + DomainModel.objects.create( + tenant=tenant, domain="everyone.localhost", folder=schema_name + ) # primary activate_public() super().setUpClass() @@ -65,11 +71,15 @@ def test_tenant_prefix(self): with tenant: self.assertEqual(tpp.tenant_prefix, tenant.get_primary_domain().folder + "/") # Try with subdomain - tenant.domain_url = f"{tenant.schema_name}.localhost" # This should be set by middleware + tenant.domain_url = ( + f"{tenant.schema_name}.localhost" # This should be set by middleware + ) tenant.folder = "" # This should be set by middleware with tenant: self.assertEqual(tpp.tenant_prefix, "/") - with SchemaDescriptor.create(schema_name="tenant1", domain_url="unexisting-domain.localhost"): + with SchemaDescriptor.create( + schema_name="tenant1", domain_url="unexisting-domain.localhost" + ): self.assertEqual(tpp.tenant_prefix, "/") def test_unprefixed_reverse(self): @@ -82,7 +92,10 @@ def test_prefixed_reverse(self): for tenant in TenantModel.objects.all(): domain = "everyone.localhost" for name, path in self.paths.items(): - self.assertEqual(self.reverser(name, domain, f"/{tenant.schema_name}/"), f"/{tenant.schema_name}{path}") + self.assertEqual( + self.reverser(name, domain, f"/{tenant.schema_name}/"), + f"/{tenant.schema_name}{path}", + ) class URLConfFactoryTestCase(TestCase): @@ -96,7 +109,9 @@ def setUpClass(cls): tenant = TenantModel(schema_name=schema_name) tenant.save(verbosity=0) DomainModel.objects.create(tenant=tenant, domain=f"{schema_name}.localhost") - DomainModel.objects.create(tenant=tenant, domain="everyone.localhost", folder=schema_name) # primary + DomainModel.objects.create( + tenant=tenant, domain="everyone.localhost", folder=schema_name + ) # primary activate_public() super().setUpClass() diff --git a/dpgs_sandbox/tests/test_utils.py b/dpgs_sandbox/tests/test_utils.py index 411c67d..84aeeb7 100644 --- a/dpgs_sandbox/tests/test_utils.py +++ b/dpgs_sandbox/tests/test_utils.py @@ -91,7 +91,9 @@ def test_dynamic_models_exist(self): self.assertFalse(utils.dynamic_models_exist()) def test_create_drop_schema(self): - self.assertFalse(utils.create_schema("public", check_if_exists=True)) # Schema existed already + self.assertFalse( + utils.create_schema("public", check_if_exists=True) + ) # Schema existed already self.assertTrue(utils.schema_exists("public")) # Schema exists self.assertTrue(utils.drop_schema("public")) # Schema was dropped self.assertFalse(utils.drop_schema("public")) # Schema no longer exists diff --git a/dpgs_sandbox/tests/test_whowill_command.py b/dpgs_sandbox/tests/test_whowill_command.py index 3c581df..4807200 100644 --- a/dpgs_sandbox/tests/test_whowill_command.py +++ b/dpgs_sandbox/tests/test_whowill_command.py @@ -33,13 +33,16 @@ def test_all_schemas(self): with StringIO() as buffer: management.call_command("whowill", all_schemas=True, stdout=buffer) self.assertEqual( - self.split_output(buffer), {"public", "sample", "localhost", "blog.localhost", "tenant1.localhost"} + self.split_output(buffer), + {"public", "sample", "localhost", "blog.localhost", "tenant1.localhost"}, ) def test_static_schemas(self): with StringIO() as buffer: management.call_command("whowill", static_schemas=True, stdout=buffer) - self.assertEqual(self.split_output(buffer), {"public", "sample", "localhost", "blog.localhost"}) + self.assertEqual( + self.split_output(buffer), {"public", "sample", "localhost", "blog.localhost"} + ) def test_tenant_like_schemas(self): with StringIO() as buffer: @@ -54,33 +57,48 @@ def test_dynamic_schemas(self): def test_specific_schemas(self): with StringIO() as buffer: management.call_command("whowill", schemas=["www", "blog", "tenant1"], stdout=buffer) - self.assertEqual(self.split_output(buffer), {"localhost", "blog.localhost", "tenant1.localhost"}) + self.assertEqual( + self.split_output(buffer), {"localhost", "blog.localhost", "tenant1.localhost"} + ) # Same test cases as before, but excluding one def test_all_schemas_minus_one(self): with StringIO() as buffer: - management.call_command("whowill", all_schemas=True, excluded_schemas=["blog"], stdout=buffer) - self.assertEqual(self.split_output(buffer), {"public", "sample", "localhost", "tenant1.localhost"}) + management.call_command( + "whowill", all_schemas=True, excluded_schemas=["blog"], stdout=buffer + ) + self.assertEqual( + self.split_output(buffer), {"public", "sample", "localhost", "tenant1.localhost"} + ) def test_static_schemas_minus_one(self): with StringIO() as buffer: - management.call_command("whowill", static_schemas=True, excluded_schemas=["sample"], stdout=buffer) + management.call_command( + "whowill", static_schemas=True, excluded_schemas=["sample"], stdout=buffer + ) self.assertEqual(self.split_output(buffer), {"public", "localhost", "blog.localhost"}) def test_tenant_like_schemas_minus_one(self): with StringIO() as buffer: - management.call_command("whowill", tenant_schemas=True, excluded_schemas=["tenant1"], stdout=buffer) + management.call_command( + "whowill", tenant_schemas=True, excluded_schemas=["tenant1"], stdout=buffer + ) self.assertEqual(self.split_output(buffer), {"sample"}) def test_dynamic_schemas_minus_one(self): with StringIO() as buffer: - management.call_command("whowill", dynamic_schemas=True, excluded_schemas=["public"], stdout=buffer) + management.call_command( + "whowill", dynamic_schemas=True, excluded_schemas=["public"], stdout=buffer + ) self.assertEqual(self.split_output(buffer), {"tenant1.localhost"}) def test_specific_schemas_minus_one(self): with StringIO() as buffer: management.call_command( - "whowill", schemas=["www", "blog", "tenant1"], excluded_schemas=["www"], stdout=buffer + "whowill", + schemas=["www", "blog", "tenant1"], + excluded_schemas=["www"], + stdout=buffer, ) self.assertEqual(self.split_output(buffer), {"blog.localhost", "tenant1.localhost"})