Setting Up a Language-Switching Button in Django - django

I'm new to Django and would like some advice on how to set up a single language switching button to switch between two languages (English and Japanese in this case), and specifically how to set up this functionality in a view and access it from a template.
In the template, I've arrived at the following which works but it includes repeated code for the form and the button, which obviously isn't great.
{% get_current_language as LANGUAGE_CODE %}
{% ifequal LANGUAGE_CODE 'en' %}
<form action="{% url 'set_language' %}" method="post" id="form_{{ 'ja' }}" >
{% csrf_token %}
<input name="next" type="hidden" value="{{ redirect_to }}" />
<input name="language" type="hidden" value="{{ 'ja' }}" />
</form>
<button class="btn btn-sm btn-outline-primary" type="submit" form="form_{{ 'ja' }}" value="Submit">日本語</button>
{% else %}
<form action="{% url 'set_language' %}" method="post" id="form_{{ 'en' }}" >
{% csrf_token %}
<input name="next" type="hidden" value="{{ redirect_to }}" />
<input name="language" type="hidden" value="{{ 'en' }}" />
</form>
<button class="btn btn-sm btn-outline-primary" type="submit" form="form_{{ 'en' }}" value="Submit">English</button>
{% endifequal %}
My apologies for the beginner question but I'd really appreciate some advice as to how to put the above logic into a view and access it from the template. Thanks in advance.

In case this is useful to anyone, I solved this problem as follows using a custom filter included in a file I called language_selector.py.
In language_selector.py:
from django import template
register = template.Library()
#register.filter
def new_lang_code(current_lang_code):
if current_lang_code == ‘en’:
return ‘ja’
else:
return ‘en’
#register.filter
def new_lang_name(current_lang_code):
if current_lang_code == ‘en’:
return ‘日本語’
else:
return ‘English’
Set up a template tags directory containing the above file.
app/
__init__.py
models.py
templatetags/
__init__.py
language_selector.py
views.py
In the template:
{% load language_selector %}
⋮
<form action=“{% url ‘set_language’ %}” method=“post” id=“form_{{ LANGUAGE_CODE|new_lang_code }}” >
{% csrf_token %}
<input name=“next” type=“hidden” value=“{{ redirect_to }}” />
<input name=“language” type=“hidden” value=“{{ LANGUAGE_CODE|new_lang_code }}” />
</form>
<button class=“btn btn-sm btn-outline-primary” type=“submit” form=“form_{{ LANGUAGE_CODE|new_lang_code }}” value=“Submit”>{{ LANGUAGE_CODE|new_lang_name }}</button>
This works and seems to be a simple and clean way to implement a language-switching button, but any comments are welcome if there are better ways to do this.

Related

Django Admin Action Confirmation Page

