Django 2.0 項目實戰: 擴展Django自帶User模型,實現用戶註冊與登錄

2021-02-07 Python Web與Django開發

用戶的註冊與登陸是一個網站應該具有的基本功能。網上很多Django關於實現用戶註冊與登錄的教程都是用Django 1.X寫的,比較老了,所以小編我覺得有必要親自動手用Django 2.0重寫用戶註冊與登陸教程。另外網上很多教程忽略了Django Auth模塊自帶的User模型而重新建立了自己用戶的模型,小編我一看到這種教程就會投去一臉鄙視的目光。一個網站會什麼要有兩個User模型? Why? Why? 本文會教你在不自建User模型的情況下實現用戶的註冊與登陸。另外,我們會對Django Auth自帶的User模型進行擴展,允許用戶添加更多的個人信息。由於全文非常的長,我們會分3部分推送,歡迎訂閱我的微信公眾號【Python與Django大咖之路】獲取最新文章。

總體開發思路


我們要利用Django 2.0開發一個叫users的app,來實現以下6項功能。我們一共將分3篇文章來介紹。本文只介紹用戶的註冊登錄部分。

由於Django Auth自帶的User模型欄位有限,我們還需要自定義模型UserProfile對其擴展。

Django Auth模塊自帶User模型所包含欄位

username:用戶名

email: 電子郵件

password:密碼

first_name:名

last_name:姓

is_active: 是否為活躍用戶。默認是True

is_staff: 是否為員工。默認是False

is_superuser: 是否為管理員。默認是False

date_joined: 加入日期。系統自動生成。

自定義的UserProfile模型

user: 與User是1對1關係

org:用戶名

telephone: 電話

mod_date: 最後修改日期。系統自動生成


第一步: 創建名叫users的app並修改設置setting.py


我們假設你已經利用Django創建了一個叫mysite的項目,你可以在終端cmd窗口cd進入這個目錄,並輸入以下命令創建一個叫users的app。

python manage.py startapp users 

然後找到mysite/settings.py裡將'users' 加到INSTALLED_APPS裡,如下圖所示。

INSTALLED_APPS = [
   'reg.apps.RegConfig',
   'django.contrib.admin',
   'django.contrib.auth',
   'django.contrib.contenttypes',
   'django.contrib.sessions',
   'django.contrib.messages',
   'django.contrib.staticfiles',
   'users',
]


第二步: 建立名叫UserProfile的模型(Model)


我們並沒有改變Django Auth自帶的User模型,也沒有建立新的User模型。UserProfile只是對User模型的擴展, 與User是1對1的關係。找到users/models.py, 並創建如下UserProfile模型。由於我們引用了Django Auth自帶的User模型,所以我們必需開始先把它import進來。


from django.db import models
from django.contrib.auth.models import User

class UserProfile(models.Model):

   user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')

   org = models.CharField(
       'Organization', max_length=128, blank=True)

   telephone = models.CharField(
       'Telephone', max_length=50, blank=True)

   mod_date = models.DateTimeField('Last modified', auto_now=True)

   class Meta:
       verbose_name = 'User Profile'

   def __str__(self):
       return self.user

然後你可以在終端輸入以下命令,就可以創建UserProfile的數據表。

python manage.py makemigrations 
python manage.py migrate 


第三步:配置URL

小編我是自上而下思考的,所以我習慣上先編寫URL,再寫視圖view。從URL配置上你應該可以直接理解我們想實現的6個功能。下面是users/urls.py裡的全部代碼。你應該注意到,我們給動態連結/register/取了個名字’register', 這樣我們就可以在html模板裡可以通過{% url 'users:register' %}調用這個連結了。

from django.urls import re_path
from . import views

app_name = 'users'
urlpatterns = [
   re_path(r'^register/$', views.register, name='register'),
   re_path(r'^login/$', views.login, name='login'),
   re_path(r'^user/(?P<pk>\d+)/profile/$', views.profile, name='profile'),
   re_path(r'^user/(?P<pk>\d+)/profile/update/$', views.profile_update, name='profile_update'),
   re_path(r'^user/(?P<pk>\d+)/pwdchange/$', views.pwd_change, name='pwd_change'),
   re_path(r'^logout/$', views.logout, name='logout'),
]

