Django i18n language switcher not working on deploy at subdirectory - django

I've got a site with two languages (it will get more in time) and a little dropdown menu to switch languages. It works as desired/expected in the development server. Urls look like this when visiting the site:
localhost:8000/en/home
localhost:8000/pl/home
The django project gets deployed on a server (Apache w/ mod-wsgi) at a subdirectory location, lets say:
mysite.com/django
Everything works as expected, even the admin site, etc, underneath that subdirectory, however, my little menu to change languages doesn't work anymore. When I change the language, the page reloads, but on the same language that it was on when I tried to change it. I can go back and forth between the languages by manually changing the url in the browser and all the pages work as expected with the translations; it's just the dropdown button that doesn't work anymore.
mysite.com/django/en/home
mysite.com/django/pl/home
At first, I thought it was my button (pretty standard off a tutorial):
<form action="{% url 'set_language' %}" method="post" class="form-inline">{% csrf_token %}
<div class="form-group mx-sm-3 mb-0">
<input type="hidden" name="text" value="{{ redirect_to }}" class="form-control form-control-sm">
<select name="language" id="langselect" onchange="this.form.submit()">
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<option value="{{ language.code }}" {% if language.code == LANGUAGE_CODE %} selected {% endif %}>
{{ language.name_local }} ({{ language.code }})
</option>
{% endfor %}
</select>
</div>
</form>
but actually if I change the Apache config to deploy the site at server root mysite.com (just to try to isolate the problem) the dropdown button, translations, and everything else work as expected. I think this means that somewhere the i18n is generating a bogus link, i.e. it's not implementing the '/django' subdirectory prefix the right way.
I feel like this should be an easy fix —a setting to toggle somewhere— but I'm stumped after spending most of the day googling and reading documentation. I'm happy to share snippets of settings, etc, but honestly, I'm not sure what's relevant.
Could someone please point me in the right direction with a keyword or a suggestion how to solve this?
Edit 1:
I tried to add the prefix to value in the <input> tag. So it read value="/django{{ redirect_to }}" but that didn't work either.
Edit 2 & 4:
Using my switcher button with the network inspector open reveals that the setlang function is calling the wrong url.
mysite.com/django/django/i18n/setlang
I still don't know how to fix it, but I'm fairly certain that's the problem.
If I change the apache config to deploy at mysite.com the redirect is via mysite.com/i18n/setlang, so it seems like I need to somehow control how the django subdirectory prefix is interpreted. But HOW?!?!?
Edit 3:
The switcher successfully changes the cookie, despite not reloading to the proper language.

So after much frustration, I have a working solution.
I had to first create a custom filter that would cut the language code from the url. Since the existing code successfully updated the language preference cookie, and since navigating to any path without a language code automatically / successfully appends the language path, I could redirect with my switcher to a path without the language code and the site will successfully render according to the user's browser preferences or cookie. Cutting out the language code from the url seemed to be the most reasonable way for now.
Add a set up infrastructure for custom template tags according to the documentaiton here. I added a templatetags directory to my app, and inside an __init__.py file and a <myapp>_extras.py file
In my case, I needed to cut the second element from the full url path, so my filter function in <myapp>_extras.py. The file looks like this:
from django import template
register = template.Library()
#register.filter
def custom_redir_lang(url_fullpath):
ls_urls = url_fullpath.split('/')
del ls_urls[1]
return '/'.join(ls_urls)
Then I change the value of the input tag in my template from "{{ redirect_to }}" to "{{ request.get_full_path|custom_redir_lang }}"
Now the site redirects properly underneath the subdirectory prefix.

Related

Django template nested include passing variables

I use django template index.html to render the frontpage. It includes another template to create a link icon. This template url_icon.html includes another template icon.html. When passing the arguments down the way, I face with an error. How to fix it?
index.html
.
.
.
{% include "url_icon.html" with name="return" url="/" %}
.
.
.
url_icon.html
{% include "icon.html" with icon={{ name }} %}
icon.html
<img src="/static/images/{{ name }}.png" />
Causing an error:
Could not parse the remainder: '{{' from '{{'
it looks like there are a few things you can do to improve/fix this. Addressing #1 and #2 should fix your issue. I've also added suggestions for best practices that would probably require refactoring (#3, #4).
It looks like you need to remove the curly-braces from name inside the {% include %} tag. Context variables can be used inside tags without extra syntax.
url_icon.html:
{% include "icon.html" with icon=name %}
icon.html will have access to name since you're not using the only keyword when updating its context, so your code might appear to work at first ({% include %} documentation). However, it looks like your intention is to refer to it as icon.
Use the variable icon in instead of name
icon.html:
<img src="/static/images/{{ icon }}.png" />
Optional suggestion: Use Django's staticfiles system
Try using the {% static %} tag for your icon. This will help make deployment easier, especially if you use a separate CDN from your webserver. There's lots of literature on how to set up staticfiles for Django projects in production, it's a large topic, but you'll be able to approach it more easily if you use the {% static %} tag from the beginning.
Optional suggestion: Django's URL routing system
Your route in index.html is hard-coded to be "/". Django has a powerful URL referencing system to leverage. If you've defined the root URL / using Django too, you can refer to it by name. Docs: {% url %}, and for the back-end, reverse().