In my Django project I have an admin action which requires an confirmation page. I oriented it on the delete_selected action, but it does not work. Here is what I have.
part of my admin.py
def my_action(modeladmin, request, queryset):
if request.POST.get('post'):
print "Performing action"
# action code here
return None
else:
return TemplateResponse(request, "admin/my_action_confirmation.html")
admin/my_action_confirmation.html
<form action="" method="post">{% csrf_token %}
<div>
<input type="hidden" name="post" value="yes" />
<input type="hidden" name="action" value="my_action" />
<input type="submit" value="Confirm" />
</div>
</form>
This works almost. I get to the confirmation page but if I click "confirm" I just get back to the original page. The part with the action code is never reached. In fact the my_action function isn't called a second time. So how do I tell django, that the my_action function should be called a second time, once I clicked confirm?
Edit: I was more missing then I thought
The corrected my_action
def my_action(modeladmin, request, queryset):
if request.POST.get('post'):
print "Performing action"
# action code here
return None
else:
request.current_app = modeladmin.admin_site.name
return TemplateResponse(request, "admin/my_action_confirmation.html")
admin/my_action_confirmation.html
{% load l10n %}
<form action="" method="post">{% csrf_token %}
<div>
{% for obj in queryset %}
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
{% endfor %}
<input type="hidden" name="post" value="yes" />
<input type="hidden" name="action" value="my_action" />
<input type="submit" value="Confirm" />
</div>
</form>
admin.py
def require_confirmation(func):
def wrapper(modeladmin, request, queryset):
if request.POST.get("confirmation") is None:
request.current_app = modeladmin.admin_site.name
context = {"action": request.POST["action"], "queryset": queryset}
return TemplateResponse(request, "admin/action_confirmation.html", context)
return func(modeladmin, request, queryset)
wrapper.__name__ = func.__name__
return wrapper
#require_confirmation
def do_dangerous_action(modeladmin, request, queryset):
return 42/0
admin/action_confirmation.html
{% extends "admin/base_site.html" %}
{% load i18n l10n admin_urls %}
{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation
delete-selected-confirmation{% endblock %}
{% block content %}
<p>Are you sure you want to {{ action }}?</p>
<ul style="padding: 0">
{% for object in queryset.all %}
<li style="list-style: none; float: left; margin: 5px">
{{ object }}
</li>
{% endfor %}
</ul>
<hr>
<br>
<form action="" method="post">{% csrf_token %}
<fieldset class="module aligned">
{% for obj in queryset.all %}
<input type="hidden" name="_selected_action" value="{{ obj.pk|unlocalize }}"/>
{% endfor %}
</fieldset>
<div class="submit-row">
<input type="hidden" name="action" value="{{ action }}"/>
<input type="submit" name="confirmation" value="Confirm"/>
<a href="#" onclick="window.history.back(); return false;"
class="button cancel-link">{% trans "No, take me back" %}</a>
</div>
</form>
{% endblock %}
Just in case of anyone wanting to add a confirmation view to something more than an action.
I wanted to make the save of an admin creation view go to a confirmation view. My model was very complex and created a lot of implications for the system. Adding the confirmation view would make sure that the admin was aware of these implications.
The solution would be overriding some _changeform_view method which is called on the creation and the edition.
The full code is here: https://gist.github.com/rsarai/d475c766871f40e52b8b4d1b12dedea2
Here is what worked for me:
Add confirm action method (in admin.py)
from django.template.response import TemplateResponse
def confirm_my_action(modeladmin, request, queryset):
response = TemplateResponse(request,
'admin/confirm_my_action.html',
{'queryset': queryset})
return response
and point to it from your admin model (in admin.py)
class SomeModelAdmin(admin.ModelAdmin):
actions = [confirm_my_action]
Add template, which has a form whose action is pointing to my_action endpoint.
{% extends "admin/base_site.html" %}
{% block content %}
<div id="content" class="colM delete-confirmation">
<form method="post" action="/admin/my_action/">
{% csrf_token %}
<div>
{% for obj in queryset %}
<input type="hidden" name="obj_ids[]" value="{{ obj.pk }}" />
<ul><li>Obj: ">{{obj}}</a></li></ul>
{% endfor %}
</div>
<input type="submit" value="Yes, I'm sure">
No, take me back
</form>
<br class="clear">
<div id="footer"></div>
</div>
{% endblock %}
Add appropriate endpoint (e.g. in urls.py).
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^admin/my_action/', my_action_method),
]

How to move singup\signin templates into dropdown menu?

