【Django】通过几分钟即可实现的用户注册和登录功能

首先

目标

在这篇文章中,我们将介绍如何使用Django框架快速实现用户注册和登录功能!通过这种方法,您可以在Web应用程序开发中高效地实现繁琐的认证功能。

这篇文章的主要目的是解释如何使用Django快速实现用户注册和登录功能。
这篇文章适合对Django感兴趣并准备开始学习的初学者?‍♂️。

关于Django框架

Django是一个由Python开发的开源Web应用程序框架。Django已经内置了许多有用的功能。因此,开发者可以摆脱繁琐的基本功能实现,专注于应用功能的开发,这也是它的本质目的。

在Django中,已经准备好了用户认证和安全功能等重要元素,你可以通过使用插件和扩展功能来进一步增强功能。因此,即使是初学者也能相对容易地开发安全的Web应用程序。

那么,现在我们就来试试用Django实现用户注册和登录功能吧!

实践

供参考,本次介绍的源代码已在GitHub上公开!

 

以下是Django文件的内容。

 

搭建开发环境

    • OS: macOS 13.2.1

 

    • Python: 3.10.3

 

    Django: 4.1.7

创建和配置Django项目

首先,我们将为这个项目创建一个虚拟环境。

% python -m venv venv

我要进入虚拟环境。

% source venv/bin/activate

我将安装Django。

(venv)% pip install --upgade pip
(venv)% pip install Django

我要创建一个Django项目。

(venv)% django-admin startproject project .

我将编辑settings.py文件。

...

ALLOWED_HOSTS = ['*']

...

让我们启动开发用服务器吧。

(venv)% python manage.py runserver
Screenshot 2023-03-28 at 9.43.54.png

创建自定义用户

在Django框架中,建议创建自定义的用户模型而不是使用Django的默认用户模型。
在Django应用程序开发的早期阶段创建自定义用户模型可以带来一些好处,例如在以后想要为用户添加年龄或地址等属性时更容易进行修改。

我会创建一个名为”accounts”的应用程序。

(venv)% python manage.py startapp accounts

在settings.py的INSTALLED_APPS中添加accounts。
此外,为了将即将创建的自定义用户用作认证用户模型,将其配置添加进去。

...
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'accounts' # 追加
]

AUTH_USER_MODEL = "accounts.User" # カスタムユーザーを認証用ユーザーとして登録

...

创建自定义用户模型。

from django.db import models
from django.contrib.auth.models import (BaseUserManager,
                                        AbstractBaseUser,
                                        PermissionsMixin)
from django.utils.translation import gettext_lazy as _


class UserManager(BaseUserManager):
    def _create_user(self, email, account_id, password, **extra_fields):
        email = self.normalize_email(email)
        user = self.model(email=email, account_id=account_id, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)

        return user

    def create_user(self, email, account_id, password=None, **extra_fields):
        extra_fields.setdefault('is_active', True)
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(
            email=email,
            account_id=account_id,
            password=password,
            **extra_fields,
        )

    def create_superuser(self, email, account_id, password, **extra_fields):
        extra_fields['is_active'] = True
        extra_fields['is_staff'] = True
        extra_fields['is_superuser'] = True
        return self._create_user(
            email=email,
            account_id=account_id,
            password=password,
            **extra_fields,
        )


class User(AbstractBaseUser, PermissionsMixin):

    account_id = models.CharField(
        verbose_name=_("account_id"),
        unique=True,
        max_length=10
    )
    email = models.EmailField(
        verbose_name=_("email"),
        unique=True
    )
    first_name = models.CharField(
        verbose_name=_("first_name"),
        max_length=150,
        null=True,
        blank=False
    )
    last_name = models.CharField(
        verbose_name=_("last_name"),
        max_length=150,
        null=True,
        blank=False
    )
    birth_date = models.DateField(
        verbose_name=_("birth_date"),
        blank=True,
        null=True
    )
    is_superuser = models.BooleanField(
        verbose_name=_("is_superuer"),
        default=False
    )
    is_staff = models.BooleanField(
        verbose_name=_('staff status'),
        default=False,
    )
    is_active = models.BooleanField(
        verbose_name=_('active'),
        default=True,
    )
    created_at = models.DateTimeField(
        verbose_name=_("created_at"),
        auto_now_add=True
    )
    updated_at = models.DateTimeField(
        verbose_name=_("updateded_at"),
        auto_now=True
    )

    objects = UserManager()

    USERNAME_FIELD = 'account_id' # ログイン時、ユーザー名の代わりにaccount_idを使用
    REQUIRED_FIELDS = ['email']  # スーパーユーザー作成時にemailも設定する

    def __str__(self):
        return self.account_id

