Django url change language code - python

I am trying to change the language of the website when users click a button in Django.
I have a base project and the urls are:
urlpatterns += i18n_patterns(
# Ecommerce is the app where I want to change the language
url(r'^', include("ecommerce.urls")),
)
The url inside Ecommerce.urls is:
urlpatterns = [
url(r'^testing/$', views.test, name='url_testing'),
... other urls
]
When I visit the url above, I first go to: http://localhost/en/testing/.
I want to set a link Change Language so that when users click it, it will change language to http://localhost/zh-hans/testing/. How do I do this in my template?
EDIT
I can now change the language using the following code but the problem is that it only works once:
<form id="languageForm" action="/i18n/setlang/" method="post">
{% csrf_token %}
<input name="next" type="hidden" value="{% url 'url_testing' %}" />
<input id="newLanguageInput" type="hidden" name="language"/>
</form>
And my links are:
<li><a onclick="changeLanguage('zh-hans')">简体</a></li>
<li><a onclick="changeLanguage('zh-hant')">繁體</a></li>
The function changeLanguage is defined like:
function changeLanguage(newLanguage) {
$('input[name="newLanguageInput"]').val(newLanguage);
$('#languageForm').submit();
}
The code works when I first click any of the 2 links, and I will be redirected to the url http://localhost/zh-hans/testing/ or http://localhost/zh-hant/testing/. The problem is after I change the language once, it no longer changes. Is there something wrong with my submit?

Actually it's not going to be a simple <a> link but a <form>.
Have a read on how to set_language redirect view. This form will be responsible for changing languages. It's easy as a pie.
Make sure you have set some LANGUAGES first.

You can change the language of the website when users click a link (no url translation, no post) like this:
navigation.html (with bootstrap4 and font awesome)
<li class="nav-item dropdown">
{% get_current_language as LANGUAGE_CODE %}
<a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown">{{ LANGUAGE_CODE }}</a>
<div class="dropdown-menu dropdown-menu-right">
{% get_available_languages as languages %}
{% for lang_code, lang_name in languages %}
<a href="{% url 'main:activate_language' lang_code %}" class="dropdown-item">
{% if lang_code == LANGUAGE_CODE %}
<i class="fas fa-check-circle"></i>
{% else %}
<i class="far fa-circle"></i>
{% endif %}
{{ lang_name }} ({{ lang_code }})
</a>
{% endfor %}
</div>
</li>
views.py
from django.shortcuts import redirect
from django.utils import translation
from django.views.generic.base import View
class ActivateLanguageView(View):
language_code = ''
redirect_to = ''
def get(self, request, *args, **kwargs):
self.redirect_to = request.META.get('HTTP_REFERER')
self.language_code = kwargs.get('language_code')
translation.activate(self.language_code)
request.session[translation.LANGUAGE_SESSION_KEY] = self.language_code
return redirect(self.redirect_to)
urls.py
from django.urls import path
from .views import ActivateLanguageView
app_name = 'main'
urlpatterns = [
path('language/activate/<language_code>/', ActivateLanguageView.as_view(), name='activate_language'),
]
It's work for me.

New snippets compatible with new API (Boostrap 5 and Django >= 4.0) updated from the excellent #Boris Đurkan answer.
Django has dropped the translation.LANGUAGE_SESSION_KEY support.
So basically we need to make this setup using the session cookie.
class ActivateLanguageView(View):
def get(self, request, lang, **kwargs):
url = request.META.get('HTTP_REFERER', '/')
translation.activate(lang)
response = HttpResponseRedirect(url)
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, lang)
return response
Boostrap has changed its jQuery binding for dropdown:
<li class="nav-item dropdown">
{% get_current_language as LANGUAGE_CODE %}
<a class="nav-link dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-bs-toggle="dropdown" aria-expanded="false">
<strong>{{ LANGUAGE_CODE }}</strong>
</a>
<div class="dropdown-menu dropdown-menu-right">
{% get_available_languages as languages %}
{% for lang_code, lang_name in languages %}
<a href="{% url 'activate_language' lang_code %}" class="dropdown-item">
{% if lang_code == LANGUAGE_CODE %}
<i class="bi bi-check-circle"></i>
{% else %}
<i class="bi bi-circle"></i>
{% endif %}
{{ lang_name }} ({{ lang_code }})
</a>
{% endfor %}
</div>
</li>
Other parts of code remains basically the same.

Related

Django, django-filter and pagination