I have a fixed navigation and I want to add dropdown box where users can singup\in (as Twitter uses).
I tried:
# project/tempates/signup.html
{% load i18n %}
{% load account socialaccount %}
{% block head_title %}{% trans "Signup" %}{% endblock %}
{% block content %}
<h1>{% trans "Sign Up" %}</h1>
<p>{% blocktrans %}Already have an account? Then please sign in.{% endblocktrans %}</p>
<form class="signup" id="signup_form" method="post" action="{% url 'account_signup' %}">
{% csrf_token %}
{{ signupform.as_p }}
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
{% endif %}
<button type="submit">{% trans "Sign Up" %} »</button>
</form>
{% endblock %}
# project/tempates/base.html
# ... a lot of basic stuff
<li class="dropdown">
<a class="dropdown-toggle" href="#" data-toggle="dropdown">Sign In <strong class="caret"></strong></a>
<div class="dropdown-menu" style="padding: 15px; padding-bottom: 0px;">
{% include './signup.html' %}
# ... rest stuff
and in dropdown box I see just the text, link to signin, and the button for confirmation of the registration.
There are no fields to enter email and passwords. As I understand, this is because no access to the form, what usually is a views' jobs. How can I get workable dropdown forms?
After 2 days of internet digging I want to summarize what I found.
There are few ways:
1. Use <form action='some address here'>. The easiest way.
To check default AllAuth forms we need to:
# ./manage.py shell
>>> import allauth.account.forms as forms
>>> f = forms.LoginForm()
>>> print(f)
Below is edited version of print(f) which is added directly to base.html
<form action="{% url 'account_login' %}" method="post">
{% csrf_token %}
<input type="hidden" name="next" value="{{ request.get_full_path }}" />
<input id="id_login" name="login" placeholder="Username or e-mail" type="text" required />
<input id="id_password" name="password" placeholder="Password" type="password" required />
<label for="id_remember">Remember Me:</label>
<input id="id_remember" name="remember" type="checkbox" />
<button type="submit">Login</button>
Forgot Password?
</form>
Method is based on the solution from ->here<-
2. Contex processor
a) Make folder your_project/your_app/context_processor. Put there 2 files - __init__.py and login_ctx.py
b) In login_ctx.py add:
from allauth.account.forms import LoginForm
def login_ctx_tag(request):
return {'loginctx': LoginForm()}
c) In project's SETTINGS add your_app.context_processors.login_ctx.login_form_ctx' inTEMPLATES` section. Something like:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates'), os.path.join(BASE_DIR, 'templates', 'allauth')],
'APP_DIRS': True,
'OPTIONS': {
'debug': DEBUG,
'context_processors': [
'your_app.context_processors.login_ctx.login_form_ctx', # <- put your processor here
'django.template.context_processors.debug',
# [...other processors...]
],
},
},
]
d) In your *.html where you need add the next:
{% if not user.is_authenticated %}
<form action="{% url 'account_login' %}" method="post">
{% csrf_token %}
<input type="hidden" name="next" value="{{ request.get_full_path }}" />
{{ loginctx }}
<button type="submit">Login</button>
</form>
{% else %}
{# display something else here... (username?) #}
{% endif %}
3. Template tag
a) Make folder your_project/your_app/templatetags. Put there 2 files - __init__.py and login_tag.py
b) In login_tag.py add:
from django import template
from allauth.account.forms import LoginForm
register = template.Library()
#register.inclusion_tag('profiles/true_login.html')
def login_form_tag(current_page=None):
return {'loginform': LoginForm(),
'redirect_to': current_page}
c) In your_project/your_app/templates/your_app/ make file login_form.html with content:
{% load account %}
{% if not user.is_authenticated %}
<form action="{% url 'account_login' %}" method="post">
{% csrf_token %}
<input type="hidden" name="next" value="{{ redirect_to }}" />
{{ loginform }}
<button type="submit">Login</button>
</form>
{% else %}
{# display something else here... (username?) #}
{% endif %}
d) In any *.html you need, add at the top {% load login_tag %} and in the needed place add {% login_form_tag request.get_full_path %}
The 2nd and 3rd methods show native AllAuth form. If you need to edit it somehow using {{form}}, ->here<- in the doc you can find some examples how to do that. Want to mention, that if in the doc is shown something like:
<div class="fieldWrapper">
{{ form.subject.errors }}
{{ form.subject.label_tag }}
{{ form.subject }}
</div>
for our case form must be changed to loginctx or loginform
Also you can write your own form or inherit AllAuth and import it to context processor or templatetag as shown above.
Both methods are based on ->this solution<-
In all 3 methods redirect works as needed (return a user to the previous page, in case of success login, else redirect to original AllAuth template at site.com/account/login).
All written above can be implemented to SignUP.
Also I asked some people, how to show errors in case of wrong username\password instead of redirect to site.com/account/login, a proposition was to use AJAX, but currently this is out of my knowledge. Some base info about connection signin\up forms to default AllAuth views can be found ->here<-. If anyone could implement it, or find any tutorial, please post it here.

Django admin action not executed after confirming

In my Django app I want to implement an intermediate page which asks for confirmation before executing a specific admin action. I took this post as an example.
I used the existing delete_confirmation.html template as a departing point and partially got it working. The confirmation page is shown and the selected objects are displayed. However my admin action is never called after clicking "Yes, I'm sure".
In my admin.py I have:
def cancel_selected_bookings(self, request, queryset):
"""
Cancel selected bookings.
"""
if request.POST.get("post"):
for booking in queryset:
booking.cancel()
message = "Booking %s successfully cancelled." % booking.booking_id
messages.info(request, message)
else:
context = {
"objects_name": "bookings",
'title': "Confirm cancellation of selected bookings:",
'cancellable_bookings': [queryset],
'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME,
}
return TemplateResponse(request, 'admin/bookings/confirm_cancel.html', context, current_app=self.admin_site.name)
In my template I have (a clipping of the full template):
<div class="grp-group">
<h2>
{% blocktrans %}
Are you sure you want to cancel the selected {{ objects_name }}?
{% endblocktrans %}
</h2>
{% for cancellable_booking in cancellable_bookings %}
<ul class="grp-nested-list">{{ cancellable_booking|unordered_list }}</ul>
{% endfor %}
</div>
<form action="" method="post">{% csrf_token %}
<div id="submit" class="grp-module grp-submit-row grp-fixed-footer">
{% for obj in queryset %}
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
{% endfor %}
<input type="hidden" name="action" value="cancel_selected_bookings" />
<input type="hidden" name="post" value="yes" />
<ul>
<li class="grp-float-left">{% trans "Cancel" %}</li>
<li><input type="submit" value="{% trans "Yes, I'm sure" %}" class="grp-button grp-default" /></li>
</ul>
<input type="hidden" name="post" value="yes" />
</div>
</form>
EDIT:
I found one problem in the above template. The lines:
{% for obj in queryset %}
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
{% endfor %}
need to be replaced by:
{% for cancellable_booking in cancellable_bookings %}
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ cancellable_booking.id }}" />
{% endfor %}
For some mysterious reason however, the value of the hidden fields isn't being set by {{cancellable_booking.id}}. When I hard code that with an existing id it all works as expected. What am I doing wrong??
This will work:
In the action method:
context = {
'objects_name': 'bookings',
'title': 'Confirm cancellation of selected bookings:',
'cancellable_bookings': [queryset],
'ids': queryset.values_list("id"),
'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME,
}
In the template:
{% for id in ids %}
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ id.0|unlocalize }}" />
{% endfor %}
Not sure why iterating the queryset doesn't work but alas...

Problem with redirecting to previous page after login in

With this code:
urls.py
url(r'^login/',
"django.contrib.auth.views.login"),
and tamplate like that:
Login
which is redirecting to page with this code:
{% if not user.is_authenticated %}
<form method="post" action="">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Zaloguj" />
<input type="hidden" name="next" value="" />
</form>
{% else %}
<h1>Już jestes zalogowany chujku</h1>
{% endif %}
I have context processor 'django.core.context_processors.request' but after login in I am redirected to
accounts/profile/
When I am on login page url is: /accounts/login/?next=/gallery/newest/. More, after login in it loged me in but I am still in /accounts/login/?next=/gallery/newest/1/.
Have I bugs in this code or answer is laying somewhere else?
Just remove <input type="hidden" name="next" value="" /> from your form, it makes django think that next variable is empty.
Finally i found answer.
I think that this code did not use my own login.html template (but if i am wrong, please correct me):
url(r'^login/',
"django.contrib.auth.views.login")
I change it to:
url(r'^login/',
"django.contrib.auth.views.login", {'template_name': 'login.html'}),
Then I change my login.html tempalate code to:
<form method="post" action="">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Zaloguj" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
I don't have the deeper meaning of what happend but this code which I copied here is working. Thanks for all for suggestions. It helps me to find answer and give a little better understanding of some things.

Add an additional form with django-registration

I have a registration form that is rendered by the following urlconf --
url(r'^$',
register,
{'backend': 'registration.backends.default.UserBackend',
'template_name': 'index.html'},
name='auth_index'),
In the template, I have the following --
<form method="post" action=".">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Register" />
</form>
How would I add additional forms and context to this template? I want to be able to have something like the following --
<form method="post" action=".">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Register" />
</form>
...
<form action='{% url waiting_list %}' method='post'>{% csrf_token %}
<p>Enter your email: <input type="text" name="email" value=""></p>
<input type="submit" name="email_submit" value="Submit Email">
</form>
{{message}}
How could I do this? (Preferably, all within a view). Thank you.
Your template is OK. Now you add the waiting_list url to your uelconf and implement it's view:
url(r'^waiting_list/$', 'myapp.views.waiting_list', {}, name='waiting_list'),
and:
# myapp/views.py
def waiting_list(request):
...
Since this is not data critical form, I would probably use #csrf_exempt to bypass csrf validation to makes thing easier. In case of error, redirect to an error page.
(BTW, usability wise, a better implementation for this problem might be posting the email with javascript.)