Flask-Babel Multiple Language URL Routing - python

I am creating a multi-language site (EN and FR) and I need it to toggle back and forth on click if the user so chooses. I am using Flask-Babel and the translations and toggle are working correctly on click, however, I need the URLs to be translated as well. I have currently wrapped my URL routes like so, with both the English and French URLs:
#main.route('/accueil')
#main.route('/home')
def index():
return render('index.html', {})
#main.route('/a-propos-de-nous')
#main.route('/about-us')
def about():
return render('about.html', {})
The rest of the code that is grabbing the language and toggling is as follows:
app = Flask(__name__, static_folder=settings.STATIC_ROOT)
main = Blueprint('main', __name__, url_prefix='/language/<lang_code>')
#app.url_defaults
def set_language_code(endpoint, values):
if 'lang_code' in values or not session['lang_code']:
return
if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
values['lang_code'] = session['lang_code']
#app.url_value_preprocessor
def get_lang_code(endpoint, values):
if values is not None:
session['lang_code'] = values.pop('lang_code', None)
#app.before_request
def ensure_lang_support():
lang_code = session['lang_code']
if lang_code and lang_code not in app.config['SUPPORTED_LANGUAGES'].keys():
return abort(404)
#babel.localeselector
def get_locale():
if session.get('lang_code') is None:
session['lang_code'] = request.accept_languages.best_match(app.config['SUPPORTED_LANGUAGES'].keys())
return session['lang_code']
The template looks like this where the user clicks on the link to change languages:
{% if session['lang_code']=='en' %}
{% set new_lang_code='fr' %}
{% else %}
{% set new_lang_code='en' %}
{% endif %}
<li>{{ _('Fr') }}</li>
As I have little experience with Python/Flask...I am struggling with the best way to switch to the translated URL. How would I go about doing this? Any information would be appreciated. Thanks in advance.

I have found a solution! I had to add endpoints to the URL routes like so:
#main.route('accueil', endpoint="index_fr")
#main.route('home', endpoint="index_en")
def index():
return render('index.html', {})
#main.route('a-propos-de-nous', endpoint="about_fr")
#main.route('about-us', endpoint="about_en")
def about():
return render('about.html', {})
This allowed me to use Babel to translate the URL endpoints like it did for the rest of the text, and grab the correct URL ending along with the language code from the session. The toggle works like this now:
{% if session['lang_code']=='en' %}
{% set new_lang_code='fr' %}
{% else %}
{% set new_lang_code='en' %}
{% endif %}
<li>{{ _('Fr') }}</li>

Related

How to change language of flask babel on a button click?

I have the function in Flask, which returns the website in english. Moreover, i want to be able to use german in the website at a button push from html. How can I change the language at a button push from english to german and from german to english? Also, is it possible to use the function get_locale only at call, not running automatically?
#babel.localeselector
def get_locale():
return 'en'
At first You need to store the address at the main app file:
#app.route('/language=<language>')
def set_language(language=None):
session['language'] = language
return redirect(url_for('home'))
Than You need to change to the same addess to get_locale function in main app file:
#babel.localeselector
def get_locale():
if request.args.get('language'):
session['language'] = request.args.get('language')
return session.get('language', 'en')
To access current language from template:
app.config['LANGUAGES'] = {
'en': 'English',
'ge': 'German',
}
app.secret_key = "super secret key"
#app.context_processor
def inject_conf_var():
return dict(AVAILABLE_LANGUAGES=app.config['LANGUAGES'], CURRENT_LANGUAGE=session.get('language', request.accept_languages.best_match(app.config['LANGUAGES'].keys())))
Here the template file:
{% for language in AVAILABLE_LANGUAGES.items() %}
{% if CURRENT_LANGUAGE == language[0] %}
{{ language[1] }}
{% else %}
<a href="{{ url_for('set_language', language=language[0]) }}" >{{ language[1] }}</a>
{% endif %}
{% endfor %}

Flask with Jinja issue with pagination after the first page

