Django从零实现用户注册与登录功能


Django用户注册和登录

在 Web 应用开发中,用户认证系统是不可或缺的核心模块之一,它负责管理用户的注册、登录、身份验证等关键功能,是保障应用安全和个性化服务的基础。 本文将详细介绍如何基于 Django 框架,使用其内置的User模型和认证工具,一步步实现用户注册和登录功能。从模型的选择、视图的编写,到模板的设计和 URL 的配置,全程结合实例代码进行讲解,帮助开发者快速掌握 Django 用户认证系统的核心用法,轻松搭建属于自己的用户管理模块。

1. 创建模型

为了实现用户的登录和注册功能,首先要创建一个用户模型,在数据库中存储用户账户信息,为了简化创建过程,我们可以直接使用Django中内置的User模型

from django.contrib.auth.models import User

1. 内置User模型

拓展一下内置User模型相关内容:

1.核心字段

内置User模型包含以下主要字段:

  • username:唯一的用户名(必填)。
  • password:加密后的密码(必填)。
  • email:电子邮箱(可选)。
  • first_name/last_name:名字 / 姓氏(可选)。
  • is_staff:是否可访问管理后台。
  • is_active:是否为活跃用户(默认为True)。
  • is_superuser:是否为超级管理员。
  • date_joined:用户注册时间。
  • last_login:最后登录时间。

2.相关方法

# 创建用户数据(将数据存储到数据库)
user = User.objects.create_user(
    username='john',
    email='john@example.com',
    password='securepass123'
)

# 验证用户(将数据与数据库中数据查询,存在则返回user对象,否则返回none)
user = authenticate(username='john', password='securepass123')

2.定义视图

由于登录和注册的表单有所不同,所以可以使用两个视图函数进行处理对应的表单

POSTGET是 HTTP 协议中两种最常用的请求方法,GET 请求常用于获取资源,POST 请求常用于提交表单或数据

1. 注册视图user_regiser

主要流程:

  1. 从表单获取数据:request.POST.get()
  2. 检查数据是否在数据库中已经存在:model.objects.filter()
  3. 将数据保存到数据库中:User.objects.create_user()
  4. 重定向到登录后的信息页面:redirect()