让我们在管理界面进行用户注册。
为了能够在管理界面中编辑自定义用户,我们将在admin.py中添加设置。

from django.contrib import admin
from django.contrib.auth.models import Group

from .models import User


admin.site.register(User, UserAdmin)  # Userモデルを登録
admin.site.unregister(Group)  # Groupモデルは不要のため非表示にします

我要进行迁移。

(venv)% python manage.py makemigrations
(venv)% python mange.py migrate

为了登录到管理界面,我们需要创建一个超级用户。

(venv)% python manage.py createsuperuser

请提供账户ID、邮箱和密码的输入,并输入任意值。

启动开发服务器

(venv)% python manage.py runserver

在浏览器中,访问 http://127.0.0.1:8000/admin,并使用您创建的超级用户账号进行登录。

Screenshot 2023-03-28 at 20.54.40.png

如果能够完成到这一步,定制用户已成功创建,没有任何问题?‍♂️

用户注册功能的实施

使用Django的UserCreationForm创建用户注册表单。

请在accounts文件夹下创建forms.py。

from django.contrib.auth.forms import UserCreationForm

from .models import User


class SignUpForm(UserCreationForm):
    class Meta:
        model = User
        fields = (
            "account_id",
            "email",
            "first_name",
            "last_name",
            "birth_date",
        )

接下来,创建视图。

from django.contrib.auth import login, authenticate
from django.views.generic import TemplateView, CreateView
from django.urls import reverse_lazy
from .forms import SignUpForm


class IndexView(TemplateView):
    """ ホームビュー """
    template_name = "index.html"


class SignupView(CreateView):
    """ ユーザー登録用ビュー """
    form_class = SignUpForm # 作成した登録用フォームを設定
    template_name = "accounts/signup.html" 
    success_url = reverse_lazy("accounts:index") # ユーザー作成後のリダイレクト先ページ

    def form_valid(self, form):
        # ユーザー作成後にそのままログイン状態にする設定
        response = super().form_valid(form)
        account_id = form.cleaned_data.get("account_id")
        password = form.cleaned_data.get("password1")
        user = authenticate(account_id=account_id, password=password)
        login(self.request, user)
        return response

我将创建一个模板。
在manage.py的同一级目录下创建一个templates文件夹,并将模板文件放置其中。

由于本文关注用户注册和登录功能,因此UI设计仅仅轻量地使用了Bootstrap?‍♂️

将创建base.html文件。

<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <!-- Bootstrap5のCDNを設定 -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
    integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
    integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
    crossorigin="anonymous"></script>
  <title>Account</title>
</head>

<body>
  <div class="container mx-auto text-center">
    {% block title %}
    {% endblock %}
    {% block content %}
    {% endblock %}
  </div>
</body>

</html>

创建index.html文件。

{% extends 'base.html' %}

{% block title %}
{% if user.is_authenticated %}
<div class="h1">MyPage</div>
{% else %}
<div class="h1">Main</div>
{% endif %}
{% endblock %}

{% block content %}
{% if user.is_authenticated %}
<div class="h2">Welcome {{ user.first_name }} {{ user.last_name }}</div>
{% else %}
<a href="{% url 'accounts:signup' %}" class="btn btn-primary">Signup</a>
{% endif %}
{% endblock %}

这次,我们将根据登录与否来实现顶部页面显示内容的不同。
具体来说,将会显示以下内容。

状態タイトルコンテンツ非ログイン時MainSignUp, Loginボタン表示ログイン時MyPageユーザー情報表示

现在我们将创建一个重要的用户注册表单模板。

{% extends 'base.html' %}

{% block title %}
<div class="h1">Create account</div>
{% endblock %}