my goal is to have a 'user_profile' page that displays relevant information of the user of interest.
Furthermore, the 'user_profile' page should include all the posts that were created by the respective user as new blog entries.
These posts, however, should be filterable with the application 'django-filter' and be paginated. At the moment I have difficulties to paginate the filtered posts. So my question is how to achieve the latter?
So far, I used following approach:
filters.py
import django_filters
class AccountPostFilter(django_filters.FilterSet):
title = django_filters.CharFilter(lookup_expr='icontains')
category = django_filters.ChoiceFilter(choices=cat_list)
class Meta:
model = Post
fields = ['title', 'category']
views.py
class UserProfile(DetailView, MultipleObjectMixin):
model = Account
template_name = 'account/user_profile.html'
paginate_by = 5
def get_context_data(self, **kwargs):
posts = Post.objects.all().filter(author=self.kwargs['pk'])
context = super().get_context_data(object_list=posts, **kwargs)
context['filterset'] = AccountPostFilter(self.request.GET, queryset=posts)
return context
Thank you very much for your time. Best wishes,
Daniel
There is another way of doing this, and do it in a clean and professional way, which will save you the trouble of using Django Filters:
Create a helper function called clean_filters (This will help you clean filters that come in from the brownser:
def clean_filters(filters):
filters = {k: v for k, v in filters.items() if v}
return filters
Create another help function called search (this will help you get the parameters from the GET request and put them in a **filters inside the django filter directive. And return them back with the paginator so you can keep the same filters when moving from page to page):
from 'your_utils_file' import clean_filters
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
def search(request):
filters = {
"account__first_name__icontains": request.GET.get("fname_kw"), # put your filters here
"account__last_name__icontains": request.GET.get("lname_kw"), # put your filters here
}
html_queries = {
"fname_kw": request.GET.get("fname_kw"),
"lname_kw": request.GET.get("lname_kw"),
}
filters = clean_filters(filters)
html_queries = clean_filters(html_queries)
posts = Post.objects.filter(**filters) # put your model here
page = request.GET.get('page', 1)
paginator = Paginator(posts, 8)
try:
posts= paginator.page(page)
except PageNotAnInteger:
posts= paginator.page(1)
except EmptyPage:
posts= paginator.page(paginator.num_pages)
return posts
Here is your view (this simply calls the search function to reduce the code of your view and make it easy for your code to maintain):
def search_page(request):
posts = search(request)
if posts is not None:
context = {
'posts': posts,
}
return render(request, "core/index.html", context)
return redirect("index")
Here is your HTML (just a classic pagination code for Django and Bootstrap. This also has the filter and the value of the filter in a loop inside the GET request):
<div class="mb-5">
{% if posts.has_other_pages %}
<nav aria-label="Page navigation example">
<ul class="pagination justify-content-center">
{% if posts.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ posts.previous_page_number }}{% for fil, fil_value in filters.items %}&{{fil}}={{fil_value}}{% endfor %}" tabindex="-1">
<i class="fa fa-angle-left"></i>
<span class="sr-only">Prev</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="javascript:void(0)" tabindex="-1">
<i class="fa fa-angle-left"></i>
<span class="sr-only">Prev</span>
</a>
</li>
{% endif %}
{% for i in posts.paginator.page_range %}
{% if posts.number == i %}
<li class="page-item active"><a class="page-link" href="javascript:void(0)">{{ i }}</a></li>
{% else %}
<li class="page-item"><a class="page-link" href="?page={{ i }}{% for fil, fil_value in filters.items %}&{{fil}}={{fil_value}}{% endfor %}">{{ i }}</a></li>
{% endif %}
{% endfor %}
{% if posts.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ posts.next_page_number }}{% for fil, fil_value in filters.items %}&{{fil}}={{fil_value}}{% endfor %}">
<i class="fa fa-angle-right"></i>
<span class="sr-only">Next</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="javascript:void(0)">
<i class="fa fa-angle-right"></i>
<span class="sr-only">Next</span>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}

Pagination of search results only in Django not working

