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

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/

Related

Using django subdomain and it says localhost does not belong to the domain example.com

I'm using the Django Package django-subdomain, and I don't think I've configured it correctly.
Now that I'm trying to load data from the DB I'm getting this error in the terminal
The host localhost:8000 does not belong to the domain example.com, unable to identify the subdomain for this request
I don't have any references to example.com in my project.
Here's my subdomain config:
ROOT_URLCONF = 'creativeflow.urls'
# A dictionary of urlconf module paths, keyed by their subdomain.
SUBDOMAIN_URLCONFS = {
None: ROOT_URLCONF, # no subdomain, e.g. ``example.com``
'www': ROOT_URLCONF,
'blog': ROOT_URLCONF + '.blogs',
}
SITE_ID = 1
Middleware:
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'subdomains.middleware.SubdomainURLRoutingMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
My urls:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^posts/(?P<year>\d{4})/(?P<months>\d{2}|\w{3})/(?P<day>\d{2})',
BlogListView.as_view(paginate_by=25), name="blog-list-view"),
]
I'm not certain of what other config I need to let me use/develop with subdomains. What do I need to change so I can access the BlogListView at http://localhost:8000/posts/2016/07/09 ? Or better via the actual subdomain of blog.creativeflow.com/posts/2016/07/09 ? I suspect the latter is just a simple change to the windows equivalent of /etc/hosts/.
SITE = 1 will correspond to the default example.com set by django.contrib.site.
django.contrib.sites registers a post_migrate signal handler which creates a default site named example.com with the domain example.com. This site will also be created after Django creates the test database.
This is stored in the DB so there's no way to set this purely in config.
To set it in the DB, follow the steps here:
>>> from django.contrib.sites.models import Site
>>> one = Site.objects.all()[0]
>>> one.domain = 'myveryspecialdomain.com'
>>> one.name = 'My Special Site Name'
>>> one.save()
Then you can run python manage.py dumpdata sites which produces a JSON of the data you've just loaded. Then later load it using django-admin loaddata fixture [fixture ...]. Otherwise you can set it via the admin interface, under the Site app.
This will display as example.org before it's fixed:
Change these:
That should fix the issue.
Why have you set SITE_ID = 1?
From the Django Docs for django.contrib.site:
django.contrib.sites registers a post_migrate signal handler which creates a default site named example.com with the domain example.com. This site will also be created after Django creates the test database.
You need to specify the correct SITE_ID for your current site.
What do I need to change so I can access the BlogListView at
http://localhost:8000/posts/2016/07/09 ? Or better via the actual
subdomain of blog.creativeflow.com
I've set up my linode with subdomains pointed to different apps and sites. I did this by configuring NGINX webserver and uWSGI web app daemon in "emperor" mode.
To test django-subdomain locally this question on adding subdomains to localhost may help.

Django i18n_patterns - How to prevent prefixing of non-active languages

