Issues with question mark in url - django

Hi i am using multiple languages in my app and it is accessible by the url like www.asd.com/reg/?lang=es
Is there some way with which i can remove this "?" from my url and it becomes like www.asd.com/reg/es and also my functionality of multiple language is working properly.
Thanks

When you access url with ?lang=es, you will get a parameter named lang with value es in your view. This is generic HTTP url scheme.
With django, similar thing can be done using appropriate url pattern. For example:
url(r'^reg/(?P<lang>[\w]+)$', 'your_app.views.view_func',),
And in view
def view_func(request, lang=None):
...
# lang will have value passed.
# do your stuff
So when you access www.asd.com/reg/es, parameter lang will have value es in your view.

Django 1.4 has a native support for such URLs - i18n_patterns. As I understand there was an app for this called django-i18nurls but it was included into django core
Also you can use django-localeurl application for this (available at BitBucket). It has a middleware that patches request.path and django's reverse function to achive locale URLs. Perhaps there are alternatives for this app
However this solutions will provide you URLs in format www.asd.com/es/reg/

Related

Django - issue with APPEND_SLASH and POST requests

I am using Django 1.10, and my goal now is to make urls available both with and without trailing slash. To do this, I added slash to all my URLs in the URLConf files, and then set APPEND_SLASH variable value to True (well, this is the default value).
Now the problem is that external POST requests (which I can't control) yield the following error:
You called this URL via POST, but the URL doesn't end in a slash and
you have APPEND_SLASH set. Django can't redirect to the slash URL
while maintaining POST data. Change your form to point to
127.0.0.1:8000/Calendar/AddAccounts/ (note the trailing slash), or set APPEND_SLASH=False in your Django settings.
They mention this in Django doc, and yet after hours of surfing the net, I can't figure out how to address this issue.
I've also come across this question, but the proposed solution doesn't seem acceptable to me. It says I have to make the users call my URLs only with trailing slash. While I know that in other languages (C# for example) it is possible to enable both options
It seems weird to me that you want to support both cases. Ideally you would want to redirect from non slash to slash(or the other way around if you want that) on the server level (nginx/apache/whatever you use) before the request hits Django.
Just choose a strategy and stick to it, so add the trailing slash to your form and never look back. :)
It's important to be consistent. https://www.branded3.com/blog/urls-trailing-slash-seo/
If the urls are used for APIs or the SEO is not important to you, you can consider both with slash and without slash by adding "/?". In django 3.X:
from django.urls import re_path
re_path(r'^query/?$', 'search.views.query'),
re_path(r'^add/?$', 'search.views.add'),
In Restframework routers:
from rest_framework.routers import DefaultRouter
class CustomDefaultRouter(DefaultRouter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.trailing_slash = '/?'
router = CustomDefaultRouter()
router.register('link', ViewSet, basename='link')

Use anchored urls in django (anchored by the id of http objects)

My level in front-end development is pretty low.
Nevertheless, I want to implement the very wide-spread behaviour of having several parts anchored in one single page instead of several separate pages, and refer to these parts in the url.
So instead of having mysite.com/how_to_walk and mysite.com/how_to_run as two different pages and templates, I would like to have one page mysite.com/how_to_do_stuff and then depending on if you want to #walk or #run, refer to the html headers with the id field as suffixes of the url.
I don't really know how to do it with django. I'd like to create only one url dispatcher that - I guess - will look like that:
url(r'^how_to_stuff/#(?P<partId>[-\w]*)', views.how_to, name='how_to')
...and then I have to create a simple view, but how to refer to the id in the render() call, I have no idea.
I found the answer to my own question. The crucial element is that when the client (browser) goes for such an anchored url mysite.com/how_to_do_stuff#run, it sends to the server only the root url mysite.com/how_to_do_stuff and then applies the anchor to it locally. So you need:
A classic, simple url/view/template combination that loads the page mysite.com/how_to_do_stuff when it is asked by the client.
A way to send the client to these anchored pages and reference them for development. I do this through an other url/view couple that redirects the client to the right anchored url.
Below is the result:
In urls.py:
...
url(r'^how_to_do_stuff/(?P<part_id>[-\w]+)', views.how_to_redirect, name='how_to'),
url(r'^how_to_do_stuff', views.how_to)
In views.py:
def how_to_redirect(request, part_id):
return HttpResponseRedirect("/how_to_do_stuff/#"+part_id)
def how_to(request):
return render(request, "GWSite/how_to_do_stuff.html")
And then I refer to these in my templates through:
{% url "how_to" "run"}
From django project website
Take a look at how you they send the num var to views.py
# URLconf
from django.conf.urls import url
urlpatterns = [
url(r'^blog/$', 'blog.views.page'),
url(r'^blog/page(?P<num>[0-9]+)/$', 'blog.views.page'),
]
# View (in blog/views.py)
def page(request, num="1"):
# Output the appropriate page of blog entries, according to num.
...

Django pagination - nice urls

I did pagination in my django projet. Everything works just perfect, but my urls looks terrible, like
host:8000/?page=1
How to create nice urls like
host:8000/page/2/ or host:8000/2/
I use standard Paginator class via ListView
How to do this w/o third party code ?
If you define url pattern like this:
url(r'^/page/(?P<page>\d+)/$', 'myapp.views.list_view'),
then ListView will pass page url keyword into paginator.
Notice:
Each path segment is supposed to be a valid resource, so it's not clear what you will display on /path/ URL.
Django pagination system assumes that webpages will default to using the URL query, so it's recommended to keep it as a URL query and it's more revealing.

How to avoid hardcode URL in Django view

I am working on a simple project on Django. Currently the views that I am implementing always return a hardcode path:
def temp_view(request):
...
return render("app/detail.html")
or
def temp_view_2(request):
...
return redirect("/app/view2")
What I want to do is to get rid of the hardcode URL (for view URL and the template URL). Is there a proper way to do that?
Thanks.
Django provides a few different methods. In a view, the django.core.urlresolvers.reverse() function is most often used. A full discussion of this problem and the options provided by Django is here:
https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-resolution-of-urls

Django1.4: Generic way to set language links in template to work with i18n_patterns?

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!