I need django i18n to work like this:
site.com/lang/ set the session language to lang, writes it to the cookie and redirects user to site.com. If it's a first-time visitor and prefix is not specified, default lang is shown.
Sorry for such a basic question,example of urls.py and middlewares maybe would be extremely appreciated.
Django comes with a pretty similar solution to what you are trying to do. It's called The set_language Redirect view. The difference is that it expects the language as a POST parameter. You might want to consider using this versus a custom solution.
If that's not what you're looking for, you could write your own Redirect View, that would set the language to lang and redirect to site.com
class SetLanguageView(RedirectView):
url = reverse('home')
def get(self, request, *args, **kwargs):
response = super(self, SetLanguageView).get(request, *args, **kwargs)
lang = kwargs.get('lang')
if lang:
# To set the language for this session
request.session[settings.LANGUAGE_SESSION_KEY] = lang
# To set it as a cookie
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, lang,
max_age=settings.LANGUAGE_COOKIE_AGE,
path=settings.LANGUAGE_COOKIE_PATH,
domain=settings.LANGUAGE_COOKIE_DOMAIN)
return response
And in urls.py you would have something like
urlpatterns = patterns('',
url(r'^(?P<lang>\w+)/$', RedirectView.as_view(), name='lang_redirect'),
Related
I am trying to implement Django basic authentication for all of the views in my views.py file. Although I can add the authentication code snippet in every view, but it will not be easy to apply this to upcoming views. Is there any way that every view in my views.py will automatically check for the authentication?
views.py
def mgmt_home(request):
##############################################################
# This code is repetitive
##############################################################
if request.user.is_anonymous:
return redirect("/login")
##############################################################
test_name = Test.objects.all()[0].test_name
metadata = {
"test_name": test_name,
}
return render(request, "mgmt_home.html", metadata)
Is there any way where I can avoid this repetitive code in all of my views?
you can use 'login_required()' decorator or 'LoginRequiredMixin' class from django authentication.
https://docs.djangoproject.com/en/3.1/topics/auth/default/
How to specify the login_required redirect url in django?
You have 2 options:
from django.contrib.auth.decorators import login_required
You can add this #login_required() decorator to your every view and it will automatically redirect a user to the login page (or whatever page you want to send the user to) any time your user is not logged in.
This option, in your case, I would not recommend, as this might be an overkill and not required for your simple problem. The solution is to create a custom Middleware and add your code to it, and then, of course, add the Middleware to the Settings.py file. This way, each time your views run, your Middlewares will run prior to that. In fact, that's the purpose of Middlewares. They are designed to reduce redundancies and problems exactly such as yours.
Create a middleware.py file anywhere on your python path. Add the below codes to your created middleware.py file
from django.http import HttpResponseRedirect
from django.urls import reverse_lazy
def redirect_to_login():
return HttpResponseRedirect(reverse_lazy('users:login'))
class AuthPageProtectionMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
if request.user.is_authenticated:
if not request.user.is_admin:
return redirect_to_login()
else:
return redirect_to_login()
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
NOTE
You can replace the redirection URL with your application-specific one.
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.
I am trying to implement a dynamic subdomain and urls system in my django project.
Generally, each user has its domain, eg. myusername.example.com
User may define dynamic URLs, for example:
myusername.example.com/something1
myusername.example.com/something2
myusername2.example.com/something1
myusername2.example.com/something2
But I have also my website running on example.com, with urls like example.com/contact, example.com/about-us and so on.
I want to all of these URLs to point to my custom view (class based) where I do some DB queries and return dynamic content. this somethin1/something2 part is fully dynamic, and there may be anything . defined by user.
I've got something like this:
urls.py
from web.views import HomeView, ContactView
urlpatterns = [
path('admin/', admin.site.urls),
path('contact', ContactView.as_view()),
path('', HomeView.as_view()),
re_path('.*', HomeView.as_view())
]
web.views.py
class HomeView(TemplateView):
template_name = 'home.html'
def dispatch(self, request, *args, **kwargs):
SERVICE_DOMAIN = settings.DOMAIN
http_host = str(request.META.get('HTTP_HOST'))
if SERVICE_DOMAIN in http_host:
subdomains = http_host.split(SERVICE_DOMAIN)[0]
subdomain = slugify.slugify(subdomains)
else:
subdomain = False
if subdomain:
print('Our subdomain is {}'.format(subdomain))
kwargs['subdomain'] = subdomain
return CustomUrlView.as_view()(self.request, *args, **kwargs)
if not subdomain:
print('run normally')
return super().dispatch(request, *args, **kwargs)
class CustomUrlView(View):
def dispatch(self, request, *args, **kwargs):
subdomain = kwargs.get('subdomain')
url = request.META.get('PATH_INFO').lower().strip().replace('/', '', 1)
# here I do some queries in DB with my url variable - it has own model etc.
Generally, everything works fine for almost all user defined urls...
Problem is when visitor opens myusername.example.com/contact - its always match the url defined in urls.py and and its not catched by my HomeView.
How can I solve it? I don't want to use any of my class based view's urls defined in urls.py when request comes from a subdomain.
You need to have a two different urls files. One for domain and second for subdomains.
Split domain and subdomain views in two url files. If you have views which works on both e.g. login, create a "common" file and include in both urls.
You can choose which url you will use, so create a middleware and inspect a host request.META.get('HTTP_HOST'). If request comes from subdomain, then simply load appropriated urls request.urlconf = 'path.to_subdomain.urls'
Note:
Be sure that ROOT_URLCONF in your settings.py point to the "domain' urls. Also, in your middleware you should inspect does subdomain exists and return 404 if it doesn't exist.
I am using i18n_patterns to internationalize my app and it's working except when I click on a link that requires login (a view protected by #login_required decorator), I am being redirected to the login form in the default language instead of the currently active one.
How I can preserve the active URL? In other words, When in the French section, I want #login_required to redirect me /fr/login/?next=/fr/clients/ instead of /en/login/?next=/fr/clients/
I had the same issue, and I solved it by editing settings.py:
from django.core.urlresolvers import reverse_lazy
LOGIN_URL = reverse_lazy('login')
The function reverse_lazy adds the correct language prefix on the URL !
(with 'login' being the name of your login route)
I'm not well-versed in i18n for Django, but I don't think this is actually possible because login_required binds its login_url parameter to the decorated function at the point of decorator application. You're probably better off writing your own decorator; assuming you don't use either of the optional parameters to login_required, you can make your own as
from django.contrib.auth.views import redirect_to_login
from django.core.urlresolvers import reverse
import functools
def login_required(fn):
#functools.wraps(fn)
def _decorated(request, *args, **kwargs):
if request.user.is_authenticated():
return fn(request, *args, **kwargs)
path = request.get_full_path()
login_url = reverse('login')
return redirect_to_login(path, login_url)
where reverse('login') gets whatever the name of your login view in your urls.py is.
I haven't tested this, but if something comes up I'll try to debug it to the best of my ability.
We use class based views for most of our project. We have run into an issue when we try and create a CSV Mixin that will allow the user to export the information from pretty much any page as a CSV file. Our particular problem deals with CSV files, but I believe my question is generic enough to relate to any file type.
The problem we are having is that the response from the view is trying to go to the template (say like from django.views.generic import TemplateView). We specify the template in the urls.py file.
url(r'^$', MyClassBasedView.as_view(template_name='my_template.html'))
How can you force the response to bypass the template and just return a standard HttpResponse? I'm guessing you'll need to override a method but I'm not sure which one.
Any suggestions?
EDIT1: It appears that I was unclear as to what we are trying to do. I have rendered a page (via a class based view) and the user will see reports of information. I need to put in a button "Export to CSV" for the user to press and it will export the information on their page and download a CSV on to their machine.
It is not an option to rewrite our views as method based views. We deal with almost all class based view types (DetailView, ListView, TemplateView, View, RedirectView, etc.)
This is a generic problem when you need to provide different responses for the same data. The point at which you would want to interject is when the context data has already been resolved but the response hasn't been constructed yet.
Class based views that resolve to the TemplateResponseMixin have several attributes and class methods that control how the response object is constructed. Do not be boxed into thinking that the name implies that only HTML responses or those that need template processing can only be facilitated by this design. Solutions can range from creating custom, reusable response classes which are based on the behavior of the TemplateResponse class or creating a reusable mixin that provides custom behavior for the render_to_response method.
In lieu of writing a custom response class, developers more often provide a custom render_to_response method on the view class or separately in a mixin, as it's pretty simple and straightforward to figure out what's going on. You'll sniff the request data to see if some different kind of response has to be constructed and, if not, you'll simply delegate to the default implementation to render a template response.
Here's how one such implementation might look like:
import csv
from django.http import HttpResponse
from django.utils.text import slugify
from django.views.generic import TemplateView
class CSVResponseMixin(object):
"""
A generic mixin that constructs a CSV response from the context data if
the CSV export option was provided in the request.
"""
def render_to_response(self, context, **response_kwargs):
"""
Creates a CSV response if requested, otherwise returns the default
template response.
"""
# Sniff if we need to return a CSV export
if 'csv' in self.request.GET.get('export', ''):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="%s.csv"' % slugify(context['title'])
writer = csv.writer(response)
# Write the data from the context somehow
for item in context['items']:
writer.writerow(item)
return response
# Business as usual otherwise
else:
return super(CSVResponseMixin, self).render_to_response(context, **response_kwargs):
Here's where you can also see when a more elaborate design with custom response classes might be needed. While this works perfectly for adding ad-hoc support for a custom response type, it doesn't scale well if you wanted to support, say, five different response types.
In that case, you'd create and test separate response classes and write a single CustomResponsesMixin class which would know about all the response classes and provide a custom render_to_response method that only configures self.response_class and delegates everything else to the response classes.
How can you force the response to bypass the template and just return
a standard HttpResponse?
This kinda defeats the point of using a TemplateView. If the thing you're trying to return isn't a templated response, then it should be a different view.
However...
I'm guessing you'll need to override a method but I'm not sure which one.
...if you prefer to hack it into an existing TemplateView, note from the source code...
class TemplateView(TemplateResponseMixin, ContextMixin, View):
"""
A view that renders a template. This view will also pass into the context
any keyword arguments passed by the url conf.
"""
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
return self.render_to_response(context)
...so you'd have to override the get() method so it doesn't call render_to_response() when returning your CSV. For example...
class MyClassBasedView(TemplateView):
def get(self, request, *args, **kwargs):
if request.GET['csv'].lower() == 'true':
# Build custom HTTP response
return my_custom_response
else:
return TemplateView.get(request, *args, **kwargs)
If you need a generic mixin for all subclasses of View, I guess you could do something like...
class MyMixin(object):
def dispatch(self, request, *args, **kwargs):
if request.GET['csv'].lower() == 'true':
# Build custom HTTP response
return my_custom_response
else:
return super(MyMixin, self).dispatch(request, *args, **kwargs)
class MyClassBasedView(MyMixin, TemplateView):
pass