def user_register(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        email = request.POST.get('email')
        password = request.POST.get('password')
        confirm_password = request.POST.get('confirm_password')
        if password != confirm_password:
            messages.error(request, '两次密码输入不一致')
        elif User.objects.filter(username=username).exists():
            messages.error(request, '用户已存在')
        elif User.objects.filter(email=email).exists():
            messages.error(request, '邮箱已注册')
        else:
            # 创建用户
            user = User.objects.create_user(username=username, email=email, password=password)
            messages.success(request, '注册成功')
            # 查询用户
            user = authenticate(request, username=username, password=password)
            if user is not None:
                login(request, user)
                return redirect('index')
            else:
                return render(request, 'login.html', {'messages': messages.get_messages(request)})
    return render(request, 'login.html')

2. 登录视图user_login

主要流程:

  1. 从表单获取数据:request.POST.get()
  2. 从数据库中查询用户数据:authenticate()
  3. 存在则可以进行登录跳转,否则提示错误
def user_login(request):
    # 处理提交的表单的数据
    if request.method == 'POST':
        # 获取表单数据
        username = request.POST.get('username')
        password = request.POST.get('password')
        # 查询用户
        user = authenticate(request, username=username, password=password)
        if user is not None:
            login(request, user)    # 登录用户,将信息保存在session中,建立一个持久的登录状态

            return redirect('index')
        else:
            messages.error(request, '用户名或密码错误')
    return render(request, 'login.html', {'messages': messages.get_messages(request)})

3. 用户信息视图index

用户信息视图要将用户信息数据渲染到前端中显示,由于在登录和注册时已经进行了login()函数将用户登录,user对象信息存放在session中,所以我们可以直接从session中获取用户信息并渲染。

def index(request):
    if request.user.is_authenticated:   # 判断是否处于登录状态
        username = request.user.username
        email = request.user.email
    else:
        username = None
        email = None

    return render(request, 'index.html', {
        'username': username,
        'email': email
    })

3. 设计模板

1. 登录/注册页面login.html

注意:

  1. form标签的method属性设置为postaction属性设置为对应的url
  2. form标签下要放置{% csrf_token %}模板标签防范CSRF攻击
  3. 每个input标签的name属性填写正确,否则视图函数无法获取对应数据

示例模板如下:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>账户管理</title>
    <!-- Tailwind CSS -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- Font Awesome -->
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">

    <!-- 自定义Tailwind配置 -->
    <script>
      tailwind.config = {
        theme: {
          extend: {
            colors: {
              primary: '#165DFF',
              secondary: '#36D399',
              neutral: {
                100: '#F3F4F6',
                200: '#E5E7EB',
                700: '#374151',
                800: '#1F2937',
                900: '#111827'
              }
            },
            fontFamily: {
              inter: ['Inter', 'system-ui', 'sans-serif']
            }
          }
        }
      }
    </script>

    <!-- 自定义工具类 -->
    <style type="text/tailwindcss">
      @layer utilities {
        .content-auto {
          content-visibility: auto;
        }
        .form-input-focus {
          @apply focus:border-primary focus:ring-2 focus:ring-primary/20 focus:outline-none;
        }
        .btn-primary {
          @apply bg-primary hover:bg-primary/90 text-white font-medium py-3 px-6 rounded-lg transition-all duration-300 shadow-lg hover:shadow-xl hover:-translate-y-0.5;
        }
        .btn-secondary {
          @apply bg-white border border-primary text-primary hover:bg-primary/5 font-medium py-3 px-6 rounded-lg transition-all duration-300;
        }
        .card-animation {
          @apply transition-all duration-500 ease-in-out;
        }
      }
    </style>

    <style>
      /* 自定义动画 */
      @keyframes fadeIn {
        from { opacity: 0; transform: translateY(10px); }
        to { opacity: 1; transform: translateY(0); }
      }

      .animate-fadeIn {
        animation: fadeIn 0.5s ease-out forwards;
      }

      .bg-gradient-custom {
        background: linear-gradient(135deg, #165DFF 0%, #0A2463 100%);
      }

      /* 平滑滚动 */
      html {
        scroll-behavior: smooth;
      }

      /* 表单错误消息样式 */
      .error-message {
        @apply text-red-500 text-sm mt-1 animate-fadeIn;
      }
    </style>
  </head>
  <body class="font-inter bg-neutral-100 min-h-screen flex items-center justify-center p-4">
    {% if messages %}
      <div class="fixed top-4 right-4 space-y-2">
        {% for message in messages %}
        <div class="bg-{{ message.tags }} text-black px-4 py-2 rounded-lg shadow-lg">
          {{ message }}
        </div>
        {% endfor %}
      </div>
    {% endif %}
    <!-- 主容器 -->
    <div class="w-full max-w-md bg-white rounded-2xl shadow-xl overflow-hidden relative">
      <!-- 背景装饰 -->
      <div class="absolute inset-0 pointer-events-none">
        <div class="absolute -top-20 -right-20 w-60 h-60 bg-primary/10 rounded-full blur-3xl"></div>
        <div class="absolute -bottom-32 -left-20 w-80 h-80 bg-secondary/10 rounded-full blur-3xl"></div>
      </div>

      <!-- 表单卡片 -->
      <div class="p-8 md:p-10 flex flex-col justify-center card-animation relative z-10">
        <!-- 注册表单 -->
        <div id="registerForm" class="animate-fadeIn">
          <h2 class="text-2xl md:text-3xl font-bold text-neutral-800 mb-6">创建账户</h2>

          <form id="registrationForm" class="space-y-5" method="post" action="{% url 'register' %}">
            {% csrf_token %}
            <div>
              <label for="registerUsername" class="block text-sm font-medium text-neutral-700 mb-1">用户名</label>
              <div class="relative">
                <span class="absolute inset-y-0 left-0 flex items-center pl-3 text-neutral-500">
                  <i class="fa fa-user"></i>
                </span>
                <input
                  type="text"
                  id="registerUsername"
                  name="username"
                  placeholder="请输入用户名"
                  class="w-full pl-10 pr-4 py-3 rounded-lg border border-neutral-200 form-input-focus transition-all duration-300"
                  required
                >
              </div>
              <div id="usernameError" class="error-message hidden"></div>
            </div>

            <div>
              <label for="registerEmail" class="block text-sm font-medium text-neutral-700 mb-1">邮箱</label>
              <div class="relative">
                <span class="absolute inset-y-0 left-0 flex items-center pl-3 text-neutral-500">
                  <i class="fa fa-envelope"></i>
                </span>
                <input
                  type="email"
                  id="registerEmail"
                  name="email"
                  placeholder="请输入邮箱"
                  class="w-full pl-10 pr-4 py-3 rounded-lg border border-neutral-200 form-input-focus transition-all duration-300"
                  required
                >
              </div>
              <div id="emailError" class="error-message hidden"></div>
            </div>

            <div>
              <label for="registerPassword" class="block text-sm font-medium text-neutral-700 mb-1">密码</label>
              <div class="relative">
                <span class="absolute inset-y-0 left-0 flex items-center pl-3 text-neutral-500">
                  <i class="fa fa-lock"></i>
                </span>
                <input
                  type="password"
                  id="registerPassword"
                  name="password"
                  placeholder="请输入密码"
                  class="w-full pl-10 pr-4 py-3 rounded-lg border border-neutral-200 form-input-focus transition-all duration-300"
                  required
                >
                <button
                  type="button"
                  id="toggleRegisterPassword"
                  class="absolute inset-y-0 right-0 flex items-center pr-3 text-neutral-500 hover:text-primary transition-colors"
                >
                  <i class="fa fa-eye-slash"></i>
                </button>
              </div>
              <div id="passwordError" class="error-message hidden"></div>
            </div>

            <div>
              <label for="registerConfirmPassword" class="block text-sm font-medium text-neutral-700 mb-1">确认密码</label>
              <div class="relative">
                <span class="absolute inset-y-0 left-0 flex items-center pl-3 text-neutral-500">
                  <i class="fa fa-lock"></i>
                </span>
                <input
                  type="password"
                  id="registerConfirmPassword"
                  name="confirm_password"
                  placeholder="请再次输入密码"
                  class="w-full pl-10 pr-4 py-3 rounded-lg border border-neutral-200 form-input-focus transition-all duration-300"
                  required
                >
                <button
                  type="button"
                  id="toggleConfirmPassword"
                  class="absolute inset-y-0 right-0 flex items-center pr-3 text-neutral-500 hover:text-primary transition-colors"
                >
                  <i class="fa fa-eye-slash"></i>
                </button>
              </div>
              <div id="confirmPasswordError" class="error-message hidden"></div>
            </div>
            <button type="submit" class="btn-primary w-full">
              注册账户
            </button>

            <div class="text-center mt-4">
              <button id="showLoginForm" class="btn-secondary w-full">
                已有账户? 登录
              </button>
            </div>
          </form>
        </div>

        <!-- 登录表单 -->
        <div id="loginForm" class="hidden animate-fadeIn">
          <h2 class="text-2xl md:text-3xl font-bold text-neutral-800 mb-6">欢迎回来</h2>

          <form id="loginForm" class="space-y-5" method="post" action="{% url 'login' %}">
            {% csrf_token %}
            <div>
              <label for="loginEmail" class="block text-sm font-medium text-neutral-700 mb-1">邮箱或用户名</label>
              <div class="relative">
                <span class="absolute inset-y-0 left-0 flex items-center pl-3 text-neutral-500">
                  <i class="fa fa-user"></i>
                </span>
                <input
                  type="text"
                  id="loginEmail"
                  name="username"
                  placeholder="请输入用户名"
                  class="w-full pl-10 pr-4 py-3 rounded-lg border border-neutral-200 form-input-focus transition-all duration-300"
                  required
                >
              </div>
              <div id="loginEmailError" class="error-message hidden"></div>
            </div>

            <div>
              <label for="loginPassword" class="block text-sm font-medium text-neutral-700 mb-1">密码</label>
              <div class="relative">
                <span class="absolute inset-y-0 left-0 flex items-center pl-3 text-neutral-500">
                  <i class="fa fa-lock"></i>
                </span>
                <input
                  type="password"
                  id="loginPassword"
                  name="password"
                  placeholder="请输入密码"
                  class="w-full pl-10 pr-4 py-3 rounded-lg border border-neutral-200 form-input-focus transition-all duration-300"
                  required
                >
                <button
                  type="button"
                  id="toggleLoginPassword"
                  class="absolute inset-y-0 right-0 flex items-center pr-3 text-neutral-500 hover:text-primary transition-colors"
                >
                  <i class="fa fa-eye-slash"></i>
                </button>
              </div>
              <div id="loginPasswordError" class="error-message hidden"></div>
            </div>

            <div class="flex items-center justify-between">
              <div class="flex items-center">
                <input
                  id="rememberMe"
                  name="rememberMe"
                  type="checkbox"
                  class="w-4 h-4 rounded border-neutral-300 text-primary focus:ring-primary/20"
                >
                <label for="rememberMe" class="ml-2 block text-sm text-neutral-600">记住我</label>
              </div>
              <div class="text-sm">
                <a href="#" class="font-medium text-primary hover:text-primary/80">忘记密码?</a>
              </div>
            </div>

            <button type="submit" class="btn-primary w-full">
              登录
            </button>

            <div class="text-center mt-4">
              <button id="showRegisterForm" class="btn-secondary w-full">
                还没有账户? 注册
              </button>
            </div>
          </form>
        </div>
      </div>
    </div>

    <script>
      // 切换登录/注册表单
      document.addEventListener('DOMContentLoaded', function() {
        const showLoginForm = document.getElementById('showLoginForm');
        const showRegisterForm = document.getElementById('showRegisterForm');
        const registerForm = document.getElementById('registerForm');
        const loginForm = document.getElementById('loginForm');

        showLoginForm.addEventListener('click', function() {
          registerForm.classList.add('hidden');
          loginForm.classList.remove('hidden');
        });

        showRegisterForm.addEventListener('click', function() {
          registerForm.classList.remove('hidden');
          loginForm.classList.add('hidden');
        });

        // 密码显示/隐藏功能
        const togglePasswordButtons = [
          document.getElementById('toggleRegisterPassword'),
          document.getElementById('toggleConfirmPassword'),
          document.getElementById('toggleLoginPassword')
        ];

        togglePasswordButtons.forEach(button => {
          button.addEventListener('click', function() {
            const input = this.previousElementSibling;
            const icon = this.querySelector('i');

            if (input.type === 'password') {
              input.type = 'text';
              icon.classList.remove('fa-eye-slash');
              icon.classList.add('fa-eye');
            } else {
              input.type = 'password';
              icon.classList.remove('fa-eye');
              icon.classList.add('fa-eye-slash');
            }
          });
        });
      });
    </script>
  </body>
</html>

2. 用户信息页index.html

该页面用于渲染用户数据信息,使用模板标签将index视图中传入的context部分数据进行渲染(usernameemail

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>欢迎用户{{ username }},邮箱:{{ email }}</h1>
</body>
</html>

4. 其他配置

1. urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', views.user_login, name='login'),
    path('register/', views.user_register, name='register'),
    path('index/', views.index, name='index')
]

2. admin.py

注册User模型

admin.site.register(User)

3. 后台管理页面

使用django内置的后台管理页面/admin来查看数据库中的信息。前提是要创建一个超级用户进行登录。终端执行命令:

 python manage.py createsupperuser

5. 项目结构

auth_demo/
├── auth/
   ├── __init__.py
   ├── __pycache__/
   ├── admin.py
   ├── apps.py
   ├── migrations/
   ├── models.py
   ├── tests.py
   └── views.py
├── auth_demo/
   ├── __init__.py
   ├── __pycache__/
   ├── asgi.py
   ├── settings.py
   ├── urls.py
   └── wsgi.py
├── db.sqlite3
├── manage.py
└── templates/
    ├── index.html
    └── login.html

6. 项目源代码

bird-six/auth_demo: django登录注册

0 条评论

发表评论

暂无评论,欢迎发表您的观点!