另外找到mysite/urls.py, 把我們這個app的URLs也加進去,如下圖所示。這樣當用戶訪問/accounts/register/時,瀏覽器會調用views.py裡的register函數。

urlpatterns = [
   url(r'^admin/', admin.site.urls),
   url(r'^accounts/', include('users.urls')),

]

第四步: 編寫試圖(view)

我們需要編寫register和login兩個視圖, 讓用戶通過表單向我們提交數據,並處理這些數據。因為這兩個視圖都需要用表單,所以我們先在users目錄下新建forms.py, 然後創建兩個form,一個RegistrationForm,一個LoginForm。代碼如下:

from django import forms
from django.contrib.auth.models import User
import re


def email_check(email):
   pattern = re.compile(r"\"?([-a-zA-Z0-9.`?{}]+@\w+\.\w+)\"?")
   return re.match(pattern, email)


class RegistrationForm(forms.Form):

   username = forms.CharField(label='Username', max_length=50)
   email = forms.EmailField(label='Email',)
   password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
   password2 = forms.CharField(label='Password Confirmation', widget=forms.PasswordInput)

   # Use clean methods to define custom validation rules

   def clean_username(self):
       username = self.cleaned_data.get('username')

       if len(username) < 6:
           raise forms.ValidationError("Your username must be at least 6 characters long.")
       elif len(username) > 50:
           raise forms.ValidationError("Your username is too long.")
       else:
           filter_result = User.objects.filter(username__exact=username)
           if len(filter_result) > 0:
               raise forms.ValidationError("Your username already exists.")

       return username

   def clean_email(self):
       email = self.cleaned_data.get('email')

       if email_check(email):
           filter_result = User.objects.filter(email__exact=email)
           if len(filter_result) > 0:
               raise forms.ValidationError("Your email already exists.")
       else:
           raise forms.ValidationError("Please enter a valid email.")

       return email

   def clean_password1(self):
       password1 = self.cleaned_data.get('password1')

if len(password1) < 6:
           raise forms.ValidationError("Your password is too short.")
       elif len(password1) > 20:
           raise forms.ValidationError("Your password is too long.")

       return password1

   def clean_password2(self):
       password1 = self.cleaned_data.get('password1')
       password2 = self.cleaned_data.get('password2')

       if password1 and password2 and password1 != password2:
           raise forms.ValidationError("Password mismatch. Please enter again.")

       return password2


class LoginForm(forms.Form):

   username = forms.CharField(label='Username', max_length=50)
   password = forms.CharField(label='Password', widget=forms.PasswordInput)

   # Use clean methods to define custom validation rules

   def clean_username(self):
       username = self.cleaned_data.get('username')

       if email_check(username):
           filter_result = User.objects.filter(email__exact=username)
           if not filter_result:
               raise forms.ValidationError("This email does not exist.")
       else:
           filter_result = User.objects.filter(username__exact=username)
           if not filter_result:
raise forms.ValidationError("This username does not exist. Please register first.")

       return username

千萬不要上面的代碼嚇到。之所以代碼這麼長是因為我們用clean方法加入了很多表單驗證項,比如檢查用戶名是否過短,用戶名是否已經存在。如果你把表單驗證拿掉,其實代碼非常少。我之所以加上這些驗證規則,是讓你了解最真實的網站開發。

當然你也可以不用新建forms.py而直接在html模板裡寫表單,但我並不建議這麼做。用forms.py的好處顯而易見: 

我們的視圖users/views.py是這樣子的。

from django.shortcuts import render, get_object_or_404
from django.contrib.auth.models import User
from .models import UserProfile
from django.contrib import auth
from .forms import RegistrationForm, LoginForm, ProfileForm, PwdChangeForm
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.contrib.auth.decorators import login_required


