I set the language in my Django app during login with:
user_language = user.profile.language
translation.activate(user_language)
request.session['django_language'] = user_language
request.session[translation.LANGUAGE_SESSION_KEY] = user_language
What happens is that app doesnt load language as I would like to.
If I use in my template {% get_current_language %} I see in my template 'en' or 'pl'. I press 'refresh page' and language changes. It's also strange that if I change a language for one user, it also affects every user.
My question is how do I set the language manually, so it stays constant, and for only specific user. I would like to set the language just after the login.
There is built-in Django middleware that let's users control their own language settings.
From the Django docs (read this link):
If you want to let each individual user specify which language they prefer, then you also need to use the LocaleMiddleware. LocaleMiddleware enables language selection based on data from the request. It customizes content for each user.
You can use get_current_language() once you're sure you've fully implemented everything you need from the Translation docs.
Related
In my app user can create and manage organizations -> projects -> resources and so on. A project instance has a FK to organization, resource has a FK to project.
In example, when user selects an active organization, i want projects view to display only projects that belong to this organization. I see these ways:
Optional argument in all specific urls:
url(r'^(?:organization/(?P<organization_id>\d+)/)?projects/$', 'app.views.projects', name='projects'),
url(r'^(?:organization/(?P<organization_id>\d+)/)?(?:project/(?P<project_id>\d+)/)?resources/$', 'app.views.resources', name='resources'),
Also, this method forces to rewrite all {% url %} tags in templates:
`{% url 'projects' org.id %}`
`{% url 'resources' org.id prj.id %}`
but that's all does not look pretty, especially urls.py.
UPDATE 1
This method forces to pass organization_id in templates everytime. Even if I write middleware to avoid annoying passing it in context,
class FetchFiltersMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
request.project_id = view_kwargs.get('project_id', 0)
request.organization_id = view_kwargs.get('organization_id', 0)
anyway, ALL site urls must be rewritten to contain organization_id, otherwise filtering will be broken. Even if it's not necessary, i.e. in user profile view all sidebar links to projects will be broken.
Storing a current filter in cookie or GET argument
like {% url 'myurl' %}&project={{ project_id }}. bad. ugly.
Storing a last selected organization/project id in cache or session
Easy to use. But not userside-explicit. Also it will generate problems when site opened in multiple tabs with different organizations selected on each tab.
At this moment i see first way looks better, but a lot of rewrite work is required. Is there a better way to do category/section based filtering?
UPDATE 2
If it's not clean yet. The selection of the project, or organization - it's actually switching the current display context. In the context of the selected organization all links should lead to projects in this organization, and so on.
I personally prefer the first way. If you have a relatively simple structure, you can simplify your template by using get_absolute_url. For example, {{ org.get_absolute_url }} could lead to the projects view, {{ prj.get_absolute_url }} to resources etc.
In an older project, I used the session-way, and still regret it, exactly because of the problems with multiple tabs/windows. Using a cookie has the same problems. I think the "ugliness" of the URL configuration in the first way is worth the gain in usability.
I started to play with new i18n_patterns in Django 1.4. Basically, i want to have language links for each of my supported languages on all of my templates headers. I have implemented my header as a separate template that is being included in other templates.
Is there a way to keep my header generic and solve this without passing the current view name or current url in template context? I guess it comes to a question how do i retrieve the current view or url from inside the template in a generic way.
BTW, i discovered that my previous approach with set_lang view to change the active language using the referrer will be broken with url_patterns as after changing the language it will change it back when redirected to the referred view.
Any help figuring out the common approach to set language links in templates to be used with url_patterns in a generic way would be appreciated!
Basically, there are two different approaches to setting the language. You can use i18n_patterns to auto-magically prefix your urls with a language code, or you can use the django.views.i18n.set_language view to change the value of the language code in the user's session (or a cookie, if your project doesn't have session support).
It's worth noting the algorithm LocaleMiddleware uses to determine language:
First, it looks for the language prefix in the requested URL. This is only performed when you are using the i18n_patterns function in your root URLconf. See Internationalization: in URL patterns for more information about the language prefix and how to internationalize URL patterns.
Failing that, it looks for a django_language key in the current user's session.
Failing that, it looks for a cookie.The name of the cookie used is set by the LANGUAGE_COOKIE_NAME setting. (The default name is django_language.)
Failing that, it looks at the Accept-Language HTTP header. This header is sent by your browser and tells the server which language(s) you prefer, in order by priority. Django tries each language in the header until it finds one with available translations.
Failing that, it uses the global LANGUAGE_CODE setting.
The problem you're likely running into is that you can't use set_language to redirect from a url that's already being served with a language prefix, unless you specifically pass a next parameter in the POST data. This is because set_language will default to redirecting to the referrer, which will include the previous language prefix, which LocaleMiddleware will then see and serve the content in the old language (because it looks for a language prefix in the url before checking the django_language session variable).
An example, for clarity:
Your user is on /en/news/article/1000, and clicks on the link which will post 'language=es' to set_language.
set_language sees 'language=es', checks to see if 'es' is available, and then sets the 'django_language' session variable (or cookie) to 'es'
Since you haven't set 'next', it redirects to the value of reqeuest.META['HTTP_REFERRER'], which is /en/news/article/1000
LocaleMiddleware (source) sees the 'en' prefix in the url, and activates the 'en' language and sets request.LANGUAGE_CODE to 'en'
I see two possible solutions:
Write your own set_language view (see the original source here), which will check for a language prefix in the referrer(use django.utils.translation.get_language_from_path), and change it to the prefix for the newly selected language before redirecting back to it.
Use javascript to do the same operation client-side, and set the next POST parameter. Really this is kind of silly; it would probably be simpler to just use javascript to dynamically prepend all urls with the user's preferred language code, and forget about set_language altogether.
It seems that this new set_language view should probably be Django's default behavior. There was a ticket raised, which included a proposed implementation, but didn't really describe the problem and was subsequently closed. I suggest opening a new ticket with a better description of your use case, the problem caused by the existing set_language implementation, and your proposed solution.
Actually, there's no need to fiddle with your view. Django has a handy slice tag, so you can just use {{ request.path|slice:'3:' }} as your link URL. This lops the language code prefix off so the language is set by the set_lang function.
Having had the same problem today with Django 1.7, I devised this solution - not very DRY but it seems to work OK (and all my tests are passing, so...).
Rather than using the builtin set_language view, I copied it and made one tiny adjustment - here's the result:
def set_language(request):
"""
Redirect to a given url while setting the chosen language in the
session or cookie. The url and the language code need to be
specified in the request parameters.
Since this view changes how the user will see the rest of the site, it must
only be accessed as a POST request. If called as a GET request, it will
redirect to the page in the request (the 'next' parameter) without changing
any state.
"""
next = request.POST.get('next', request.GET.get('next'))
if not is_safe_url(url=next, host=request.get_host()):
next = request.META.get('HTTP_REFERER')
if not is_safe_url(url=next, host=request.get_host()):
next = '/'
lang_code = request.POST.get('language', None)
# Start changed part
next = urlparse(next).path # Failsafe when next is take from HTTP_REFERER
# We need to be able to filter out the language prefix from the next URL
current_language = translation.get_language_from_path(next)
translation.activate(current_language)
next_data = resolve(next)
translation.activate(lang_code) # this should ensure we get the right URL for the next page
next = reverse(next_data.view_name, args=next_data.args, kwargs=next_data.kwargs)
# End changed part
response = http.HttpResponseRedirect(next)
if request.method == 'POST':
if lang_code and check_for_language(lang_code):
if hasattr(request, 'session'):
request.session[LANGUAGE_SESSION_KEY] = lang_code
else:
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, lang_code,
max_age=settings.LANGUAGE_COOKIE_AGE,
path=settings.LANGUAGE_COOKIE_PATH,
domain=settings.LANGUAGE_COOKIE_DOMAIN)
return response
To sum it up, I resolve() the view parameters for next, then pass the data to reverse() after activating the new language. Hope this helps.
My apologies for the long delay. Thank you all for your answers.
First of all to comment on the two solution options by Chris:
Neither custom set_language nor javascript are good for the purpose as the whole beauty of url patterns is being SEO friendly.
Furthermore, simply replacing the prefix language in URL cannot be treated as full solution for urlpattern based urls as the whole URL might be translatable too. Ex: /en/profile/ for english and /fr/profil/ for french.
To solve such a problem one needs to capture the viewfunc and the arguments in order to reverse it for different language.
Fortunately for me, my project does not use translatable URLs for now and I took the following approach.
Add django.core.context_processors.request to TEMPLATE_CONTEXT_PROCESSORS so that request to be available in template rendering context.
Use RequestContext in views when rendering your views. This is always the case for me independent of the topic.
Write a quite simple templatetag that requires the context and takes an argument a language code to return the current URL in the given language. It basically makes sure the current URL has valid language-code prefix and simply returns another string that is the same current URL with replaced language-code.
ex:
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def get_current_url_for_lang(context, lang_code):
request=context.get('request',False)
if not request:
return None
request=context['request']
curr_url=request.path
if len(curr_url) < 4 or curr_url[0] != '/' or curr_url[3] != '/':
return curr_url
if context.get('LANGUAGES',False):
codes = []
for code,name in context['LANGUAGES']:
codes.append(code)
curr_langcode = curr_url[1:3]
if lang_code not in codes or curr_langcode not in codes:
return curr_url
changed_url = '/'+lang_code+curr_url[3:]
return changed_url
Furthermore, if one does not like to inject full request into context it will be quite straightforward to write your own context_processor that simply pushes the request.path as current_url_path for instance and use that instead in your templatetag.
Your comments are welcomed as always!
I am new to django and I really like its modular construction so I decided to take advantage of it and put all the separated functionalities each in different app.
Now I need a way to switch on and off this apps by both user and admin.
The user options panel would look like this:
[ ] blog
---------------------
[ ] tagging [BUY]
After checking "blog" option user would get the blog in his profile and after buying and checking "tagging" he would get tagging for the blog.
The admin panel would have an ability to show or hide an app from user panel.
I wonder if:
there is an app which would help me switch on and off an app for specyfic user
and if not -
what would be a proper "architecture" for such django app?
Can it be done dynamically in middleware or should it be done during login (check available apps from database, switch them on, redirect to user home page)?
Any advices for such a task?
Thanks,
Robert
I haven't heard of any such app… But I don't expect it would be too hard to build.
If I were doing it, I would put a permissions check in the entry points to each app. For example:
check_app_permission = lambda request: permissions.check_app_permission("blog", request)
def view_blog(request, …):
check_app_permission(request)
…
(it might even be possible to do some magic and inject this check at the urls.py level…)
Additionally, I would create a has_app_permission template tag:
<div id="sidebar">
{% if has_app_permission "blog" %}
{% include "blog/sidebar_recent_posts.html" %}
{% endif %}
</div>
Or similar.
Finally, there are approximately a million ways you could implement the permission system… And without more information I wouldn't be able to comment. The simplest, though, would be something like this:
class App(Model):
name = CharField(…)
class AppPermission(object):
app = ForeignKey(App)
user = ForiegnKey(User)
def user_has_permission(user, app_name):
return AppPermission.objects.filter(app__name=app_name, user=user).exists()
I would avoid trying to do this with middleware, because if I understand the problem correctly, I expect you will (or, at least, I expect I would) end up spending a bunch of time building a generic framework which, in the end, would just have checks similar to those above.
Does that help? Is there something I can clarify?
I have a 'live settings' app which I use to store certain global site settings. Certain pages of my site reference these settings, although generally they only use 2-3 at a time. I access these settings using a custom template tag like so:
{% settings site_name %}
Each time I use this tag, the model will retrieve the setting from the database, so if I had something like:
{% settings site_name %} {% settings num_articles %}
the database would get queried twice.
What I'd like to do is not hit the database more than once per page for the settings values, if I could get all the settings used on a page and then hit the database to retrieve them all, that would be much better.
I had a look through the documentation and it seems Django has no pre render signal which I could use to update any references to the settings, so does anyone have any suggestions how I could optimise this system? And yes obviously caching is the obvious answer but I'd still like to improve on this for when the page isn't cached.
Agreeing with Daniel that process_template_response is the hook you're looking for. I don't know how many settings you have, but it could also make sense to retrieve all settings once, and store them in some dictionary like structure, so you can access them anytime without hitting the database. You could retrieve them once either per request, or - even if your settings do barely change - when django initializes. You could additionally use django's signals to update your cached settings on delete or update.
If you have a look at django-dbsettings you will see it does a similar thing!
Django 1.3 includes the new TemplateResponse class, which allows you to modify the response later in the rendering process - eg in middleware. This looks like it might do what you want.
If you wish to stick to a scheme similar to what you already have, I'd implement a filter that takes in a list of keys and returns a dict with all the relevant values.
Example filter:
def get_settings(key_list):
# take values from conf.settings. In practice, can be from any source
return dict((k, getattr(settings, k, None)) for k in key_list.split(","))
register.filter("get_settings", get_settings)
Usage:
{# load multiple values with a single call #}
{% with "KEY1,KEY2"|get_settings as settings %}
setting values {{ settings.KEY1 }} and {{ settings.KEY2 }}
{% endwith %}
Note that I used a filter instead of a tag because the with builtin expects a single value before as.
I have a Django app (on Google App Engine) that I wish to internationalize.
settings.py:
USE_I18N = True
LANGUAGE_CODE = 'en'
# Restrict supported languages (and JS media generation)
LANGUAGES = (
('en', 'English'),
('fr', 'French'),
)
MIDDLEWARE_CLASSES = (
'ragendja.middleware.ErrorMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
# i18n
'django.middleware.locale.LocaleMiddleware',
...
I have generated .po and .mo files for my app in locale/fr/LC_MESSAGES (though not at the global level).
I set my browser Accept-Language heading to "fr" and Django ignores it. When I look at request.LANGUAGE_CODE it is always "en".
I can tell the browser is proper because I visit some other i18n-aware site and it returns French.
How do I find what Django thinks is missing about my setup?
I saw this question and it didn't help me.
I am running Django 1.0 using app engine patch 1.0.2.2 on Google App Engine.
There's a certain order that Django does things in terms of i18n.
First it checks LANGUAGE_CODE. It's the site-wide language and if nothing else is set, this is the language the user gets.
Second, since you've added the LocaleMiddleware, it checks if django_language is set in the user session. I would suggest clearing the session information in the DB or creating a completely new user to try with.
Third, it checks if there's a django_language cookie set (or, actually, the name of the cookie is defined by the LANGUAGE_COOKIE_NAME). I would suggest deleting this cookie.
Fourth, it looks for the Accept-Language HTTP header. Which is where your browser setting comes in.
Good luck!
Taken from this page, you can remove the HTTP_ACCEPT_LANGUAGE from the request and fall back on the LocaleMiddleware:
class ForceDefaultLanguageMiddleware(object):
"""
Ignore Accept-Language HTTP headers
This will force the I18N machinery to always choose settings.LANGUAGE_CODE
as the default initial language, unless another one is set via sessions or cookies
Should be installed *before* any middleware that checks request.META['HTTP_ACCEPT_LANGUAGE'],
namely django.middleware.locale.LocaleMiddleware
"""
def process_request(self, request):
if request.META.has_key('HTTP_ACCEPT_LANGUAGE'):
del request.META['HTTP_ACCEPT_LANGUAGE']