{% block content %}
<div>
  <br>
  <form method="POST">
    {% csrf_token %}
    {{ form.non_field_errors }}
    {% for field in form %}
    {{ field.label }}
    {{ field }}
    {{ field.errors }}
    <br>
    {% endfor %}
    <div class="mt-3">
      <button type="submit" class="btn btn-primary">Create</button>
      <a href="{% url 'accounts:index' %}" class="btn btn-secondary">Back</a>
    </div>
  </form>
</div>
{% endblock %}

我們將對出現在這裡的模板標籤進行解釋。

{% csrf_token %}
CSRF(Cross-Site Request Forgery)対策として使用されます。フォームを使ったPOSTリクエストを送信する際に、このタグを使ってCSRFトークンを自動的に生成し、フォーム内に埋め込むことができます。このトークンを使用することで、不正なPOSTリクエストを防ぐことができます。デフォルトでは、このタグを付けずにPOSTリクエストを行った場合エラーが発生します。

{{ form.non_field_errors }}
フォーム全体に関連するエラーメッセージを表示するための変数です。例えば、フォーム内の2つのフィールド間の関係に問題がある場合や、全体的なバリデーションエラーが発生した場合に、この変数を使ってエラーメッセージを表示することができます。

{% for field in form %} {% endfor %}
Djangoテンプレートタグのforループ構文です。この構文を使用することで、フォーム内のすべてのフィールドを繰り返し処理し、それぞれのフィールドに対してHTMLを生成することができます。

{{ field.label }}
フィールド名を表示するタグ

{{ field }}
フィールドの入力フォーム(ウィジェット)を表示するためのタグです

{{ field.errors }}
任意のフィールドに関連するエラーメッセージを表示するための変数

在settings.py中设置模板文件放置的目录位置。

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'], # templatesディレクトリを設定
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

为了能够从浏览器中操作注册表单,我们将创建一个路由。

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include("accounts.urls")) # accounts.urls.pyを読み込むための設定を追加
]

在accounts目录中新建一个urls.py文件,并写入配置信息。

from django.urls import path

from . import views

app_name = "accounts"

urlpatterns = [
    path("", views.IndexView.as_view(), name="index"),
    path('signup/', views.SignupView.as_view(), name="signup"),
]

如果能做到这一点,让我们尝试验证已创建用户注册表单的操作!

启动开发服务器,并在浏览器中访问http://127.0.0.1:8000,确认是否显示主页。

Screenshot 2023-03-28 at 21.57.08.png

主页已经显示出来了!

点击”注册”按钮,我们将从”创建账户”页面开始创建一个账户。

Screenshot 2023-03-29 at 8.23.57.png

在表格中输入用户信息,并点击”创建”按钮。(在这里,出生日期可以留空。)

Screenshot 2023-03-29 at 8.24.57.png

用户创建后,身份验证也通过了,MyPage页面就会显示出来?‍♂️。现在,用户注册功能已经完成了!

实现登录功能

我们来实现登录功能!我们将创建一个登录用的表单。

from django.contrib.auth.forms import UserCreationForm, AuthenticationForm # 追加

from .models import User


class SignUpForm(UserCreationForm):
    class Meta:
        model = User
        fields = (
            "account_id",
            "email",
            "first_name",
            "last_name",
            "birth_date",
        )


# ログインフォームを追加
class LoginFrom(AuthenticationForm):
    class Meta:
        model = User

创建一个LoginView。

from django.contrib.auth import login, authenticate
from django.views.generic import TemplateView, CreateView
from django.contrib.auth.views import LoginView as BaseLoginView
from django.urls import reverse_lazy
from .forms import SignUpForm, LoginFrom # ログインフォームをimport


class IndexView(TemplateView):
    template_name = "index.html"


class SignupView(CreateView):
    form_class = SignUpForm
    template_name = "accounts/signup.html"
    success_url = reverse_lazy("accounts:index")

    def form_valid(self, form):
        # login after signup
        response = super().form_valid(form)
        account_id = form.cleaned_data.get("account_id")
        password = form.cleaned_data.get("password1")
        user = authenticate(account_id=account_id, password=password)
        login(self.request, user)
        return response