def register(request):
   if request.method == 'POST':

       form = RegistrationForm(request.POST)
       if form.is_valid():
           username = form.cleaned_data['username']
           email = form.cleaned_data['email']
           password = form.cleaned_data['password2']

           # 使用內置User自帶create_user方法創建用戶,不需要使用save()
             user = User.objects.create_user(username=username, password=password, email=email)

           # 如果直接使用objects.create()方法後不需要使用save()
             user_profile = UserProfile(user=user)
           user_profile.save()

           return HttpResponseRedirect("/accounts/login/")

   else:
       form = RegistrationForm()

   return render(request, 'users/registration.html', {'form': form})


def login(request):
   if request.method == 'POST':
       form = LoginForm(request.POST)
       if form.is_valid():
           username = form.cleaned_data['username']
           password = form.cleaned_data['password']

           user = auth.authenticate(username=username, password=password)

           if user is not None and user.is_active:
               auth.login(request, user)
               return HttpResponseRedirect(reverse('users:profile', args=[user.id]))

           else:
               # 登陸失敗
                 return render(request, 'users/login.html', {'form': form,
                              'message': 'Wrong password. Please try again.'})
   else:
       form = LoginForm()

   return render(request, 'users/login.html', {'form': form})

我們先看下views.register函數是怎麼工作的:

當用戶通過POST方法提交表單,我們先驗證表單RegistrationForm的數據是否有效。如果有效,我們先用Django User模型自帶的create_user方法創建user對象,再創建user_profile。用戶通過一張表單提交數據,我們實際上分別存儲在兩張表裡。

如果用戶註冊成功,我們通過HttpResponseRedirect方法轉到登陸頁面

如果用戶沒有提交表單或不是通過POST方法提交表單,我們轉到註冊頁面,生成一張空的RegistrationForm

我們再看下views.login函數是怎麼工作的:

當用戶通過POST方法提交表單,我們先驗證表單LoginForm的數據是否有效。如果有效,我們調用Django自帶的auth.authenticate() 來驗證用戶名和密碼是否正確。如果正確且用戶是活躍的,我們調用auth.login()來進行登錄。

如果用戶登錄失敗,會重新轉到登錄頁面,並返回錯誤信息。

如果用戶登錄成功,我們通過HttpResponseRedirect方法轉到用戶個人信息頁面

如果用戶沒有提交表單或不是通過POST方法提交表單,我們轉到登錄頁面,生成一張空的LoginForm

第五步: 編寫HTML模板(Template)

在users目錄下創建/templates/users/文件夾,編寫html模板registration.html和login.html。其目錄結構應該如下圖所示:

下面是模板registration.html的代碼:

{% block content %}
<div class="form-wrapper">
  <form method="post" action="" enctype="multipart/form-data">
     {% csrf_token %}
     {% for field in form %}
          <div class="fieldWrapper">
       {{ field.errors }}
       {{ field.label_tag }} {{ field }}
       {% if field.help_text %}
            <p class="help">{{ field.help_text|safe }}</p>
       {% endif %}
          </div>
       {% endfor %}
     <div class="button-wrapper submit">
        <input type="submit" value="Submit" />
     </div>
  </form>
</div>
{% endblock %}

下面是模板login.html的代碼:

{% block content %}
<h2>Login</h2>
{% if message %}
{{ message }}
{% endif %}
<div class="form-wrapper">
  <form method="post" action="" enctype="multipart/form-data">
     {% csrf_token %}
     {% for field in form %}
          <div class="fieldWrapper">
       {{ field.errors }}
       {{ field.label_tag }} {{ field }}
       {% if field.help_text %}
            <p class="help">{{ field.help_text|safe }}</p>
       {% endif %}
          </div>
       {% endfor %}
     <div class="button-wrapper submit">
        <input type="submit" value="Login" />
     </div>
  </form>
   <a href="/accounts/register">Register</a>
</div>
{% endblock %}

第六步:實戰效果

現在你可以在終端輸入以下命令,看實戰效果了。

python manage.py runserver

打開瀏覽器,訪問http://127.0.0.1:8000/accounts/register/和http://127.0.0.1:8000/accounts/login/,你就應該看到如下效果了。