I have implemented a search bar and function into a django web app in the following view:
def search_products(request):
search_query = request.GET.get('search', '')
products = Product.objects.filter(Q(name__icontains=search_query) | Q(brand__icontains=search_query))
paginator = Paginator(products, 40)
page_number = request.GET.get('page', 1)
page = paginator.get_page(page_number)
if page.has_next():
next_url = f'?page={page.next_page_number()}'
else:
next_url = ''
if page.has_previous():
prev_url = f'?page={page.previous_page_number()}'
else:
prev_url = ''
return render(request, 'store/search_products.html',
context={'products': page.object_list, 'page': page, 'next_page_url': next_url,
'prev_page_url': prev_url})
The URLs are setup as follows:
urlpatterns = [
path('', views.store_view, name='store'),
path('wishlist/', views.wishlist_view, name='wishlist'),
path('update_item/', views.updateItem, name='update_item'),
path('search_products/', views.search_products, name='search_products'),
]
And the HTML for the search results is as follows:
<nav aria-label="Page navigation example">
<ul class="pagination">
<li class="page-item {% if not prev_page_url %} disabled {% endif %}">
<a class="page-link" href="{{ prev_page_url }}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% for n in page.paginator.page_range %}
{% if page.number == n %}
<li class="page-item active" aria-current="page">
<a class="page-link" href="?page={{ n }}">{{ n }}
<span class="sr-only"></span>
</a></li>
{% elif n > page.number|add:-4 and n < page.number|add:4 %}
<li class="page-item">
<a class="page-link" href="?page={{ n }}">{{ n }}</a>
</li>
{% endif %}
{% endfor %}
<li class="page-item {% if not next_page_url %} disabled {% endif %}">
<a class="page-link" href="{{ next_page_url }}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
I think the issue may be due to the fact that the search query enters the URL before the page number (?page={{ n }}), this may be wrong however. I am not sure how to fix this or implement this change. Any help would be appreciated.
The reason that your pagination is not working is that when you click on another page link the page query parameter replaces the search query parameter and your search results will be gone.
The solution is to to append page query parameter to search query parameter it will be some thing like this for example: url/?search=example&page=2
This is how you can do that:
Inside your app directory create a folder called templatetags
Inside the templatetags folder create a file called pagination_tags.py(name is up to you)
# Your template tag in app_name/templatetags/pagination_tags.py
from django import template
from urllib.parse import urlencode
register = template.Library()
#register.simple_tag
def url_replace (request, field, value):
dict_ = request.GET.copy()
dict_[field] = value
return dict_.urlencode()
After creating your template tag load it in your html page and use it
<!-- load your templatetag in your html file -->
<!-- you should load your .py file -->
{% load pagination_tags %}
<!-- now you can use it -->
<!-- Here is an Example to know how to use it -->
<a class="page-link text-dark" href="?{% url_replace request 'page' 1 %}">1</a>
<!-- first param is request , second param is is your lookup field and third param is your page number -->

Reverse for 'project_detail' with keyword arguments '{'pk': 1}' not found. 1 pattern(s) tried: ['projects//int:pk/$']

I'm trying to make a simple portofolio app using django but I'm keep getting this error.
I looked everywhere on google but nothing helps.
I'm basically trying to link the Read more button(in project_index.html) to the project_details.html page using {% url 'project_detail' pk=project.pk %}
Views.py:
from django.shortcuts import render
from projects.models import Project
# Create your views here.
def project_index(request):
projects = Project.objects.all()
context = {"projects": projects}
return render(request, "project_index.html", context)
def project_detail(request, pk):
project = Project.objects.get(pk=pk)
context = {"project": project}
return render(request, "project_detail.html", context)
Urls.py:
from django.urls import path
from projects import views
urlpatterns = [
path("", views.project_index, name="project_index"),
path("/int:pk/", views.project_detail, name="project_detail"),
]
models.py:
from django.db import models
# Create your models here.
class Project(models.Model):
title = models.CharField(max_length=100)
description = models.TextField()
technology = models.CharField(max_length=20)
image = models.FilePathField(path="/img")
project_index.html:
{% extends "base.html" %}
{% load static %}
{% block page_content %}
<h1>Projects</h1>
<div class="row">
{% for project in projects %}
<div class="col-md-4">
<div class="card mb-2">
<img class="card-img-top" src="{% static project.image %}">
<div class="card-body">
<h5 class="card-title">{{ project.title }}</h5>
<p class="card-text">{{ project.description }}</p>
**<a href="{% url 'project_detail' pk=project.pk %}"**
class="btn btn-primary">Read More</a>
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock %}
project_detail.html:
{% extends "base.html" %}
{% load static %}
{% block page_content %}
<h1>{{ project.title }}</h1>
<div class="row">
<div class="col-md-8">
<img src="{% static project.image %}" alt="" width="100%">
</div>
<div class="col-md-4">
<h5>About the project:</h5>
<p>{{ project.description }}</p>
<br>
<h5>Technology used:</h5>
<p>{{ project.technology }}</p>
</div>
</div>
{% endblock %}
Thanks in advance.
correct your url
path("<int:pk>/", views.project_detail, name="project_detail")

