在Django自帶的User類中,只有用戶名、郵箱、密碼等等一些基礎信息。若是此時有添加用戶電話,暱稱,qq號等其餘信息的需求時,自帶User類的弊端就出現了。那麼若是出現上述需求時,就須要自定義用戶模型。html
在Django的文檔中對於自定義用戶模型,有下面這麼兩段話。python
有兩種方法能夠擴展默認User模型而無需替換本身的模型。若是您須要的更改純粹是行爲上的,而且不須要對數據庫中存儲的內容進行任何更改,則能夠基於建立代理模型User。這容許代理模型提供的任何功能,包括默認排序,自定義管理器或自定義模型方法。git
若是您但願存儲與之相關的信息User,可使用 OneToOneField包含這些字段的模型來獲取其餘信息。這種一對一模型一般稱爲配置文件模型,由於它可能存儲有關站點用戶的非身份驗證相關信息。數據庫
自定義模型繼承AbstractUserdjango
1.AbstractUser 類的部分代碼架構
class AbstractUser(AbstractBaseUser, PermissionsMixin): """ An abstract base class implementing a fully featured User model with admin-compliant permissions. Username and password are required. Other fields are optional. """ username_validator = UnicodeUsernameValidator() if six.PY3 else ASCIIUsernameValidator() 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=30, 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.' ), )
2.平時常用的User類app
class User(AbstractUser): """ Users within the Django authentication system are represented by this model. Username, password and email are required. Other fields are optional. """ class Meta(AbstractUser.Meta): swappable = 'AUTH_USER_MODEL'
能夠看出一樣的方法,我也能夠試着使用一個新的 User 來繼承 AbstractUser 類,並添加上須要的字段。我一開始在本地開發使用的是這種辦法,後來出了不少的一系列問題,好比數據庫遷移失敗,修改表結構,並且數據庫遷移失敗是因爲migrations文件衝突致使的,必須刪除該遷移文件,並從新生成遷移文件。考慮到這樣下去,會影響到我生產環境的數據庫,並且須要修改的代碼也比較多,因此最後不得不棄用此方法。工具
若是要具體使用這種方法,能夠查看Django文檔的擴展示有User模型是如何使用的。post
3.優缺點
1) 優勢:
①自定義強。
②沒有沒必要要的字段(須要繼承AbstractBaseUser)。
2) 缺點:
①須要刪除庫來或者要項目一開始就使用。
②配置admin麻煩 。網站
新的模型拓展關聯User
除了將新模型繼承 AbstractUser ,還可創建新模型使其關聯User。這種方法很是適合項目已經基本成型,但在後來開發中又須要修改用戶表結構的操做,最終我選用了拓展關聯User的方法。
1.先看看官方的一個例子
例如建立一個Employee模型:
from django.contrib.auth.models import User class Employee(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) department = models.CharField(max_length=100)
假設現有員工Fred Smith同時擁有User和Employee模型,你可使用Django的標準相關模型約定訪問相關信息:
>>> u = User.objects.get(username='fsmith') >>> freds_department = u.employee.department
要將配置文件模型的字段添加到管理員的用戶頁面,須要在應用程序中定義一個 InlineModelAdmin(對於此示例,咱們將使用a StackedInline)admin.py並將其添加到在UserAdmin類中註冊的 User類:
from django.contrib import admin from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from django.contrib.auth.models import User from my_user_profile_app.models import Employee # Define an inline admin descriptor for Employee model # which acts a bit like a singleton class EmployeeInline(admin.StackedInline): model = Employee can_delete = False verbose_name_plural = 'employee' # Define a new User admin class UserAdmin(BaseUserAdmin): inlines = (EmployeeInline,) # Re-register UserAdmin admin.site.unregister(User) admin.site.register(User, UserAdmin)
能夠看到,在註冊應用時,用到了admin下的StackedInline,這裏與xadmin的註冊有所區分,後面會提到。
2.優缺點
1) 優勢:
①使用方便。
②不用刪除庫重來影響總體架構。
2) 缺點:
①對比繼承方法,查詢速度稍稍慢一丁點。
3.需求分析
在一開始我就用的是Django自帶的用戶管理系統,因此個人用戶屬性只有用戶名、郵箱、密碼。光靠這些不是很容易記住用戶的身份,應該還須要相似於暱稱的屬性,這樣可讓用戶以用戶用戶名進行登陸,登陸以後能夠顯示他們本身設置的暱稱。
4.新建Profile模型
我定義了一個Profile類,經過models的OneToOneField將其一對一地關聯到User類,這裏我只新增了nickname(暱稱),也能夠添加其餘的。
from django.db import models from django.contrib.auth.models import User class Profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, verbose_name='用戶名') nickname = models.CharField(max_length=20, verbose_name='暱稱') def __str__(self): return self.nickname class Meta: verbose_name = '暱稱' verbose_name_plural = '暱稱'
5.註冊Profile模型
若是你的後臺用的是admin,那麼直接根據官方的例子就能正常將新的User模型註冊到後臺。這裏我用的是xadmin後臺,它與admin同樣,都屬於Djnago的後臺管理,只不過xadmin的功能要略顯強大一些,具體在Django中配置xadmin能夠查看網站搭建 (第十四天) xadmin後臺強化,這裏就不贅述了。由於xadmin在註冊應用時,繼承的是object,而admin繼承的是admin.ModelAdmin,因此在實現註冊模型上有必定的區別。
以前,我在xadmin模塊中怎樣都找不到StackedInline,內心想着完了,又要從新使用admin了。試弄了一會,發現其實InlineModelAdmin仍是照樣繼承object,而UserAdmin在xadmin/plugins/auth 目錄下。最後實現以下:
import xadmin from xadmin.plugins.auth import UserAdmin as BaseAdmin from django.contrib.auth.models import User from .models import Profile class ProfileInline(object): model = Profile extra = 0 class UserAdmin(BaseAdmin): inlines = [ProfileInline] list_display = ['username', 'nickname', 'email', 'is_staff', 'is_active', 'is_superuser'] def nickname(self, obj): return obj.profile.nickname nickname.short_description = '暱稱' xadmin.site.unregister(User) xadmin.site.register(User, UserAdmin)
上面是將Profile模型的nickname字段註冊到User中,還能夠將Profile模型再註冊一個應用。
class ProfileAdmin(object): """ 做用:自定義文章管理工具 admin.ModelAdmin:繼承admin.ModelAdmin類 """ list_display = ['user', 'nickname'] xadmin.site.register(Profile, ProfileAdmin)
作好這一步,就能夠去xadmin後臺爲用戶添加暱稱了。
6.添加User模型的方法
實現好模型註冊之後,爲了能將用戶的暱稱顯示在頁面中,還須要爲User類綁定一些方法。
def get_nickname_or_username(self): if Profile.objects.filter(user=self).exists(): profile = Profile.objects.get(user=self) return profile.nickname else: return self.username def has_nickname(self): return Profile.objects.filter(user=self).exists() # 綁定方法不須要添加括號 User.get_nickname_or_username = get_nickname_or_username User.has_nickname = has_nickname
而後就能夠在以前顯示username的地方修改成nickname的顯示,如導航欄的歡迎用戶。
<a href="#" class="dropdown-toggle hidden-sm" data-toggle="dropdown" role="button"> <span class="glyphicon glyphicon-user item"></span> 您好: {{ user.get_nickname_or_username }}<span class="caret"></span> </a>
7.定義修改暱稱表單
以前仍是須要管理員本身到後臺對用戶進行添加暱稱操做,要是可讓用戶本身修改暱稱豈不是更好。實現起來也不難,只要再創建一個form表單,將post的數據清洗一下,再將合法的暱稱綁定在user上。關於Django的form表單使用,能夠參考以前寫的用戶註冊或登陸表單。
一樣,在user/forms.py下新建ChangeNameForm表單。
from django.contrib import auth from django.contrib.auth.models import User class ChangeNameForm(forms.Form): new_nickname = forms.CharField( label='新暱稱', max_length=20, min_length=3, widget=forms.TextInput( attrs={'placeholder': '請輸入新的暱稱'} ) ) def __init__(self, *args, **kwargs): if 'user' in kwargs: self.user = kwargs.pop('user') super(ChangeNameForm, self).__init__(*args, **kwargs) def clean(self): # 驗證用戶是否處在登陸狀態 if self.user.is_authenticated: self.cleaned_data['user'] = self.user else: raise forms.ValidationError('用戶還沒有登陸') return self.cleaned_data def clean_new_nickname(self): new_nickname = self.cleaned_data.get('new_nickname', '').strip() if new_nickname == '': raise forms.ValidationError("新的暱稱不能爲空") return new_nickname
8.定義修改暱稱的邏輯處理
from .models import Profile from .forms import * from django.shortcuts import redirect, render def change_name(request): """修改暱稱""" if request.method == 'POST': name_form = ChangeNameForm(request.POST, user=request.user) if name_form.is_valid(): new_nickname = name_form.cleaned_data['new_nickname'] profile, created = Profile.objects.get_or_create(user=request.user) profile.nickname = new_nickname profile.save() return redirect(request.GET.get('from', reverse('blog:home'))) else: name_form = ChangeNameForm() context = {'name_form': name_form} return render(request, 'user/change_name.html', context)
9.創建修改暱稱視圖
{% extends 'base.html' %} {% load staticfiles %} {% block title %} 修改暱稱 {% endblock %} {% block nav_login_active %}active{% endblock %} {% block lunbobox %}{% endblock %} {% block content %} <div class="container" style="margin-top: 70px;"> <div class="head-login"> <h2 class="text-info">修改暱稱</h2> <span>與我取得聯繫,共同成長吧</span> </div> <div class="change_name"> <form action="" method="POST"> {% csrf_token %} {% for field in name_form %} <label for="{{ field.id_for_label }}">{{ field.label }}:</label> {{ field }} <p class="text-danger">{{ field.errors.as_text }}</p> {% endfor %} {# 錯誤信息標紅 #} <span class="text-danger">{{ login_form.non_field_errors }}</span> {# <span>用戶名:</span> #} {# <input type="text" name="username"> #} {# <span>密碼:</span> #} {# <input type="password" name="password"> #} <button class="btn btn-primary pull-right" type="submit">修改</button> </form> </div> </div> {% endblock %}
原文出處:https://www.jzfblog.com/detail/119,文章的更新編輯以此連接爲準。歡迎關注源站文章!