當你註冊時兩次密碼不一致,或用戶名太短,或用戶名已存在,表單會告訴你錯誤在哪裡,如下圖所示:

當你成功登錄時,你會被轉到個人資料頁面。你將被允許修改自己的信息,如下圖所示:

結語


本文利用Django 2.0實現了用戶註冊與登錄的兩個功能,擴展了Django自帶的User模型,並分享了代碼。接下來我會分享如何實現查看用戶資料,允許用戶修改自己資料,修改密碼和退出登錄的其它4個功能。另外,我會專門介紹如何調用靜態文件css和js來美化本項目中的表單。

如果你喜歡我的文章,歡迎關注我的微信號【Python與Django大咖之路】

相關焦點

  • django 自帶 user 欄位擴展及頭像上傳
    2.通過繼承 AbstractUser 來實現首先需要在 models.py 文件中創建自己的 user modelfrom django.contrib.auth.models import AbstractUserfrom django.db import modelsclass User(AbstractUser):
  • 「原創」Django第六章、模型操作
    auth_user django_migrationsauth_user_groups django_sessionauth_user_user_permissionssqlite>
  • Django HTML表單實現用戶登錄退出(含源碼)
    HTML表單實現用戶的登錄通過前一節的學習,通過 HTML 表單並不難實現用戶的登錄功能,那麼大家先思考一下,用戶登錄的邏輯打開是怎麼樣的呢?分析這個邏輯,大家也可以去體驗一下其他網站的登錄功能,從用戶的註冊到登錄最後用戶退出,這整個的流程都需要大家細細的品味,並發現其中的規律,並且學以致用。當自己不熟練的時候,學會去借鑑其他人的經驗,往往是一個不錯的選擇。
  • Django第二十八課
    本部分內容參考B站UP再敲一行代碼的Django2教程[1]⚠️:多圖預警第二十八課 自定義用戶模型在上節課登錄功能中,我們提到了個人資料。這節課我們繼續講述關於個人資料中的用戶自定義內容。這裡大概有兩種方法:一是繼承django的用戶模型;二是使用新的模型拓展關聯User(🌟🌟🌟🌟🌟)。1. 繼承Django的用戶模型我們之前使用的User是在django.contrib.auth.models中引用的,我們可以查看這部分源碼,分析這個User是如何創建的。
  • Django第二十六課
    需求分析要實現點讚功能,需要的是:具體應該怎麼實現呢?在Django中,最核心但是也最基本的就是模型。模型可以記錄數據,我們很多操作都是基於數據進行操作的。對於點讚功能,點讚總數,是否點讚都是在資料庫中。所以第一步就可以考慮如何構建模型。
  • python程式設計師嘔心瀝血整理 Django 優秀資源大全
    django-fluent-dashboard, star:166 - Django 項目的增強型管理面板。django-grappelli, star:2087 - 可用於 Django 管理界面的一個絢麗皮膚。django-hijack, star:542 - 允許超級用戶以其他用戶微分登錄和操作。
  • Django實戰教程: 開發企業級應用智能文檔管理系統smartdoc(1)
    本教程一共分三部分,第1部分搭基礎框架(標黃部分),第2部分講解用戶權限控制,第3部分建立文檔搜索和Ajax搜索功能。本文是第一部分。項目開發環境Django 2.1 + Python 3.5 + SQLite。因為用戶上傳的文件還包括圖片,所以請確保你已通過pip安裝python的pillow圖片庫。
  • Django REST Framework教程(6): 認證詳解及如何使用Token認證
    然而前篇文章中我們使用了Django默認的基於session的認證方式,實際前後端分離開發項目中後臺更多採用的是token(令牌認證)。本文將詳細介紹如何在DRF中使用不同的認證方案,並重點介紹如何使用DRF自帶的token認證。什麼是認證(Authentication)?
  • Django 2.1.7 Admin - 註冊模型、自定義顯示列表欄位
    使用Django的管理模塊,需要按照如下步驟操作:1.管理界面本地化 2.創建管理員 3.註冊模型類 4.自定義管理頁面1.管理界面本地化本地化是將顯示的語言、時間等使用本地的習慣,這裡的本地化就是進行中國化,中國大陸地區使用簡體中文
  • 10道題教你使用python Django框架來實現web應用,值得收藏
    1、django+python27+mysql的生產環境,想換成python3,請問需要哪些注意事項?生產環境數據不想被破壞,所以不敢輕易下手,希望大神指點一下?關於python升級:可以使用Python3自帶工具2to3,將python2的代碼轉換為python3。少部分代碼仍需手工轉換。
  • 「原創」Django第五章、模型建立與遷移
    模型的建立我們要建立一個博客系統,我們回顧前面所學知識,重新建立一個Django項目名字為myblog,在命令行敲如下命令:python manage.py startproject myblog然後在manage.py
  • 如何創建Django應用並連接資料庫
    .# Internationalization# https://docs.djangoproject.com/en/2.0/topics/i18n/LANGUAGE_CODE = 'en-us'TIME_ZONE = 'UTC'USE_I18N = TrueUSE_L10N = TrueUSE_TZ = True...
  • Vue結合Django-Rest-Framework實現登錄認證(下)
    前言在上一篇 Vue結合Django-Rest-Framework實現登錄認證(上)一文中,我們利用token實現了一個非常簡單的用戶登錄認證功能。那這一篇文章來完成上一篇文章結尾處梳理的需要完善的三個功能點。
  • 如果你是完美主義者,那麼Django就是你的菜!
    AuthenticationMiddleware中間件:表示當前登錄的用戶的AUTH_USER_MODEL的實例,這個模型是Django內置的Auth模塊下的User模型。如果用戶當前未登錄,則user將被設置為AnonymousUser的實例。
  • 8個能提高Django開發效率的Python包
    clean_pyc命令會將項目目錄中所有位置的.pyc項目刪除。 create_template_tags用於在您指定的應用程式中創建一個模板標籤目錄結構。 describe_form將顯示模型的表單定義,然後您可以將其複製/粘貼到forms.py中。(注意,這將生成一個普通的Django表單,而不是一個模型表單。)
  • Django QuerySet查詢基礎與技巧.有了她,再也不用擔心SQL注入了.
    先假設有如下模型,以供下面的QuerySet查詢使用:#coding:utf-8from django.db import modelsfrom django.contrib.auth.models import User #博客模型class Blog(models.Model):
  • 一個完整的Django入門指南
    我們將使用Python 3,因為大部分主要的Python庫已經被移植到python 3,並且下一個主要的django版本(2.x)也將不再支持python 2。所以Python 3是正確的選擇。最好的方法是通過Homebrew安裝。
  • 好程式設計師Python培訓分享Django中間件基礎用法詳解
    1.在django項目文件夾下創建一個用於存放中間件函數的文件夾 文件夾的名字可自定義,本文使用utils作為文件夾名2.在utils文件夾中創建中間件py文件 如用於登陸驗證的中間件loginCheck.py from django.http import HttpResponseRedirect from django.utils.deprecation import MiddlewareMixin
  • Python Django之路與您同行
    主要在這裡寫下自己的學習筆記、軟體測試思考及讀書感悟等,後續可能會系統的介紹一些python、django、移動端自動化測試、接口自動化測試、性能測試等。歡迎您的光臨!要想深入測試,必須了解功能邏輯,對數據流及網站架構比較清楚,這點也說過多次,真的很重要,必須要體現在工作當中,養成習慣,絕對不要對自己測試過的功能模塊其中的技術實現不清楚。這樣測試路會不好走!!!
  • Django分頁完整示例
    在django中可以使用兩種方法進行分頁,第一種方法是使用基於函數的視圖,第二種方法是使用基於類的視圖。現在,首先,需要使用此命令創建一個新的django項目。我稱這個項目為MyProjectdjango-admin startproject ProjectName首先,需要將目錄更改為已創建的項目,然後需要創建一個App,我將其稱為MyApp。