Django: login link to appear only when a user is logged in

I am making an app in django in which i have used the django built in authentication system. Both login and logout links are present in the navbar. I want to make the logout link appear only when the user has logged in and not at all times. How do i do that?
code snippet of project/urls.py:
urlpatterns = [
url(r'^login/$', views.login, {'template_name': 'login.html', 'authentication_form': LoginForm}, name='login'),
url(r'^logout/$', views.logout, {'next_page': '/home'}), ]
code snippet of login.html;
<div class="container">
<section id="content">
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
{% if next %}
{% if user.is_authenticated %}
<p>Your account doesn't have access to this page. To proceed,
please login with an account that has access.</p>
{% else %}
<p>Please login to see this page.</p>
{% endif %}
{% endif %}
<form action="{% url 'login' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<h1>Login Form</h1>
<div class="imgcontainer">
<img src="{% static 'student/patient.jpg' %}" alt="Avatar" class="avatar">
</div>
<div class="username">
{{ form.username.label_tag }}
{{ form.username }}
</div>
<div class="password">
{{ form.password.label_tag }}
{{ form.password }}
</div>
<div class="submitb">
<input type="submit" value="Log In" name="mybtn">
</div>
<div class="resetb">
<input type="submit" value="Reset">
Forgot password?
</div>
<input type="hidden" name="next" value="{{ next }}" />
</form>
code snippet of base.html(only the navbar is shown):
<ul>
<li><a class="active" href="/home">Home</a></li>
<li>About </li>
<li>Sign up</li>
<li>Doctor's login</li>
<li>Patient's login</li>
<li>FAQs</li>
<li>Contact us</li>
<li>Reviews</li>
<li>Logout</li>
</ul>
Thanks for your help in advance.
Put the logout list item in an if block that checks that the user is authenticated like so:
<ul>
...
{% if request.user.is_authenticated %}
<li>Logout</li>
{% endif %}
</ul>
Although using the variable request.user.is_authenticated is one way. To make it simpler create HTML pages such that login and logout button are disintegrated.
Pages which appear after login should only contain the logout option/button. This will smoothen your development process.

Can't iterate from BlogPost object in my template (Django)

I can't iterate through BlogPost object in my templates. Nothing is showing up for some reason. I probably forgot something. In the shell I can obtain the objects without any problem. So something is wrong, and I can't figure out what.
views.py:
def latest_posts(request):
latest_posts = BlogPost.objects.all().filter(site_id=1)[:50]
render(request, (settings.PROJECT_ROOT + "/main/templates/includes/latest_posts.html"), {"latest_posts": latest_posts})
latest_posts.html:
{% load pages_tags mezzanine_tags i18n accounts_tags %}
<div class="panel panel-default" >
<div class="panel-heading">
<h3 class="panel-title">{% trans "Latest Posts" %}</h3>
</div>
<div class="panel-body" style="padding:0;border:0px;">
{% for lp in latest_posts %}
<ul class="list-group-latest-posts">
<li class="list-group-item-latest-posts">
<img class="media-object left" src="#" width="40" height="40" alt="#">
<p>{{ lp.title }}<br><span class="latest-post-name">user_name</span><span class="latest-post-divider"> - </span><span class="latest-post-time">6 Hours Ago</span></p>
</li>
</ul>
{% endfor %}
</div>
</div>
This is my structure. In base.html:
{% if '/' in request.path %}
{% else %}
{% include "includes/sidebar.html" %}
{% endif %}
sidebar.html:
<div class="col-md-4 right">
{% include 'includes/latest_posts.html' %}
</div>
In my urls.py:
url("^$", direct_to_template, {"template": "index.html"}, name="home"),
You're page is loaded from a different view called direct_to_template that has nothing to do with the latest_posts view so it will never find its context data.
So now 1 of two things needs to happen, either you just consume the code from latest_posts re: the context data into that other view and include it in that context. Or you make a url to point to that page
from views import latest_posts
url("^latest_posts$", latest_posts, name="latest_posts"),
Now this will get you the posts showing from the url /latest_posts, but it probably doesn't look very pretty, it could be an option to have the latest_posts view still load the base.html template which will make it look more like you expect, although a browse through the documentation on template inheritance may help more

Categories