Django along with bootstrap navbar - distiinguishing link to current page

I use bootstrap for my django project.
I have a navbar like this:
[home][gallery][user]
How can I change the CSS properties of a specific navbar button to correspond to a currently opened page?
For example, if I am on the home page, the home button would be highlighted.
Solution 1
I usually have the navbar template gets included in all templates, each template should define what the page is this.
For example
# nav.html
<div class="..">
<div class="..">My Nav</div>
<ul class="..">
Home
Settings
</ul>
</div>
Then in each template you specify which should be active. something like this
# home.html
{% include "yourtemplatedir/nav.html" with active='home' %}
# settings.html
{% include "yourtemplatedir/nav.html" with active='settings' %}
Solution 2
Using context processor will make it sometimes easy
def get_current_path(request):
return {
'current_path': request.get_full_path()
}
In your template you can use {{ current_path }} to determine which nav item should be active.
You can also enhance the context processor code in order to check the prefix of the url and set the active_page variable automatically. so you don't need to set it with each include (In case you include the nav.html in your base always). However it really hard to have exceptions in here.

Showing 'cancel' on login page to return user to where they were (using django.contrib.auth)

We are using the #login_required decorator so that users see a login page if they try to access a url for which they need to be authenticated.
We want to show a 'cancel' button on the login page, which should return the user to whichever page they were on when they tried to access the url (by clicking a link etc - we don't need to deal with them manually entering the url).
At the moment our login.html looks for a request parameter 'login_cancel_url' and if present uses that (otherwise the home page).
However, this means we have to manually pass this parameter (set to the url of the current page) whenever we show a link or button that leads to an 'authentication required' url.
Is there a more elegant way to do this?
Thanks, Martin
Well you can try get the referrer header from the request but as far as I am aware, it's browser dependent and is not very reliable so the way you are doing it is probably best. You could try make life easier by creating template tags to avoid having to rewrite the return URL manually.
You are easily able to get the current URL from django's request object on any page, so instead of setting it manually on the link, you could write a snippet of html:
link_to_login.html
<!-- You should probably get /login/ using the {% url ... %} template tag -->
<a href="/login/?login_cancel_url={{ request.path|urlencode }}">
Login Page</a>
and use the {% include "link_to_login.html"%} template tag.
Alternatively, If the text needs to be different depending on the link you can instead create an inclusion template tag:
templatetags/extra_auth_tags.py
#register.inclusion_tag('templates/extra_auth_tags/login_link.html')
def login_link(context, text=None):
return {
'text':text
}
templates/extra_auth_tags/login_link.html
<!-- You should probably get /login/ using the {% url ... %} template tag -->
<a href="/login/?login_cancel_url={{ request.path|urlencode }}">
{% if text %}
{{ text }}
{% else %}
Some Default Text
{% endif %}
</a>
and then call it in your templates as {% login_link text="Check you messages" %}. Be aware that keyword arguments for inclusion tags are only supported in the django dev version so you might need to write the template tag by hand.

Create a PDF or Printable version of change_list.html in Django Admin

I would like to add a tool link at the top of my admin change_list.html, which I have already done, and have this link basically be able to produce some sort of printable document version of my models data based off of my current filter settings. Basically a print button in the admin change_list.html.
so far I have overridden the change_list.html to create the link, and I notice that this
<li>
<a href="{{ choice.query_string|iriencode }}" class="addlink">
{% blocktrans %}View PDF{% endblocktrans %}
</a>
</li>
gives you a link based on these choices.. but Im kinda lost as to the best/easiest way to do this..
Sorry, new to Django. I know I can use ReportLabs to generate pdfs, but not a 100% on how to get the filtered data from change_list to it.
A bit late, but for those who might be searching "in the future" like me, this might be helpful: http://djangosnippets.org/snippets/1842/

How can I make sure that the urls work the same in built-in web-server and Apache

The situation is:
I have Apache with mod_python on windows xp and my django project is not in the document root.
The Django project location is defined with the tag. The django.root ist also defined there.
All the urls work fine in the built-in server but unfortunately not in Apache. In some urls, especially the ones not pointing to the admin do not work. The django.root part gets cut off.
How can I avoid this ?
One solution could be to set the django-project into Apache's document-root. Are there other solutions?
Django will use the django.root part correctly if you compose links in the template files with {% url %} tags and by calling reverse() in your HTTPResponseRedirects() calls.
Its value is stored in HttpRequest - request.META['SCRIPT_NAME'] and you can use it also in templates with:
{% if user.is_staff %}
<li>
Administration
</li>
{% endif %}