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
Related
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/
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.
I have been making a Django app within an existing Django project. All I wanted to test is whether my newly built app sends the correct HttpResponse text object to my browser from the view I wrote in views.pay of my app. The urls have been properly configured and also has been included in the project. Also, the project has my app added to its installed apps.
My urls.py in the project has :
url(r'^loginapi/', include('loginapi.urls')),
My urls.py in the app has :
urlpatterns = patterns('',
url(r'^$', views.authenticateRemoteUser, name = 'loginAPIUrl'),
)
My installed apps in the project settings.py files has :
INSTALLED_APPS += (
'core',
'people',
'places',
'media',
#Just added loginapi app to test my HTTP post request
'loginapi',
)
My View is as follows :
from django.http import HttpResponse
def authenticateRemoteUser(request):
return HttpResponse("hello world")
Additional Data ::
Middleware are as follows :
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
What I am getting on browser is as follows:
Page not found (404)
Request Method: GET
Request URL: http://www.example.com/loginapi/
(Can't post image without 10 reputations)
Note : www.example.com is used for local usage and has been added to my hosts for my server ip. So, not a problem.
Note2 : My app is deployed in AWS EC2 but I don't think it is the reason.
Note3 : Do not bother about my url as I intend to build a larger app once this step has been crossed.
Try moving url(r'^loginapi/', include('loginapi.urls')), to the very top of the patterns in your root urls.py, to make sure it isn't being shadowed by something else.
As an example, consider the following urlpatterns:
urlpatterns = [
url(r'^', views.catch_all),
url(r'^loginapi/', include('loginapi.urls'))
]
A request to /loginapi/ will be matched by the regex ^', and handled by the catch_all view instead of the intended loginapi view. You can fix the problem by moving the catch_all url pattern below the other ones.
I've got an issue with translations not working on Django 1.6. I've added this to my settings.py:
LANGUAGE_CODE = 'en-us'
ugettext = lambda s: s
LANGUAGES = (
('en', ugettext('English')),
('de', ugettext('German')),
)
Also added middlewares:
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
as well as to my *.py files whenever I'm using a string which shall be l10nd:
from django.utils.translation import ugettext_lazy as _
My templates start with:
{% extends "base.html" %}
{% load i18n %}
and inside the template I used the trans placeholder. E.g.
<h1>{% trans "Register a tank" %}</h1>
I have provided translations in locale/de/LC_MESSAGES/django.po:
msgid "Register a tank"
msgstr "Einen neuen Tank anmelden"
My browser is set to request German content first:
Browser settings
What did I miss?
P.S. The project I'm currently fuzzy around is hosted on GitHub: https://github.com/frlan/blankspot
Add LOCALE_PATHS to settings.py and set it as below:
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
LOCALE_PATHS = (
os.path.join(BASE_DIR, 'locale'),
)
Note that LOCALE_PATHS must be a tuple (look at the comma at the end of the path).
Now based on LOCALE_PATHS, the locale folder should be in the root of your project.
And be sure that you run the commands django-admin.py makemessages -l de and django-admin.py compilemessages from the root of your project.
djPrj
|
+---> djPrj
|
+---> djApp
|
+---> locale
|
+---> templates
Also rearrange your MIDDLEWARE_CLASSES to be LocaleMiddleware after SessionMiddleware and before CommonMiddleware as mentioned here:
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
Restart your service (python manage.py runserver) and check again.
Just to ensure that your localization is applied to your Django admin page with the default django.mo file of Django, do the following test:
First in main urls.py of project replace patterns with i18n_patterns:
from django.conf.urls.i18n import i18n_patterns
urlpatterns = i18n_patterns('',
url(r'^admin/', include(admin.site.urls)),
# ...
)
Now go to the admin page with a de prefix, like: http://127.0.0.1:8000/de/admin/
And the admin page should be shown in German.
OK, are you able to see the admin page of Django in German?
Also check your view with the de prefix too.
According to your project code, some sentences are not in trans blocks. Put them as:
{% trans "your sentence" %}
Also you must use ugettext_lazy instead of ugettext in your code for views and models (Read here and here.)
Replace this:
from django.utils.translation import ugettext as _
with:
from django.utils.translation import ugettext_lazy as _
And now everything will work.
In my case, I used en-gb as the parameter to run
django-admin.py makemessages -l en-gb
Instead, it should be en_GB.
django-admin.py makemessages -l en_GB
Please set translated string in django.po and then use python manage.py compilemessages
for e.g
#: path/to/python/module.py:23
msgid "Welcome to my site."
msgstr "put appropriate translated string here"
Suggestion-: You can use django-rosetta package to add translated string from UI interface. It is easy to add T-string from django-admin. https://github.com/mbi/django-rosetta
Check the cookies and the session -- according to How Django Discovers Language Preference, the process is this:
language prefix in the URL, when using i18n_patterns in URLconf
_language key in the current user's session
django_language cookie (or as specified by settings.LANGUAGE_COOKIE_NAME)
Accept-Language HTTP header (this is what your browser setting sends)
settings.LANGUAGE_CODE
Since your browser settings are set to prefer 'de', I suspect the LocaleMiddleware must decide otherwise in one of the previous steps 1. - 3.
You need to enable the LocaleMiddleware in your settings, to tell Django to do language detection based on the browser-settings. Changing your language preferences effectly sets the Accept-Language header. You might need to check in an incognito window, because other means of language detection have a higher priority, such as the user's session and the django_language cookie.
I'm learning django with this excellent book "Practical Django Projects". I was able to follow the steps of the book perfectly, but now I stumbled upon the following problem.
When creating the page http://127.0.0.1:8000/first-page/ as image below:
I found the following error page:
Slightly different of this one, announced on book (page 16):
Then, I openned the file urls.py and added to it the following line of code:
(r'', include ('django.contrib.flatpages.urls')),
And the code looks like this:
I saved urls.py and accessed http://127.0.0.1:8000/first-page/ again, finding the same error message above (nothing changed), when, according to the book, I should now have found this other error page:
As a result, after I create the directory and file default.html for templates, as follows:
And have changed the TEMPLATE_DIRS settings.py file like this:
Again, the http://127.0.0.1:8000/first-page/ shows that same error message when, this time should show this:
I have repeated several times the steps.
I'm using Python 2.6 and django 1.1 (the same version of the book).
Does anyone have any idea about what I'm doing wrong?
Thank you in advance for any help.
It all seems so simple and straightforward. And yet, does not work!
Here my settings file code:
# Django settings for cms project.
DEBUG = True
TEMPLATE_DEBUG = DEBUG
ADMINS = (
# ('Your Name', 'your_email#domain.com'),
)
MANAGERS = ADMINS
DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
DATABASE_NAME = 'C:\Projetos\cms\cms.db' # Or path to database file if using sqlite3.
DATABASE_USER = '' # Not used with sqlite3.
DATABASE_PASSWORD = '' # Not used with sqlite3.
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
TIME_ZONE = 'America/Chicago'
LANGUAGE_CODE = 'en-us'
SITE_ID = 1
USE_I18N = True
MEDIA_ROOT = ''
MEDIA_URL = ''
ADMIN_MEDIA_PREFIX = '/media/'
# Make this unique, and don't share it with anybody.
SECRET_KEY = ''
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.load_template_source',
'django.template.loaders.app_directories.load_template_source',
# 'django.template.loaders.eggs.load_template_source',
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
)
ROOT_URLCONF = 'cms.urls'
TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
'C:/Projetos/templates/',
)
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.admin',
'django.contrib.flatpages',
# Uncomment the next line to enable the admin:
# 'django.contrib.admin',
)
The answer to all errors was this (thanks, mongoose_za):
"On page 15 [of the book "Practical Django Projects"] you must make sure you edit the example.com site instead of adding a new site. You might have noticed in your settings.py the SITE_ID = 1. If you add a new site 127.0.0.1:8000 then that will have a SITE_ID of 2, and in the following section flatpage views filter by default on the current site which is 1."
So, I just change SITE_ID = 2 (instead of 1) in settings.py, because I had added a new site 127.0.0.1:8000.
After that, I changed the path to my templates folder according to instructions on the book to:
TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
'C:/Projetos/templates/',
)
I found the expected result (the blank page "My first page")
I also have followed this advice:
"On p13 is where the first deviation from the older django kicks in. In your settings.py add 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware', (don't forget the comma) to your MIDDLEWARE_CLASSES."
All this was found on this great blog:
http://blog.haydon.id.au/2008/08/2-your-first-django-site-simple-cms.html
Firstly: the template does not exist error is because you dont have the template
C:/Projects/templates/flatpages/default.html (also in your screenshot your template dir is spelled "Projetos")
Secondly: you shouldnt need to add any the url rule for flatpages. Flatpages works via middleware:
'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
so within your settings file:
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
'django.middleware.common.CommonMiddleware',
#'debug_toolbar.middleware.DebugToolbarMiddleware',
)
The TemplateDoesNotExist error is not on your template, but on 500.html. There's an error in your code in your template or view and Django is responding with a 500 error. However, in development, you should have DEBUG = True, in which case Django will show a stacktrace instead of trying to load 500.html.
So the simple solution is to make DEBUG = True so you can see the real error and correct that. However, you'll still need 500.html when you finally launch, so you might as well go ahead and create that too.