Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static Files Won't Serve #248

Closed
gradedcatfood opened this issue Mar 18, 2019 · 9 comments
Closed

Static Files Won't Serve #248

gradedcatfood opened this issue Mar 18, 2019 · 9 comments

Comments

@gradedcatfood
Copy link

gradedcatfood commented Mar 18, 2019

Following the docs here: https://django-tenants.readthedocs.io/en/latest/files.html and can't get static files to serve

I run python manage.py collectstatic_schemas --schema=tenant1

Static files gets copied to my_proj_path/staticfiles/tenant1 but logging into the admin via the frontend still yields 404 with css/js files. within the tenant1 folder I see the normal django admin folder with the static resources.

Chrome is looking for these files at (example: http://tenant.localhost:8000/static/tenant1/admin/css/base.css) which looks correct and the same as the folder structure where collectstatic_schemas is writing to.

I believe I am following the docs very closely but can't get static files to serve. Any help would be appreciated

The relevant parts of my settings files are:

# This gets added to `INSTALLED_APPS` as described with the docs
SHARED_APPS = (
    'django_tenants',
    'playground.customers',
    'django.contrib.contenttypes',
    'django.contrib.staticfiles',
)


STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

# imported from: `from django.conf.global_settings import STATICFILES_FINDERS`
STATICFILES_FINDERS.insert(0, "django_tenants.staticfiles.finders.TenantFileSystemFinder")

MULTITENANT_STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "tenants/%s/static"),
]

STATICFILES_STORAGE = "django_tenants.staticfiles.storage.TenantStaticFilesStorage"

MULTITENANT_RELATIVE_STATIC_ROOT = ""  # (default: create sub-directory for each tenant)
@gradedcatfood
Copy link
Author

Figured it out, posting here for any future people:

pip install django_compressor and add it to installed apps and then changed STATICFILES_FINDERS to the following

STATICFILES_FINDERS = (
    'django_tenants.staticfiles.finders.TenantFileSystemFinder',
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
    'compressor.finders.CompressorFinder',
)

Now static files serve correctly

@jcass77
Copy link
Contributor

jcass77 commented Mar 18, 2019

Relying on dango_compressor here might yield unexpected results.

I haven’t looked at the code, but assume that package is not tenant-aware. So if it is working now after adding dango_compressor to STATICFILES_FINDERS then it just means that dango_compressor is finding static files to serve somewhere in its search path.

These static files may or may not actually belong to the specific tenant that you are trying to access.

Did you run python manage.py collectstatic_schemas --schema=public first to copy the public static files to the staticfiles directory?

@gradedcatfood
Copy link
Author

That makes sense, thanks for the reply.

Yes, I am first running python manage.py collectstatic_schemas --schema=public then python manage.py collectstatic_schemas --schema=tenant1 but still wont work unless I have dango_compressor added to STATICFILES_FINDERS

@alexandernst
Copy link
Contributor

I'm experiencing the same (or very similar) problem.
I have a public tenant and 2 client tenants (foo and bar).

I have noticed that the assets of the admin aren't loading based on the domain. Instead, they always load the URL of the first tenant that manages to make a GET request to the admin.

So, if I restart Django's server and then open localhost:8000/admin, Django starts generating the proper URLs. But, if then I try to go to foo.com:8000/admin, it doesn't work as the URLs still are being generated as if I was accessing localhost:8000.

Example:

Sitio_administrativo___Sitio_de_administración_de_Django_y_Sitio_administrativo___Sitio_de_administración_de_Django

@alexandernst
Copy link
Contributor

I'll patch this once #252 and #255 get merged.

@tomturner
Copy link
Member

Merged

@alexandernst
Copy link
Contributor

So, I'm working on the fix for this, but... we have a serious problem... The bug is threading-related...

Somehow, somewhere, connection.schema_name get's changed from the correct tenant's schema name to None and then to public a few times, leaving everything ultra-fucked. Running Django's server with --nothreading makes the bug disappear. So, yeah... that will be a huge PITA to debug.

@alexandernst
Copy link
Contributor

Just in order to keep everybody on track on this, this is where the bug happens:

main_service_1        |   File "/usr/local/lib/python3.6/site-packages/django/core/servers/basehttp.py", line 169, in handle
main_service_1        |     self.handle_one_request()
main_service_1        |   File "/usr/local/lib/python3.6/site-packages/django/core/servers/basehttp.py", line 194, in handle_one_request
main_service_1        |     handler.run(self.server.get_app())
main_service_1        |   File "/usr/local/lib/python3.6/wsgiref/handlers.py", line 137, in run
main_service_1        |     self.result = application(self.environ, self.start_response)
main_service_1        |   File "/usr/local/lib/python3.6/site-packages/django/contrib/staticfiles/handlers.py", line 66, in __call__
main_service_1        |     return super().__call__(environ, start_response)
main_service_1        |   File "/usr/local/lib/python3.6/site-packages/django/core/handlers/wsgi.py", line 140, in __call__
main_service_1        |     signals.request_started.send(sender=self.__class__, environ=environ)
main_service_1        |   File "/usr/local/lib/python3.6/site-packages/django/dispatch/dispatcher.py", line 175, in send
main_service_1        |     for receiver in self._live_receivers(sender)
main_service_1        |   File "/usr/local/lib/python3.6/site-packages/django/dispatch/dispatcher.py", line 175, in <listcomp>
main_service_1        |     for receiver in self._live_receivers(sender)
main_service_1        |   File "/usr/local/lib/python3.6/site-packages/django/db/__init__.py", line 50, in reset_queries
main_service_1        |     for conn in connections.all():
main_service_1        |   File "/usr/local/lib/python3.6/site-packages/django/db/utils.py", line 217, in all
main_service_1        |     return [self[alias] for alias in self]
main_service_1        |   File "/usr/local/lib/python3.6/site-packages/django/db/utils.py", line 217, in <listcomp>
main_service_1        |     return [self[alias] for alias in self]
main_service_1        |   File "/usr/local/lib/python3.6/site-packages/django/db/utils.py", line 203, in __getitem__
main_service_1        |     conn = backend.DatabaseWrapper(db, alias)

the last line will create a new instances of the database wrapper, which is our postgresql_backend.base.DatabaseWrapper. And our __init__ method will do set_schema_to_public().

So, basically, if there is only one thread, the middleware will correctly handle each domain, but if there are 2 or more threads, booom.

@diveshkapoor
Copy link

We are Facing the same issue with django version 3.1.5

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants