Dynamically serve static content based on server name in Django - python

I'm writing a web application in Django that is accessed from multiple domains to the same IP address. The idea is that each domain the application is accessed from will receive unique branding.
So for example, if there were two domains, reseller.com and oem.com, and you went to oem.com, it would take you to to the same website as reseller.com, but with differently themed content (say, sent from /static/oem.com/{files} instead of /static/reseller.com/{files}).
Basically my idea has been to define a custom template tag, that receives the SERVER_NAME as an argument, which would return the location of the content.
Are there any alternatives, or simply easier options?
Edit: I should probably add that I'm using MongoDB for this project, and as such it's more than likely Django's ORM won't be used for the project.
Edit again: More clarification; I'm using nginx.

Not sure how to do simple rewrite rule in nginx. Aside from a template tag (if you are only swapping out static content, then I think a template tag is the way to go), if the sites are gonna be different at all, template-wise, you can handle it by writing a custom template loader.
This allows you to pick what templates you would like to use when you render your page. This method has a graceful way of failing if the loader fails to find a matching template for your specific domain. If it doesn't find a match, it will fall back to your main templates directory. So you could have custom stuff for some domains, and more generic for others.
But to make a decision what to serve based upon a request header, you'll need to make the request available to the loader via _thread_locals, I do this in some middleware:
#custom.middleware.py
try:
from threading import local
except ImportError:
from django.utils._threading_local import local
_thread_locals = local()
def get_current_request():
return getattr(_thread_locals, 'request', None)
class RequestMiddleware():
def process_request(self, request):
_thread_locals.request = request
Next write a template loader (update the path to your middleware):
#custom.loaders.py
from os.path import join
from django.conf import settings
from django.template import TemplateDoesNotExist
from path.to.middleware import get_current_request
def load_template_source(template_name, template_dirs=None):
request = get_current_request()
host = request.get_host()
path_to_template_dir = None
for site in settings.SITE_TEMPLATE_FOLDERS:
if site[0] == host:
path_to_template_dir = site[1]
break
if path_to_template_dir:
try:
filepath = join(path_to_template_dir, template_name)
file = open(filepath)
try:
return (file.read(), filepath)
finally:
file.close()
except IOError:
pass
raise TemplateDoesNotExist(template_name)
and lastly update your settings file with three things 1) add the template loader (make sure its listed first) 2) add the middleware 3) and then add a new variable SITE_TEMPLATE_FOLDERS with a tuple of tuples containing domains and paths to template folders:
#settings.py
.....
TEMPLATE_LOADERS = (
'custom.loaders.load_template_source',
'django.template.loaders.filesystem.load_template_source',
'django.template.loaders.app_directories.load_template_source',
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'domain.middleware.SessionMiddleware',
'custom.middleware.RequestMiddleware',
)
SITE_TEMPLATE_FOLDERS = (
('mydomain.com', '/path/to/templates'),
('myotherdomain.com', '/path/to/other/templates')
)
...
Seems like a lot, but now you can easily add a new domain via your settings file.

You're looking for the "sites" framework.

For example, Apache has mod_rewrite that you can use to rewrite URLs:
RewriteCond %{HTTP_REFERER} ^www.domain1.com$ [NC]
RewriteRule /static/[^/]+ /static/domain1/$1 [L]
RewriteCond %{HTTP_REFERER} ^www.domain2.com$ [NC]
RewriteRule /static/[^/]+ /static/domain2/$1 [L]
(this is untested)
other servers also have similar functionality.
Just make sure your django application emits static urls that are site-independent and can be correctly rewritten.

Related

redirection from old urls to new urls

Hello everyone I want to redirect URLs like this
from
https://www.example.co.uk/pages/terms-conditions
to
https://www.example.co.uk/terms-conditions
I developed the updated site using the Django framework and now want to redirect old site URLs to new URLs and not losing traffic. what is the best way of doing…?
New site is not published it is on local machine
my view.py
my urls.py
I guess this is not best practice but you can just redirect the user to the right url if he hits the other one in your views.py like:
def old_view(request):
return redirect('https://www.example.co.uk/terms-conditions')
def new_view(request):
return render(request, "your_app/conditions.html")
with urls.py like:
path('pages/terms-conditions/', views.old_view, name='old_view')
path('terms-conditions/', views.new_view, name='new_view')
Assuming you want to remove the application name from the urls. In your project's urls.py NOT your applications urls.py. You should have a line similar to the following:
path('pages/', include('pages.urls', namespace='pages')),
replace the 'pages/' with just ''.
Note: Using the pages/ allows you to provide segregation between your applications to ensure the urls do not conflict

Access to media files only for authenticated users

I have project in django 1.0.4 - yes I know it is old.
I want to use the lack of access to media (audio) files for users who are not logged in.
After making changes to nginx, logged in users also have no access.
I tried with view and url function - no result
my nginx settings:
location /media/content/audio/ {
deny all;
}
my function and url
#login_required
def protected_serve(request, path, document_root=None, show_indexes=False):
if not request.user.is_authenticated:
raise Http404()
else:
return serve(request, path, document_root, show_indexes)
urlpatterns += patterns('',
(r'^media/content/audio/(?P<path>.*)$', protected_serve),
)
You're very close to having the whole puzzle put together. There are two things you need to do:
Configure NGINX that you do want to be able to serve data from a particular folder, but that said folder isn't public. The authorization to send files from a folder will come from the application behind NGINX, not from external requests to NGINX.
Have your django app send the kind of response to NGINX that NGINX understands to mean "serve this file from the protected area in 1"
The way you achieve the first goal is to use the config directive "internal"
Achieving the second goal is to use the HTTP response header "X-Accel-Redirect" as #ralf states in the comments above.
Here is a blog post on the subject: https://clubhouse.io/developer-how-to/how-to-use-internal-redirects-in-nginx/
A Python project to help you achieve the same goal: https://pypi.org/project/django-transfer/
NGINX Docs: https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/

Get the subdomain in a Django view? using django-hosts library

I'm making a project in django. The aplication have to show diferent images and languages depending on the subdomain. For example: www.mysite.com will be the default page in english, but if the subdomain is mx.mysite.com the language must be spanish.
Whith django-hosts I can make that each subdomain redirect to a diferent django-app and I works well. The problem is that I want to make only one app to all diferent subdomains, only changing the language.
I think that is possible get the subdomain in a view and render the template with the language depending on the subdomain. But I don't know how to do it, please help.
THIS IS MY DJANGO HOSTS.PY where the host 'www' and 'mx' redirect to the same app 'mysite' but must be in diferent languages.
from django.conf import settings
from django_hosts import patterns, host
host_patterns = patterns('',
host(r'www', 'mysite.urls', name='www'),
host(r'help', 'help.urls', name='help'),
host(r'mx', 'mysite.urls', name='mx'),
)
General solution
Here's a general solution, which works with any supported language code and without the django-hosts package.
Write a custom middleware, which checks the subdomain, and then sets the session variable LANGUAGE_SESSION_KEY (or the cookie defined by LANGUAGE_COOKIE_NAME in settings.py. The default name is django_language.) with the language from the subdomain.
The custom middleware: (you can save it as middleware.py in the same folder as settings.py)
from django.conf import settings
from django.utils.translation import LANGUAGE_SESSION_KEY
class SetLanguageFromSubdomain:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
host = request.get_host()
# Parse language code from the host
lang = host.split('.')[0]
if lang == 'www':
lang = 'en'
# If requested language is in the languages supported by django,
# language_session_key session variable is populated with the
# supported language code.
for django_language in settings.LANGUAGES:
if django_language[0] == lang or django_language[0].endswith('-%s' % lang):
request.session[LANGUAGE_SESSION_KEY] = django_language[0]
break
response = self.get_response(request)
return response
Install this middleware between session and locale middlewares in settings.py
'django.contrib.sessions.middleware.SessionMiddleware',
#...
# put the middleware here (change projectname to your project's name)
'projectname.middleware.SetLanguageFromSubdomain',
#...
'django.middleware.locale.LocaleMiddleware',
More information about how django discovers language preference: https://docs.djangoproject.com/en/2.2/topics/i18n/translation/#how-django-discovers-language-preference
Languages supported by django: https://github.com/django/django/blob/master/django/conf/global_settings.py
More information about how to make a Django project translatable: https://docs.djangoproject.com/en/2.2/topics/i18n/translation/
More information about writing a custom middleware: https://docs.djangoproject.com/en/2.2/topics/http/middleware/

How to change static and media directory for Django project that have 2 apps

This is a continuation for my previous question How to get hostname or IP in settings.py so that i can use it to decide which app's urls to use
I am making a django project that have 2 apps. When you open www.webName.co.id it will use urls.py from app A, but when you open webName.co.uk it will use urls.py from app B.
Basically my project will have 1 backend, multiple frontend urls and views, and each apps have their own models.
But i am having problem with how Django decide which static folder and media root it is using. I want to change which static folder and media it is using depending on the www. Basically i want to use static and media folder in app A when you enter webName.co.uk, and another static and media folder in app B when you enter webName.co.uk
The way i am going to do this is adding codes in middleware to change the settings for MEDIA_ROOT and STATICFILES_DIRS, but the documentation said i should not do this. How do i achieve what i wanted? thanks
class SimpleMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before the view (and later middleware) are called.
# sets to show Taiwan or Indo version
# sets the timezone too
the_host = request.get_host()
http_host = request.META['HTTP_HOST']
if(the_host == 'http://www.webName.com.tw' or the_host == 'http://webName.com.tw'):
#translation.activate('zh_TW')
# i am planning to change the SETTINGS in runtime here
request.urlconf = 'webName.urls_taiwan'
elif (the_host == 'http://www.webName.id' or the_host == 'http://webName.id'):
#translation.activate('in_ID')
# i am planning to change the SETTINGS in runtime here
request.urlconf = 'webName.urls_indonesia'
response = self.get_response(request)
# Code to be executed for each request/response after the view is called.
return response
When creating static folders name them like this:
appA/static/appA/*
appB/static/appB/*
When you use your static files in your html template you can:
{% static 'appA/something.js' %}
and
{% static 'appB/something.js' %}
I have not worked with 'media' files, but I assume you can do basically the same thing.
This is not the right approach.
Django includes support for collecting static files from different apps into a central location, via the collectstatic management command. You should run that on deploy.
For the media files, you should simply upload them to a central location.

Where do i put template that i want to render

The following is the code that i got from a website for basic-authentication using "django-piston".
Here i have login.html page which i wana show,where do i put that and what is this realm keyword in the following code.................
from django.conf.urls.defaults import *
from piston.resource import Resource
from piston.authentication import HttpBasicAuthentication
from mysite.myapp.api.handlers import BlogpostHandler
auth = HttpBasicAuthentication(realm="Django Piston Example")
blogpost_handler = Resource(BlogpostHandler, authentication=auth)
urlpatterns = patterns('',
url(r'^blogpost/(?P<post_slug>[^/]+)/', blogpost_handler),
url(r'^blogposts/', blogpost_handler),
)
Django searches for template directories in a number of places, depending on your template-loader settings, but the most basic way of specifying template directories is by using the TEMPLATE_DIRS setting.
Look in your django-piston app where the templates are. Eg:
/path/to/site-packgages/django-piston/templates/template.html
If you have the default loader settings applied in your project, than you can override the templates by creating at template with the same path and filename but then in your project directory:
/path/to/projectdir/django-piston/templates/template.html
A realm is a community or territory over which a sovereign rules; a kingdom. 2. A field, sphere, or province: the realm of science.
Reading the Piston source code I think a realm is just a name that is passed to the authentication handler and probably only used by that handler to inform the user about the area he is about to receive access to: "Your about to receive access to [realm]".
Disclaimer: The realm guess is my best guess. I'm not a Piston user.

Categories

Resources