In my django settings.py file, I have six active languages:
LANGUAGES = (
('de', gettext_noop('German')),
('en', gettext_noop('English')),
('es', gettext_noop('Spanish')),
('fr', gettext_noop('French')),
('nl', gettext_noop('Dutch')),
('pt', gettext_noop('Portuguese')),
)
These pages work great when using i18n patterns:
http://exmaple.com/de/main
http://exmaple.com/nl/main
etc...
However, if you search my site in Google, you'll see multiple pages for language prefixes. Some for languages that I don't support. Some of which don't even exist:
http://examble.com/ch/main
http://exmaple.com/zz/main
etc..
I'm not sure why these pages are getting indexed. They aren't in my sitemap. However, Django does serve them as pages.
Question, what is the best way to modify i18n_patterns so that it only allows valid, active languages as defined in settings.py? I would like all other 2 char prefixes to 404.
The best solution(I know) is to use solid-i18n-urls.
Install the package:
pip install solid_i18n
Modify settings a little bit:
# Default language, that will be used for requests without language prefix
LANGUAGE_CODE = 'en'
# supported languages
LANGUAGES = (
('en', 'English'),
('ru', 'Russian'),
)
# enable django translation
USE_I18N = True
#Add SolidLocaleMiddleware instead of LocaleMiddleware to MIDDLEWARE_CLASSES:
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'solid_i18n.middleware.SolidLocaleMiddleware',
'django.middleware.common.CommonMiddleware',
)
Use solid_i18n_patterns instead of i18n_patterns
from django.conf.urls import patterns, include, url
from solid_i18n.urls import solid_i18n_patterns
urlpatterns = solid_i18n_patterns('',
url(r'^main/$', 'about.view', name='about'),
)
Now, if you go to example.com/en/main it works fine as en is specified in your languages linst but if you go to example.com/ch/main it throws a 404 page not found error.
This is not a direct solution, but may help you or point you to a good solution.
What about a custom middleware ?
Here we have 2 options:
A middleware where you check the country of the user and redirect to your allowed countries (If the user country is not allowed, you can redirect to a custom url or show a 404 error)
A middleware where you check the url-path of the client, so you will have /country_code/url and you can do as above, if the path is not allowed you can redirect to a custom url or show a 404 error
Little examples:
1. A middleware to check country
pygeoIP is used in the example to get country by ip
import pygeoip
class CountryMiddleware:
def process_request(self, request):
allowed_countries = ['GB','ES', 'FR'] # Add your allowed countries
gi = pygeoip.GeoIP('/usr/share/GeoIP/GeoIP.dat', pygeoip.MEMORY_CACHE)
ip = request.META.get('REMOTE_ADDR')
user_country = gi.country_code_by_addr(ip)
if user_country not in allowed_countries:
return HttpResponse... # Here you decide what to do if the url is not allowed
# Show 404 error
# or Redirect to other page...
2. A middleware to check url
class DomainMiddleware:
def process_request(self, request):
"""Parse out the subdomain from the request"""
# You especify full path or root paths
# If you specify '/en' as allowed paths, '/en/whatever' are allowed
ALLOWED_PATHS = ['/en','/fr', '/es'] # You add here allowed paths'
path = request.path
can_access = False
for url in ALLOWED_PATHS: # Find if the path is an allowed url
if url in path: # If any allowed url is in path
can_access=True
break
if not can_access: # If user url is not allowed
return HttpResponse... # Here you decide what to do if the url is not allowed
# Show 404 error
# or Redirect to other page...
If you decide to use any of this options you have to remember:
You need to add middleware files in the path your_project/middleware/middlewarefile.py
You need to add middlewares on your settings.py:
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
# etc.....
'yourproject.middleware.domainmiddleware.DomainMiddleware',
)
The code I showed here is not completed or tested, it's an orientation to help you find a good soluiton

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.

How to set django middleware in settings file

Hi all i am trying to add middleware to my app and the app is located in this location
myapp/sitemanager/middleware/__init__.py
myapp/sitemanager/middleware/redirects.py
what is the right way to declare it in my settings.py file.i currently have it set this way
MIDDLEWARE_CLASSES = ("sitemanager.middleware.redirects.SiteDetectionMiddleware")
but i keep geting this error
ImproperlyConfigured: Error importing middleware sitemanager.middleware.redirects: "cannot import name Address"
Thanks
You'd make sure that your Python path is properly configured.
Suppose (in your project) your directory structure resembling this:
Django 1.4
/mysite
/mysite/mysite #defult settings.py gonna here...
/mysite/apps
/mysite/apps/__init__.py
/mysite/apps/main
/mysite/apps/main/__init__.py
/mysite/apps/main/models.py
/mysite/apps/main/views.py
/mysite/apps/main/middleware/__init__.py
/mysite/apps/main/middleware/log.py
It is my simple midlleware logger exemple (in log.py):
from django.http import HttpRequest
import datetime
class Logger(object):
def process_request(self, request):
f = open('/tmp/log.txt', 'w')
f.write(str(datetime.datetime.now()))
Note that my custom middleware class (in log.py) is under my middleware python package, that is under main app.
So, you should put in your settings.py something like this:
import sys
sys.path.append(MY_PROJECT_ROOT)
and in middleware tuple:
MIDDLEWARE_CLASSES = (
'...',
'apps.main.middleware.log.Logger',
)

Dynamically serve static content based on server name in Django

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.

Categories

Resources