How to move singup\signin templates into dropdown menu? - django

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.

Related

Customized django all-auth form not submitting

I am using the django all-auth login form. I wanted to customize the look of the form fields so I changed login.html within the account folder to look like this:
<form class="login" method="POST" action="{% url 'account_login' %}">
{% csrf_token %}
{% for field in form.visible_fields|slice:'2' %}
<div class="row form-group">
{% if field.name == 'login' %}
<input type="text" placeholder="Email"><i class="fas fa-at"></i>
{% else %}
<input type="password" placeholder="Password"><i class="la la-lock"></i>
{% endif %}
</div>
{% endfor %}
Forgot Password?
<button type="submit">Sign In</button>
</form>
The form renders exactly how I would like it to, however nothing happens when I click on submit. What is strange to me is that the form submits perfectly fine if in place of my for loop I simply type {{ form.as_p }}, it just doesn't look how I want. Can anyone see an error in my loop, or is there something else wrong here. I have been looking for a solution online but so far been unsuccessful
You need to specify the names of the fields in your input tags otherwise the POST dictionary will be empty. You are using {% if field.name == 'login' %} but you forgot to specify the name attribute. Same applies for the password input.
<form class="login" method="POST" action="{% url 'account_login' %}">
{% csrf_token %}
{% for field in form.visible_fields|slice:'2' %}
<div class="row form-group">
{% if field.name == 'login' %}
<input name='login' type="text" placeholder="Email"><i class="fas fa-at"></i>
{% else %}
<input name='password' type="password" placeholder="Password"><i class="la la-lock"></i>
{% endif %}
</div>
{% endfor %}
Forgot Password?
<button type="submit">Sign In</button>
</form>

Setting Up a Language-Switching Button in 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.

django delete model entry from selected form option

I have the following form displaying entries of a model for user settings. When selected, I would like that a button catches its pk and send it to a Delete view.Here is the current code but I am missing this part.
user_detail template html
<form id="SettingsListForm"><label>&nbsp Settings List : &nbsp &nbsp &nbsp</label>
{% if user.usersetting.first %}
<select class="form-control" name="settingslist" id = "settingslist" form="SettingsListForm" >
{% for settings in user.usersetting.all %}
<option value="{{ settings.file.url }}">{{ settings }}
</option>
{% endfor %}
</select>
{% else %}
<li class="list-group-item">NO SETTINGS YET</li>
{% endif %}
<button class="btn btn-outline-light btn-circle"><i class="glyphicon glyphicon-minus" href="{% url 'my_app:setting_delete' pk=user.usersetting.last.id %}"></i></button>
{% block delete_setting_confirm_block %}
{% endblock %}
</form>
setting_confirm_delete html template with delete_setting_confirm_block
{% extends 'login_app/user_detail.html' %}
{% block delete_setting_confirm_block %}
<h4>
ARE YOU <b>REALLY</b> SURE YOU WANT TO <b>DELETE</b> THIS SETTING ?
<form method="POST">
{% csrf_token %}
<button type="submit" class="btn btn-outline-light btn-danger" value="Delete">YES</button>
<a class="btn btn-outline-light btn-default" href="{% url 'login_app:user_detail' pk=user.id %}"><b>NO</b></a>
</form>
</h4>
{% endblock %}
my_app urls
url(r'^setting/(?P<pk>\d+)/$',views.UserSettingDeleteView.as_view(),name='setting_delete'),
UserSettingDeleteView in my_app views
class UserSettingDeleteView(DeleteView):
model = models.UserSetting
template_name = 'my_app/setting_confirm_delete.html'
def get_success_url(self):
return reverse('my_app:user_detail', kwargs={'pk': self.object.user.pk})
Somehow, a similar technique works fine when using listgroups:
working sample in user_detail html
<ul class="list-group">
{% if user.userdata.first %}
{% for data in user.userdata.all %}
<li class="list-group-item">{{ data }}<a class="btn btn-outline-light btn-circle" href="{% url 'my_app:data_delete' pk=data.pk %}"><i class="glyphicon glyphicon-remove"></i></a></i></li>
{% endfor %}
{% block delete_data_confirm_block %}
{% endblock %}
{% else %}
<li class="list-group-item">NOTHING RECORDED YET</li>
{% endif %}
</ul>
In your template.html, you should create a form for deletion, like this:
<form action="{% url 'my_app:setting_delete' pk=user.usersetting.last.id %}" method="post">
{% csrf_token %}
<input type="submit" value="Delete" class="btn btn-outline-light btn-circle">
</form>
Because in HTML you can not send directly PUT or DELETE, you should fake it via a POST request. It will be useful to read this, it is explained well.
Your UserSettingDeleteView can be as simple as that:
from django.views.generic.edit import DeleteView
from django.urls import reverse_lazy
class UserSettingDeleteView(DeleteView):
model = MyModel
# Replace with the model you want to delete (User)
success_url = reverse_lazy('my_app:setting_list')
# After deletion, possibly you will want to redirect the user to the list view
The built-in DeleteView cares itself to find your model by the pk parameter you pass through your url and delete it, you just have to configure the model and success_url fields.
Now if you click on your Delete button, you should expect your desired entry to be deleted and the user to be redirected to the list view.
EDIT:
I forgot that you want to get this entry via options. Here you will have to use some JavaScript in order to find the currently selected element and send it's pk to your DeleteView.
At first, add id's to your option tags, like this:
<select class="form-control" name="settingslist" id="settingslist" form="SettingsListForm">
{% for settings in user.usersetting.all %}
<option value="{{ settings.file.url }}" id="{{ settings.id }}">{{ settings }}</option>
{% endfor %}
</select>
And then add some jQuery:
var settingId = $('#SettingsListName').find(":selected").attr('id');
In the end, you need to send settingId to the corresponding url, but I am not very familiar with JavaScript.
Try putting a hidden input in your form. Right now, nothing is passing through the form.
<input type="hidden" name="del_setting" value="{{user.usersetting.last.id}}">
So your form would look like this
<form method="POST">
{% csrf_token %}
<input type="hidden" name="del_setting" value="{{user.usersetting.last.id}}">
<button type="submit" class="btn btn-outline-light btn-danger" value="Delete">YES</button>
<a class="btn btn-outline-light btn-default" href="{% url 'login_app:user_detail' pk=user.id %}"><b>NO</b></a>
</form>
You also probably should move this out of the current form in your user detail template so that you're not putting a form within a form:
{% block delete_setting_confirm_block %}
{% endblock %}
None of the previously mentioned solutions would work so I guess the problem is the nested bootstrap select item which is supposed to display the pk of the setting.
For simplicity I then removed the problem by using list-group instead
<ul class="list-group">
{% if user.usersetting.first %}
{% for settings in user.usersetting.all %}
<li class="list-group-item">{{ settings }}<a class="btn btn-outline-light btn-circle" href="{% url 'my_app:setting_delete' pk=settings.pk %}"><i class="glyphicon glyphicon-remove"></i></a></li>
{% endfor %}
<label>Create a new setting...</label>
<a class="btn btn-outline-light btn-circle"><i class="glyphicon glyphicon-plus"></i></a>
{% else %}
<li class="list-group-item">Create a new setting...<a class="btn btn-outline-light btn-circle"><i class="glyphicon glyphicon-plus"></i></a></li>
{% endif %}
{% block delete_setting_confirm_block %}
{% endblock %}
</ul>
As a workaround,this works well.

