前言
Django
為我們提供了內建的User
模型,不需要我們再額外定義使用者模型,建立使用者體系了。它的完整的路徑是在django.contrib.auth.models.User
。
User模型原始碼分析
class User(AbstractUser):
"""
Django 身份驗證系統中的使用者由該模型表示
需要使用者名稱和密碼。其他欄位是可選的。
"""
class Meta(AbstractUser.Meta):
swappable = 'AUTH_USER_MODEL'
我們可以看到User
這個類本身沒幹什麼事情,而是繼承自AbstractUser
類,那麼我們檢視下AbstractUser
的原始碼
class AbstractUser(AbstractBaseUser, PermissionsMixin):
"""
一個抽象基類實現了一個功能齊全的使用者模型 符合管理員的許可權。
需要使用者名稱和密碼。 其他欄位是可選的。
"""
# 使用者民校驗
username_validator = UnicodeUsernameValidator()
username = models.CharField(
_('username'),
max_length=150,
unique=True,
help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
validators=[username_validator],
error_messages={
'unique': _("A user with that username already exists."),
},
)
first_name = models.CharField(_('first name'), max_length=30, blank=True)
last_name = models.CharField(_('last name'), max_length=150, blank=True)
email = models.EmailField(_('email address'), blank=True)
is_staff = models.BooleanField(
_('staff status'),
default=False,
help_text=_('Designates whether the user can log into this admin site.'),
)
is_active = models.BooleanField(
_('active'),
default=True,
help_text=_(
'Designates whether this user should be treated as active. '
'Unselect this instead of deleting accounts.'
),
)
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
# objects使用者管理,裡面有建立使用者的方法
objects = UserManager()
EMAIL_FIELD = 'email'
# 用來描述User模型名字欄位的字串,作為唯一的標識。如果沒有修改,那麼會使用USERNAME來作為唯一欄位。
USERNAME_FIELD = 'username'
# 一個欄位名列表,用於當通過createsuperuser管理命令建立一個使用者時的提示。
REQUIRED_FIELDS = ['email']
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
abstract = True
def clean(self):
super().clean()
self.email = self.__class__.objects.normalize_email(self.email)
def get_full_name(self):
"""
返回first_name和last_name,中間有個空格
"""
full_name = '%s %s' % (self.first_name, self.last_name)
return full_name.strip()
def get_short_name(self):
"""返回使用者的first_name."""
return self.first_name
def email_user(self, subject, message, from_email=None, **kwargs):
"""傳送郵件給使用者."""
send_mail(subject, message, from_email, [self.email], **kwargs)
我們可以看到AbstractUser
繼承自AbstractBaseUser
和PermissionsMixin
AbstractBaseUser
:基礎的User模型類PermissionsMixin
:許可權類
我們先看看AbstractUser
有哪些欄位和方法
欄位
username
:使用者名稱。150個字元以內。可以包含數字和英文字元,以及_
、@
、+
、.
和-
字元。不能為空,且必須唯一!first_name
:外國人的first_name
,在30個字元以內。可以為空。last_name
:外國人的last_name
,在150個字元以內。可以為空。email
:郵箱。可以為空。password
:密碼。經過雜湊過後的密碼。(父類AbstractBaseUser
的屬性)groups
:分組。一個使用者可以屬於多個分組,一個分組可以擁有多個使用者。groups
這個欄位是跟Group
的一個多對多的關係。(父類PermissionsMixin
的屬性)user_permissions
:許可權。一個使用者可以擁有多個許可權,一個許可權可以被多個使用者所有用。和Permission
屬於一種多對多的關係。(父類PermissionsMixin
的屬性)is_staff
:是否可以進入到admin
的站點。代表是否是員工is_active
:是否是可用的。對於一些想要刪除賬號的資料,我們設定這個值為False
就可以了,而不是真正的從資料庫中刪除。is_superuser
:是否是超級管理員。如果是超級管理員,那麼擁有整個網站的所有許可權。(父類PermissionsMixin
的屬性)last_login
:上次登入的時間。(父類AbstractBaseUser
的屬性)date_joined
:賬號建立的時間。
User模型基本用法
建立使用者
建立使用者需要用到objects = UserManager()
中的方法,我們點選UserManager
檢視原始碼
class UserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, username, email, password, **extra_fields):
"""
使用給定的使用者名稱、電子郵件和密碼建立並儲存使用者。
"""
# 如果沒有username則丟擲異常
if not username:
raise ValueError('The given username must be set')
# 標準化電子郵件,檢視原始碼會發現是用@進行分割
email = self.normalize_email(email)
# 標準化使用者名稱
username = self.model.normalize_username(username)
user = self.model(username=username, email=email, **extra_fields)
# 為使用者設定密碼,將純文字密碼轉換為用於資料庫儲存的雜湊值
user.set_password(password)
# 儲存使用者
user.save(using=self._db)
return user
def create_user(self, username, email=None, password=None, **extra_fields):
# 設定is_staff預設值為False,is_superuser預設值為False
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(username, email, password, **extra_fields)
def create_superuser(self, username, email, password, **extra_fields):
# 設定is_staff預設值為True,is_superuser預設值為True
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
# 如果呼叫此方法,is_staff必須為True,否則會丟擲異常
if extra_fields.get('is_staff') is not True:
raise ValueError('Superuser must have is_staff=True.')
# 如果呼叫此方法,is_superuser必須為True,否則會丟擲異常
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self._create_user(username, email, password, **extra_fields)
以上原始碼寫的十分清楚相信小夥伴們也看得很清晰了,接下里我們實際操作一下
def index(request):
user = User.objects.create_user(username="jkc", email="[email protected]", password="123456")
user.save()
return HttpResponse("ok")
我們訪問以上檢視後,就建立了一個普通使用者,檢視資料庫中的auth_user
表,如下
建立超級使用者
建立超級使用者有兩種方式。第一種是使用程式碼的方式。用程式碼建立超級使用者跟建立普通使用者非常的類似,只不過是使用create_superuser
。示例程式碼如下:
def index(request):
user = User.objects.create_superuser(username="jkc3", email="[email protected]", password="123456")
user.save()
return HttpResponse("ok")
也可以通過命令列的方式。命令如下:
python manage.py createsuperuser
後面就會提示你輸入使用者名稱、郵箱以及密碼。
修改密碼
因為密碼是需要經過加密後才能儲存進去的。所以如果想要修改密碼,不能直接修改password
欄位,而需要通過呼叫set_password
來達到修改密碼的目的。示例程式碼如下:
def index(request):
user = User.objects.get(pk=1)
user.set_password('111111')
user.save()
return HttpResponse("ok")
改之前密碼為$1FMDwi2zsgQu$2+8/zL6ZR43oXIvIRGfK6xrWUnv2IRjdPxVaqEwqyjM=
,改完之後為$u6rNdNTvLbEG$r4TcrVsTsibcVF3ZfZIJPjLNvq73wyusLShDmpSZeKM=
登入驗證
Django
的驗證系統已經幫我們實現了登入驗證的功能。通過django.contrib.auth.authenticate
即可實現。這個方法只能通過username
和password
來進行驗證。示例程式碼如下:
def index(request):
user = authenticate(username="jkc", password="111111")
if user:
return HttpResponse("登入成功")
else:
return HttpResponse("登入失敗")
擴充套件使用者模型
Django
內建的User
模型雖然已經足夠強大了。但是有時候還是不能滿足我們的需求。比如在驗證使用者登入的時候,他用的是使用者名稱
作為驗證,而我們有時候需要通過手機號
碼或者郵箱
來進行驗證。還有比如我們想要增加一些新的欄位。那麼這時候我們就需要擴充套件使用者模型了。擴充套件使用者模型有多種方式。這裡我們來一一討論下。
繼承自AbstractUser
對於authenticate
不滿意,並且不想要修改原來User
物件上的一些欄位,但是想要增加一些欄位,那麼這時候可以直接繼承自django.contrib.auth.models.AbstractUser
,其實這個類也是django.contrib.auth.models.User
的父類。比如我們想要在原來User
模型的基礎之上新增一個phone
欄位。示例程式碼如下:
from django.contrib.auth.base_user import BaseUserManager
from django.db import models
from django.contrib.auth.models import AbstractUser
class UserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self,phone,password,**extra_fields):
if not phone:
raise ValueError("請填入手機號碼!")
if not password:
raise ValueError('請輸入密碼!')
user = self.model(phone=phone,*extra_fields)
user.set_password(password)
user.save()
return user
def create_user(self,phone,password,**extra_fields):
extra_fields.setdefault('is_superuser',False)
return self._create_user(phone,password)
def create_superuser(self,phone,password,**extra_fields):
extra_fields['is_superuser'] = True
return self._create_user(phone,password)
class User(AbstractUser):
phone = models.CharField(max_length=11, unique=True, verbose_name="手機號碼")
# 指定phone作為USERNAME_FIELD,以後使用authenticate
# 函式驗證的時候,就可以根據phone來驗證,而不是原來的username
USERNAME_FIELD = 'phone'
# 提醒使用者輸入的欄位
REQUIRED_FIELDS = []
# 重新定義Manager物件,在建立user的時候使用phone和password,而不是使用username和password
objects = UserManager()
然後再在settings中配置好AUTH_USER_MODEL=yourapp.User
。
注意:這種方式因為破壞了原來User
模型的表結構,所以必須要在第一次migrate
前就先定義好。
以上我們重新定義了User
模型,新增了phone
欄位,並把phone
作為校驗欄位,我們先來看下資料庫的表結構
接下里我們通過createsuperuser
命令來建立超級使用者
我們會發現建立超級使用者的時候,不再需要username
欄位來校驗了,接下來我們驗證一下登入,現在的結構需要用phone
欄位和密碼來登入,而不是使用username
,我們編寫檢視函式來嘗試
def index(request):
# 先使用手機號密碼登入
user = authenticate(username="12345678901", password="admin123")
if user:
return HttpResponse('手機號密碼登入成功')
else:
return HttpResponse('手機號密碼登入失敗')
然後訪問檢視,返回手機號密碼登入成功
,說明現在校驗的欄位的內容是手機號,我們再來試試使用使用者名稱能否登入成功
def index(request):
# 由於之前未設定username,這裡先為id為1的使用者設定username
user = User.objects.get(pk=1)
user.username = "jkc"
user.save()
print("儲存成功")
u = authenticate(username="jkc", password="admin123")
if u:
return HttpResponse('使用者名稱登入成功')
else:
return HttpResponse('使用者名稱登入失敗')
我們訪問檢視,最後返回的是手機號驗證碼登入失敗
,說明現在username
校驗的是手機號,我們輸入使用者名稱是校驗不通過的