Does Django automatically URL decode values - python

If I have a django app that accepts data at /XXXX/, will Django pass me (in the view) the url-decoded or url-encoded data?
Mock-up Example:
def view(req, s):
return HttpResponse("Is this URL-decoded? : " + s)
urlpatterns = [
...,
path('<str:s>/', view),
...,
]

The answer is No. Django passed the url "AS IS" and DO NOT encodes or decodes it on its own.
For HttpResponse object, Based on this:
In contrast to HttpRequest objects, which are created automatically by Django, HttpResponse objects are your responsibility. Each view you write is responsible for instantiating, populating, and returning an HttpResponse.
from here, Django sets the parameter values exactly the same as what you provide (you may refer this as "the decoded one"). If you need to decode the url, you can use the code below (Python 3+):
from urllib.parse import unquote
url = "http://..."
urllib.unquote(url)
And for encoding:
from django.utils.http import urlencode
urlencode(url)

Related

Fetching a mobile or desktop template using django middleware [duplicate]

I want to write custom template loader for my Django app which looks for a specific folder based on a key that is part of the request.
Let me get into more details to be clear. Assume that I will be getting a key on every request(which I populate using a middleware).
Example: request.key could be 'india' or 'usa' or 'uk'.
I want my template loader to look for the template "templates/<key>/<template.html>". So when I say {% include "home.html" %}, I want the template loader to load "templates/india/home.html" or "templates/usa/home.html" or "templates/uk/home.html" based on the request.
Is there a way to pass the request object to a custom template loader?
I've been searching for the same solution and, after a couple days of searching, decided to use threading.local(). Simply make the request object global for the duration of the HTTP request processing! Commence rotten tomato throwing from the gallery.
Let me explain:
As of Django 1.8 (according to the development version docs) the "dirs" argument for all template finding functions will be deprecated. (ref)
This means that there are no arguments passed into a custom template loader other than the template name being requested and the list of template directories. If you want to access paramters in the request URL (or even the session information) you'll have to "reach out" into some other storage mechanism.
import threading
_local = threading.local()
class CustomMiddleware:
def process_request(self, request):
_local.request = request
def load_template_source(template_name, template_dirs=None):
if _local.request:
# Get the request URL and work your magic here!
pass
In my case it wasn't the request object (directly) I was after but rather what site (I'm developing a SaaS solution) the template should be rendered for.
To find the template to render Django uses the get_template method which only gets the template_name and optional dirs argument. So you cannot really pass the request there.
However, if you customize your render_to_response function to pass along a dirs argument you should be able to do it.
For example (assuming you are using a RequestContext as most people would):
from django import shortcuts
from django.conf import settings
def render_to_response(template_name, dictionary=None, context_instance=None, content_type=None, dirs):
assert context_instance, 'This method requires a `RequestContext` instance to function'
if not dirs:
dirs = []
dirs.append(os.path.join(settings.BASE_TEMPLATE_DIR, context_instance['request'].key)
return shortcuts.render_to_response(template_name, dictionary, context_instance, content_type, dirs)

Django settings LOGIN_REDIRECT_URL dynamic based on language

Let's say I have LOGIN_REDIRECT_URL='/foo/bar/' and I am on http://127.0.0.1/en/login/ and I login successfully. I will be redirected to http://127.0.0.1/foo/bar/ resulting in losing the language prefix. What should I do in order to maintain the language code in the url? Of course, I want all the other languages codes work and also the default one just like that:
http://127.0.0.1/en/login/ -> http://127.0.0.1/en/foo/bar/
http://127.0.0.1/login/ -> http://127.0.0.1/foo/bar/
http://127.0.0.1/ro/login/ -> http://127.0.0.1/ro/foo/bar/
In that case you can extend Django's LoginView and override the get_success_url method.
Something like this:
from django.contrib.auth.views import LoginView
from django.utils.translation import get_language
class CustomLoginView(LoginView):
def get_success_url(self):
# lang = get your language from the URL (it'd be helpful if you post your URLs in order to help better) or you can use like this:
lang = get_language()
url = '{}/{}/login'.format(yourbaseurl, lang)
return url
Assuming you are using Django auth and the LoginView from the framework, you can parametrize the URL to redirect to after logging in. The LOGIN_REDIRECT_URL is a fallback when it is not specified.
The LoginView attribute is called next. The next url can be set using a parameter from the previous page like ?next=<your_url>. Just don't forget to sanitize it using is_safe_url() from django.utils.http.

Prevent Django's JsonResponse to serialize my string

I have a JSON string that I compute from a Pandas dataframe
aggr.aggregated.to_json(orient='values')
I cannot directly provide aggr.aggregated to a standard Python JSON serializer because it would not follow the orient='values' rules and would do so differently.
I want to serve my own JSON string as a response from a Django view:
return JsonResponse(aggr.aggregated.to_json(orient='values'))
However, in the code above, Django tried to serialize my JSON string.
How can I use JsonResponse exclusively to set the Content-Type header to application/json but not to serialize a string that is already serialized?
There is no benefit in using JsonResponse if you don't want it to encode the JSON for you.
Just use HttpResponse and set the content-type header yourself:
return HttpResponse(
aggr.aggregated.to_json(orient='values'),
content_type='application/json'
)

Django splitting a textfield using template tags

I know that I can use {{value|truncatewords:x}} to truncate a textfield after a given number of words. Is it possible to use the truncated part afterwords if I want to sandwich something in between the text? Like using a string in python if I were to give
>>>string[:2]
>>>something in between
>>>string[2:]
but using template tags because I am iterating through a for loop and cannot pass it through my views?
Thanks
You would need a custom template filter here.
Here's a simple example based on truncatewords() filter implementation:
from django import template
from django.template.defaultfilters import stringfilter
from django.utils.text import Truncator
register = template.Library()
#register.filter(is_safe=True)
#stringfilter
def sandwich(value, args):
length, cutlet = args.split(',')
length = int(length)
truncated_value = Truncator(value).words(length, truncate='')
return ' '.join([truncated_value, cutlet, value[len(truncated_value):].strip()])
Example output:
>>> from django.template import Template, Context
>>> template = Template('{% load filters %}{{ value|sandwich:"2,magic" }}')
>>> context = Context({'value': 'What a wonderful world!'})
>>> template.render(context)
u'What a magic wonderful world!'
Note that Django doesn't allow multiple arguments to be passed in the template filter - that's why they are passed as a comma-separated string and then parsed. See more about the idea here: How do I add multiple arguments to my custom template filter in a django template?
Also, you would probably need to catch possible exceptions in case only one argument passed in the string, the length value cannot be converted to int etc.

How to make a redirect and keep the query string?

I want to make a redirect and keep what is the query string. Something like self.redirect plus the query parameters that was sent. Is that possible?
newurl = '/my/new/route?' + urllib.urlencode(self.request.params)
self.redirect(newurl)
You can fetch the query string to the current request with self.request.query_string; thus you can redirect to a new URL with self.redirect('/new/url?' + self.request.query_string).
Use the RedirectView.
from django.views.generic.base import RedirectView
path('go-to-django/', RedirectView.as_view(url='https://djangoproject.com', query_string=True), name='go-to-django')
This worked for me in Django 2.2. The query string is available as a QueryDict instance request.GET for an HTTP GET and request.POST for an HTTP POST. Convert these to normal dictionaries and then use urlencode.
from django.utils.http import urlencode
query_string = urlencode(request.GET.dict()) # or request.GET.urlencode()
new_url = '/my/new/route' + '?' + query_string
See https://docs.djangoproject.com/en/2.2/ref/request-response/.

Categories