# ログインビューを作成
class LoginView(BaseLoginView):
    form_class = LoginFrom
    template_name = "accounts/login.html"

我将创建登录模板。

{% extends 'base.html' %}

{% block title %}
<div class="h1">Login</div>
{% endblock %}
{% block content %}
<form method="post">
    {% csrf_token %}
    {{ form.non_field_errors }}
    {% for field in form %}
    {{ field.label }}
    {{ field }}
    {{ field.errors }}
    <br>
    {% endfor %}
    <div class="mt-3">
        <button type="submit" class="btn btn-primary">Login</button>
        <a href="{% url 'accounts:index' %}" class="btn btn-secondary">Back</a>
    </div>
</form>
{% endblock %}

设置登录页面的URL。

from django.urls import path

from . import views

app_name = "accounts"

urlpatterns = [
    path("", views.IndexView.as_view(), name="index"),
    path('signup/', views.SignupView.as_view(), name="signup"),
    path('login/', views.LoginView.as_view(), name="login"),
]

在登录后重定向的URL,将其设置在settings.py中。

...
LOGIN_REDIRECT_URL = "accounts:index"

通过这个,登录后会自动跳转到首页。

实现登出功能

我們將實施登出功能。

在views.py中创建一个LogoutView。

from django.contrib.auth import login, authenticate
from django.views.generic import TemplateView, CreateView
from django.contrib.auth.views import LoginView as BaseLoginView,  LogoutView as BaseLogoutView
from django.urls import reverse_lazy
from .forms import SignUpForm, LoginFrom


class IndexView(TemplateView):
    template_name = "index.html"


class SignupView(CreateView):
    form_class = SignUpForm
    template_name = "accounts/signup.html"
    success_url = reverse_lazy("accounts:index")

    def form_valid(self, form):
        # login after signup
        response = super().form_valid(form)
        account_id = form.cleaned_data.get("account_id")
        password = form.cleaned_data.get("password1")
        user = authenticate(account_id=account_id, password=password)
        login(self.request, user)
        return response


class LoginView(BaseLoginView):
    form_class = LoginFrom
    template_name = "accounts/login.html"

# LogoutViewを追加
class LogoutView(BaseLogoutView):
    success_url = reverse_lazy("accounts:index")

我們將在urls.py文件中設置logout的路徑。

from django.urls import path

from . import views

app_name = "accounts"

urlpatterns = [
    path("", views.IndexView.as_view(), name="index"),
    path('signup/', views.SignupView.as_view(), name="signup"),
    path('login/', views.LoginView.as_view(), name="login"),
    path('logout/', views.LogoutView.as_view(), name="logout"), # 追加
]

在模板中添加登出按钮。

{% extends 'base.html' %}

{% block title %}
{% if user.is_authenticated %}
<div class="h1">MyPage</div>
{% else %}
<div class="h1">Main</div>
{% endif %}
{% endblock %}

{% block content %}
{% if user.is_authenticated %}
<div class="h2">Welcome {{ user.first_name }} {{ user.last_name }}</div>
<!-- ↓追加 -->
<a href="{% url 'accounts:logout' %}" class="btn btn-primary">Logout</a> 
{% else %}
<a href="{% url 'accounts:signup' %}" class="btn btn-primary">Signup</a>
<a href="{% url 'accounts:login' %}" class="btn btn-primary">Login</a>
{% endif %}
{% endblock %}

在settings.py中设置登出后的重定向URL。这次我们设置为跳转到登录页面。

...
LOGIN_REDIRECT_URL = "accounts:index"
LOGOUT_REDIRECT_URL = "accounts:login"

通过在我的页面上点击“Logout”按钮即可进行登出操作。

登录和注销操作的确认

用户创建后处于登录状态,首先点击注销按钮,然后进行注销。

Screenshot 2023-03-29 at 20.15.50.png
Screenshot 2023-03-29 at 20.16.52.png
Screenshot 2023-03-29 at 20.19.06.png
Screenshot 2023-03-29 at 20.19.54.png

总结

由于Django内置了认证功能,我们可以轻松实现用户注册和登录功能!
这篇文章是为那些急于学习Django认证功能的人准备的推荐文章,接下来我们还会介绍表单定制等内容,敬请期待?

bannerAds