I have created a dropdown menu to search for parts by project number from an SQL database. The first page loads with the correct parts but any pages subsequent give the error of:
TypeError: show_compound() missing 1 required positional argument: 'search_string'
From what I've seen online it seems I may need to use *args or pass the search_string to the template but I am unsure of how to use *args or where to insert the search_string value on the template.
#parts_database.route('/searchcompound', methods=['GET', 'POST'])
#login_required
def compounds_search():
form = ProjectSearch(request.form)
if form.validate_on_submit():
search_string = form.select.data.project_number
return show_compound(search_string)
return render_template('parts_database/search_compounds.html', form=form)
#parts_database.route('/showcompound', methods=['GET'])
#login_required
def show_compound(search_string):
page = request.args.get('page', 1, type=int)
pagination = PartsTable.query.filter_by(project_number=search_string).order_by(PartsTable.part_number).paginate(page, per_page=15, error_out=False)
compound = pagination.items
page_10 = pagination.next_num+9
if page_10 > pagination.pages:
pageincrement = pagination.pages
else:
pageincrement = page_10
page_decrement = page - 10
if page_decrement < 1:
page_decrement = 1
return render_template('parts_database/showpartstable.html', compound=compound, pagination=pagination, pageincrement=pageincrement, page_decrement=page_decrement)
template :
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% import "_macros.html" as macros %}
{% block title %}Amos{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Parts</h1>
{% include 'parts_database/_showpartstable.html' %}
{% if pagination %}
<div class="pagination">
{{ macros.pagination_widget(page_decrement, pageincrement, pagination, '.show_compound') }}
</div>
{% endif %}
</div>
{% endblock %}
If a view takes an argument, you must include that variable in the route.
In your case, you are missing search_string in your show_compound route definition. Try something like this:
#parts_database.route('/showcompound/<search_string>', methods=['GET'])
#login_required
def show_compound(search_string):
(...)
EDIT:
Also, I'd sugest to redirect instead of calling another view's function.
Replace this:
if form.validate_on_submit():
search_string = form.select.data.project_number
return show_compound(search_string)
with this:
You'll have to import redirect before that:
from flask import redirect # (Add this at the top)
(...)
if form.validate_on_submit():
search_string = form.select.data.project_number
return redirect(url_for('parts_database.show_compound', search_string=search_string))

Django - Show/hide urls in base html dependant on user group?

I want to try and show or hide urls in my navigation page based on user group.
currently i am adding to the top of every view and sending through a value to the template and checking against that, but this doesnt seem too efficient, also if a view does not require auth and that value is not set, will that break the template?
is there a better way to do this?
like a global.py where i could check and set then use it in any template? or something completely different altogether?
view.py
Authorised_user = ''
if request.user.is_authenticated():
Authorised_user = 'IT'
#login_required
def index(request):
return render(request, 'service/index.html', {
'Authorised': Authorised_user,
})
template.html
{% if Authorised == 'IT' or Authorised =='Netwworks' %}
Link
{% endif %}
i do have the user groups in django admin
Based on Get user group in a template
Create user_tags.py / group_tags.py at an appropriate place. e.g. auth_extra/templatetags/user_tags.py
from django import template
register = template.Library()
#register.filter('in_group')
def in_group(user, group_name):
return user.groups.filter(name=group_name).exists()
Then in your template:
{% load user_tags %}
{% if request.user|in_group:"IT"%}
IT only link
{% endif %}
{% if request.user|in_group:"Netwworks"%}
Netwworks only link
{% endif %}
Easiest way around this for me was https://stackoverflow.com/a/17087532/8326187.
Here you don't have to create a custom template tag.
{% if request.user.groups.all.0.name == "groupname" %}
...
{% endif %}
You need to create context_processors.py and create a function say
def foo():
Authorised_user = ''
if request.user.is_authenticated():
Authorised_user = 'IT'
Then in setttings
TEMPLATE_CONTEXT_PROCESSORS = ("path_to_context_processor.foo")
this way you can use foo variable in all the templates without explicitly defining in all the views.
You can also have a look here:https://rubayeet.wordpress.com/2009/10/31/django-how-to-make-a-variable-available-in-all-templates/

Python Flask cannot get element from form

Im having trouble getting anything from the shown HTML form
I always get "ValueError: View function did not return a response"
Can somebody help me out here please? I have tried every variation of request.get that I can find on the web. Also if I specify my form should use post it uses get anyway - anybody know why this is?
Im new to flask so forgive my ignorance!
Thanks in advance.
The python file (routes.py)
from flask import Flask, render_template, request
import os
app = Flask(__name__)
musicpath = os.listdir(r"C:\Users\Oscar\Music\iTunes\iTunes Media\Music")
lsize = str(len(musicpath))
looper = len(musicpath)
#app.route('/')
def home():
return render_template('home.html', lsize=20, looper=looper, musicpath=musicpath)
#app.route('/pop', methods=['POST', 'GET'])
def pop():
if request.method == "GET":
text = request.args.get('som')
return text
#Have tried every variation of request.get
#app.route('/about')
def about():
name = "Hello!"
return render_template('about.html', name=name)
if __name__ == '__main__':
app.run(debug=True)
The html file (home.html)
{% extends "layout.html" %}
{% block content %}
<div class="jumbo">
<h2>A Music app!<h2>
</div>
<div>
{% if lsize %}
<form action="/pop">
<select id="som" size="20">
{% for i in range(looper):%}
<option value="{{i}}">{{ musicpath[i] }}</option>
{% endfor %}
</select>
</form>
{% endif %}
</div>
Select,
{% endblock %}
You don't have a name attribute on your select element. That is the attribute that browsers use to send information in forms; without it no data will be sent.
Note also that your pop handler does not do anything if the method is POST, even though you explicitly say you accept that method.

Django - how to tell if a template fragment is already cached?

I am using Django's Template Fragment Caching so in a template.html file
{% extends 'base.html' %}
{% load cache %}
{% block content %}
{% cache 500 "myCacheKey" %}
My html here...
{% endcache %}
{% endblock %}
This is working fine - I can see it's getting cached and hit but the view is doing something expensive to provide data to this view and thats getting called every time.
In views.py
def index(request)
data = api.getSomeExpensiveData()
return render_to_response('template.html', {'data':data} )
So how do I tell if the cache is avail before the call to api.getSomeExpensiveData()?
I can't use cache.get('myCacheKey') as the cache isn't found - does it use some naming scheme and if so can I either use something like
cache.get(cache.getTemplateFragmentKey("myCacheKey"))
or
cache.getTemplateFragment("myCacheKey")
If you do not use that data in your view, something as simple as this might work:
def index(request)
get_data = api.getSomeExpensiveData
return render_to_response('template.html', {'get_data':get_data} )
In template
{% block content %}
{% cache 500 "myCacheKey" %}
{{ get_data.something }}
Or maybe
{% for something in get_data %}
{% endfor %}
{% endcache %}
{% endblock %}
Django template automatically calls all callable objects.
EDIT:
If you need to use get_data more than once in your template you'll need some wrapper. Something similar to this:
def index(request)
class get_data(object):
data = False
def __call__(self):
if not self.data:
self.data = api.getSomeExpensiveData()
return self.data
return render_to_response('template.html', {'get_data':get_data()} )
I found this SO - How do I access template cache?
And adapted it to
from django.utils.hashcompat import md5_constructor
from django.utils.http import urlquote
from django.core.cache import cache
def hasFragmentCache(key, variables = []):
hash = md5_constructor(u':'.join([urlquote(var) for var in variables]))
return cache.has_key(cache_key)
Edit - I've accepted skirmantas answer as whilst this does exactly as asked its the better approach as then the template and view are more loosly coupled. Using this method you need to know the name of each cache fragment and whats used where. A designer moves things around and it would fall over.

Categories