url-tag for url include

i use the set_language redirect view form from the docs.
urls.py
urlpatterns += patterns('',
(r'^i18n/', include('django.conf.urls.i18n')),
)
template:
<form action="/i18n/setlang/" method="post">
{% csrf_token %}
<input name="next" type="hidden" value="{{ redirect_to }}" />
<select name="language">
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<option value="{{ language.code }}">{{ language.name_local }} ({{ language.code }})</option>
{% endfor %}
</select>
<input type="submit" value="Go" />
</form>
Which is the right syntax to replace the action attribute "/i18n/setlang/" with the url-template-tag?
EDIT:
I found the right url in the include (thanks to Daniel!):
<form action="{% url django.views.i18n.set_language %}" method="post">
As the documentation says, you can enter the full path of a view as the {% url %} parameter
{% url django.views.i18n.set_language %} # quote or unquote based on your Django version
You don't care about the URL of the include, you care about the URLs inside the include. You should look at the code or the documentation for django.conf.urls.i18n to find the setlang URL you are interested in, and use that name.

Implementing a login in django

In my base.html I placed this:
{% if user.is_authenticated %}
you are logged in!
{% else %}
<h3>Login</h3>
<form action="/login/" method="post" accept-charset="utf-8">
<label for="username">Username</label><input type="text" name="username" value="" id="username" />
<label for="password">Password</label><input type="password" name="password" value="" id="password" />
<p><input type="submit" value="Login →"></p>
</form>
{% endif %}
In urls.py:
(r'^login/$', 'django.contrib.auth.views.login'),
(r'^logout/$', 'django.contrib.auth.views.logout'),
When I accessed /login I had to make a login.html file. I created templates/registration/login.html:
{% extends "base.html" %}
{% block content %}
{% if form.errors %}
<p>Your username/pass didnt match</p>
{% endif %}
{% endblock %}
I'm not seeing the username/pass, but I'm still seeing the meaning my user is not authenticated yet.
Btw, I don't have the CSRF middleware loaded. Did I miss a step or two?
One other thing, I accessed logout.html and it went into my django admin logout page. I tried making a templates/registration/logout.html but it didn't override that part. Hrm?
Are you passing the user variable to your template context? Either explicitly, or via a context processor?
Doh. Apparently my actual template had action="/login" but when I typed it in the example here it had an end slash, I re-added the end slash and that was it.