Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I extend the Django "login" form?

So, right now I'm doing the basic login. In urls.py, I go to django contrib login:

(r'^login/?$','django.contrib.auth.views.login',{'template_name':'login.html'}),

That shoots it to here:

@csrf_protect
@never_cache
def login(request, template_name='registration/login.html',
          redirect_field_name=REDIRECT_FIELD_NAME,
          authentication_form=AuthenticationForm):

That view uses the AuthenticationForm forms model:

class AuthenticationForm(forms.Form):
    """
    Base class for authenticating users. Extend this to get a form that accepts
    username/password logins.
    """
    username = forms.CharField(label=_("Username"), max_length=30)
    password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)

So...my objective is to to change the username form! By adding this to it: widget = forms.TextInput(attrs={'placeholder': 'username'}). That's it. That's all I want to add to the username input box. But, I don't want to change the actual django forms.py file, since that's part of django contrib and I feel bad changing that file.

What do I do? Should I create a form that extends AuthenticationForm? If so, how do I import that? And how do I pass that in as an argument via my urls.py? I don't know what to do.

like image 483
TIMEX Avatar asked Jan 10 '11 05:01

TIMEX


People also ask

How do you extend user models?

Extending the existing User model If you wish to store information related to User , you can use a one-to-one relationship to a model containing the fields for additional information. This one-to-one model is often called a profile model, as it might store non-auth related information about a site user.


2 Answers

You need to subclass the AuthenticationForm class, and then you need to change your urls.py,

class MyAuthenticationForm(AuthenticationForm):
    # add your form widget here
    widget = .....

Then import this class into your urls.py file and update the call,

(r'^login/?$','django.contrib.auth.views.login',{'template_name':'login.html', 'authentication_form':MyAuthenticationForm}),

I'm too tired to lookup the links on the documentation site to see what type of field you need to use but this should do the trick to get you started without having to modify the django forms.py which you definitely should feel bad about changing!

like image 141
milkypostman Avatar answered Oct 06 '22 01:10

milkypostman


Here's another solution using path instead of the deprecated url, to create a custom version of the page and form to reset your password after following a link sent out in an email. I'm using Django 2 and Python 3.

In my api app folder, I've created a folder templates/account with three files inside.

base.html:

<!DOCTYPE html>
<html>
  <head>
    <title>{% block head_title %}{% endblock %}</title>
    {% block extra_head %}
    {% endblock %}
  </head>
  <body>
    {% block body %}
    {% block content %}
    {% endblock %}
    {% endblock %}
    {% block extra_body %}
    {% endblock %}
  </body>
</html>

password_reset_complete.html:

{% extends "account/base.html" %}

{% block content %}

<h3>Password reset successful</h3>

<p>Your password has been changed. Please log in using the link below.</p>

<p><a href="/login/">Login</a></p>

{% endblock %}

password_reset_confirm.html:

{% extends 'account/base.html' %}

{% block content %}
  {% if validlink %}
    <h3>Enter your new password</h3>
    <form method="post">
      {% csrf_token %}
      {{ form.as_p }}
      <button type="submit">Change password</button>
    </form>
  {% else %}
    <p>
      The password reset link was invalid, possibly because it has already been used.
      Please request a new password reset.
    </p>
  {% endif %}
{% endblock %}

In my api app folder, I've created a file forms.py for my custom form which extends the default SetPasswordForm form.

forms.py:

from django import forms
from django.contrib.auth.forms import SetPasswordForm
from django.utils.translation import gettext, gettext_lazy as _
from django.contrib.auth import password_validation

class SetPasswordFormCustom(SetPasswordForm):
    new_password1 = forms.CharField(
        label=_("New password custom"),
        widget=forms.PasswordInput,
        strip=False,
    )
    new_password2 = forms.CharField(
        label=_("New password confirmation custom"),
        strip=False,
        widget=forms.PasswordInput,
        help_text=password_validation.password_validators_help_text_html(),
    )

And finally urls.py:

# api/urls.py
from django.urls import include, path
from django.contrib.auth import views
from django.conf.urls import include, url
from django.views.generic.base import RedirectView
from .forms import SetPasswordFormCustom

urlpatterns = [
...
path('reset/<uidb64>/<token>/',
    views.PasswordResetConfirmView.as_view(template_name='account/password_reset_confirm.html',
    form_class=SetPasswordFormCustom),
    name='password_reset_confirm'),
path('reset/done/', views.PasswordResetCompleteView.as_view(template_name='account/password_reset_complete.html'), name='password_reset_complete'),

]

Note 'from .forms import SetPasswordFormCustom'. The . allows a file in the same folder to be imported.

My reason for doing all this is that I haven't got reset password to work in my native React frontend, so I'm having to use the Django templates for this function. I want to be able to restyle it and have links that fit with my app as nearly as possible. But this method should generally allow you to create your own custom version of a Django auth form.

like image 28
Little Brain Avatar answered Oct 06 '22 01:10

Little Brain