有兩種方法來擴(kuò)展默認(rèn)的User Model而不用重寫自己的模型。如果你不需要改變存儲在數(shù)據(jù)庫中的字段,而只是需要改變Model的行為,您可以創(chuàng)建一個基于User的代理Model。允許的行為包括默認(rèn)的ordering,custom managers, 或者 custom model methods。
如果你想存儲與User有關(guān)的信息,可以使用一個OneToOneField字段關(guān)聯(lián)到一個存儲額外信息的Model。這一Model通常被稱為一個PRofile model模型,它可以用來存儲一些非驗證所需的信息。例如,你可以創(chuàng)建一個Model:
from django.contrib.auth.models import Userclass Employee(models.Model): user = models.OneToOneField(User) department = models.CharField(max_length=100)
訪問:
>>> u = User.objects.get(username='fsmith')>>> freds_department = u.employee.department
如果需要將profile model的字段添加到admin管理界面的user頁面上,需要在應(yīng)用app的admin.py中定義InlineModelAdmin
from django.contrib import adminfrom django.contrib.auth.admin import UserAdminfrom django.contrib.auth.models import Userfrom my_user_profile_app.models import Employee# Define an inline admin descriptor for Employee model# which acts a bit like a singletonclass EmployeeInline(admin.StackedInline): model = Employee can_delete = False verbose_name_plural = 'employee'# Define a new User adminclass UserAdmin(UserAdmin): inlines = (EmployeeInline, )# Re-register UserAdminadmin.site.unregister(User)admin.site.register(User, UserAdmin)
這些profile models并不特別,只是與User Model有一個OneToOne鏈接。所以當(dāng)一個user實例創(chuàng)建時,profile model并不會自動創(chuàng)建。
有時候User Model并不適合你的網(wǎng)站,比如你要將email而不是username作為認(rèn)證標(biāo)識,這時候就需要重寫User Model。
首先,需要將settings中的默認(rèn)User Model覆蓋:
AUTH_USER_MODEL = 'myapp.MyUser'
如果AUTH_USER_MODEL已被重設(shè),那當(dāng)User Model通過ForeignKey或者M(jìn)anyToManyField訪問時,不能直接訪問,而是要通過AUTH_USER_MODEL來訪問:
from django.conf import settingsfrom django.db import modelsclass Article(models.Model): author = models.ForeignKey(settings.AUTH_USER_MODEL)
最簡單的定制一個User Model的方法是繼承用戶類AbstractBaseUser。
源碼:
@python_2_unicode_compatibleclass AbstractBaseUser(models.Model): passWord = models.CharField(_('password'), max_length=128) last_login = models.DateTimeField(_('last login'), blank=True, null=True) is_active = True REQUIRED_FIELDS = [] class Meta: abstract = True def get_username(self): "Return the identifying username for this User" return getattr(self, self.USERNAME_FIELD) def __str__(self): return self.get_username() def natural_key(self): return (self.get_username(),) def is_anonymous(self): """ Always returns False. This is a way of comparing User objects to anonymous users. """ return False def is_authenticated(self): """ Always return True. This is a way to tell if the user has been authenticated in templates. """ return True def set_password(self, raw_password): self.password = make_password(raw_password) def check_password(self, raw_password): """ Returns a boolean of whether the raw_password was correct. Handles hashing formats behind the scenes. """ def setter(raw_password): self.set_password(raw_password) self.save(update_fields=["password"]) return check_password(raw_password, self.password, setter) def set_unusable_password(self): # Sets a value that will never be a valid hash self.password = make_password(None) def has_usable_password(self): return is_password_usable(self.password) def get_full_name(self): raise NotImplementedError('subclasses of AbstractBaseUser must provide a get_full_name() method') def get_short_name(self): raise NotImplementedError('subclasses of AbstractBaseUser must provide a get_short_name() method.') def get_session_auth_hash(self): """ Returns an HMAC of the password field. """ key_salt = "django.contrib.auth.models.AbstractBaseUser.get_session_auth_hash" return salted_hmac(key_salt, self.password).hexdigest()
一些關(guān)鍵的字段和方法:
USERNAME_FIELD
必需的。設(shè)置認(rèn)證標(biāo)識。設(shè)置成標(biāo)識的字段unique必須為True。
class MyUser(AbstractBaseUser): identifier = models.CharField(max_length=40, unique=True) ... USERNAME_FIELD = 'identifier'
上面的例子中identifier即作為MyUser的認(rèn)證標(biāo)識。
REQUIRED_FIELDS
字段name組成的列表。當(dāng)創(chuàng)建superuser時用到的字段。
class MyUser(AbstractBaseUser): ... date_of_birth = models.DateField() height = models.FloatField() ... REQUIRED_FIELDS = ['date_of_birth', 'height']
列表中不應(yīng)該包含USERNAME_FIELD字段和password字段。
is_active
AbstractBaseUser默認(rèn)為True。
get_full_name()
get_short_name()
AbstractBaseUser的子類必須定義的兩個方法。
下面為一些AbstractBaseUser的子類可以使用的方法:
get_username()
返回USERNAME_FIELD的值.
is_anonymous()
返回False。
is_authenticated()
返回True。檢查一個user是否已登錄。
set_password(raw_password)
設(shè)置密碼
check_password(raw_password)
檢查密碼是否正確
set_unusable_password()
設(shè)置user無密碼
has_usable_password()
Returns False if set_unusable_password() has been called for this user.
get_session_auth_hash()
返回密碼字段的HMAC. Used for Session invalidation on password change.
還需要為自己的User Model定義一個custom manager。
class models.CustomUserManager
create_user(*username_field*, password=None, **other_fields)
接受username field和required字段來創(chuàng)建用戶。例如,如果使用email作為username field, date_of_birth作為required field:
def create_user(self, email, date_of_birth, password=None): # create user here ...
create_superuser(*username_field*, password, **other_fields)
創(chuàng)建superuser
def create_superuser(self, email, date_of_birth, password): # create superuser here ...
create_superuser中的password是必需的。
UserCreationForm
依賴于User Model. 擴(kuò)展User時必須重寫。
UserChangeForm
依賴于User Model. 擴(kuò)展User時必須重寫。
AuthenticationForm
Works with任何AbstractBaseUser子類 ,and will adapt to use the field defined in USERNAME_FIELD.
PasswordResetForm
Assumes that the user model has an integer primary key, has a field named email that can be used to identify the user, and a boolean field named is_active to prevent password resets for inactive users.
SetPasswordForm
Works with 任何AbstractBaseUser子類
PasswordChangeForm
Works with任何AbstractBaseUser子類
AdminPasswordChangeForm
Works with任何AbstractBaseUser子類。
如果想自己定義的User Model能與admin管理系統(tǒng)一起使用,還需要定義一些字段和方法。
is_staff
是否允許user訪問admin界面
is_active
用戶是否活躍。
has_perm(perm, obj=None):
user是否擁有perm權(quán)限。
has_module_perms(app_label):
user是否擁有app中的權(quán)限
如果要定制User的權(quán)限系統(tǒng),最簡單的方法是繼承PermissionsMixin
源碼:
class PermissionsMixin(models.Model): """ A mixin class that adds the fields and methods necessary to support Django's Group and Permission model using the ModelBackend. """ is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_('Designates that this user has all permissions without ' 'explicitly assigning them.')) groups = models.ManyToManyField(Group, verbose_name=_('groups'), blank=True, help_text=_('The groups this user belongs to. A user will ' 'get all permissions granted to each of ' 'their groups.'), related_name="user_set", related_query_name="user") user_permissions = models.ManyToManyField(Permission, verbose_name=_('user permissions'), blank=True, help_text=_('Specific permissions for this user.'), related_name="user_set", related_query_name="user") class Meta: abstract = True def get_group_permissions(self, obj=None): """ Returns a list of permission strings that this user has through their groups. This method queries all available auth backends. If an object is passed in, only permissions matching this object are returned. """ permissions = set() for backend in auth.get_backends(): if hasattr(backend, "get_group_permissions"): permissions.update(backend.get_group_permissions(self, obj)) return permissions def get_all_permissions(self, obj=None): return _user_get_all_permissions(self, obj) def has_perm(self, perm, obj=None): """ Returns True if the user has the specified permission. This method queries all available auth backends, but returns immediately if any backend returns True. Thus, a user who has permission from a single auth backend is assumed to have permission in general. If an object is provided, permissions for this specific object are checked. """ # Active superusers have all permissions. if self.is_active and self.is_superuser: return True # Otherwise we need to check the backends. return _user_has_perm(self, perm, obj) def has_perms(self, perm_list, obj=None): """ Returns True if the user has each of the specified permissions. If object is passed, it checks if the user has all required perms for this object. """ for perm in perm_list: if not self.has_perm(perm, obj): return False return True def has_module_perms(self, app_label): """ Returns True if the user has any permissions in the given app label. Uses pretty much the same logic as has_perm, above. """ # Active superusers have all permissions. if self.is_active and self.is_superuser: return True return _user_has_module_perms(self, app_label)Django內(nèi)置的User對象就繼承了AbstractBaseUser和PermissionsMixin。
源碼:
class AbstractUser(AbstractBaseUser, PermissionsMixin): """ An abstract base class implementing a fully featured User model with admin-compliant permissions. Username, password and email are required. Other fields are optional. """ username = models.CharField(_('username'), max_length=30, unique=True, help_text=_('Required. 30 characters or fewer. Letters, digits and ' '@/./+/-/_ only.'), validators=[ validators.RegexValidator(r'^[/w.@+-]+$', _('Enter a valid username. ' 'This value may contain only letters, numbers ' 'and @/./+/-/_ characters.'), 'invalid'), ], 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.')) date_joined = models.DateTimeField(_('date joined'), default=timezone.now) objects = UserManager() USERNAME_FIELD = 'username' REQUIRED_FIELDS = ['email'] class Meta: verbose_name = _('user') verbose_name_plural = _('users') abstract = True def get_full_name(self): """ Returns the first_name plus the last_name, with a space in between. """ full_name = '%s %s' % (self.first_name, self.last_name) return full_name.strip() def get_short_name(self): "Returns the short name for the user." return self.first_name def email_user(self, subject, message, from_email=None, **kwargs): """ Sends an email to this User. """ send_mail(subject, message, from_email, [self.email], **kwargs)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'現(xiàn)在可以看一個完整的自定義User Model例子:
from django.db import modelsfrom django.contrib.auth.models import ( BaseUserManager, AbstractBaseUser)class MyUserManager(BaseUserManager): def create_user(self, email, date_of_birth, password=None): """ Creates and saves a User with the given email, date of birth and password. """ if not email: raise ValueError('Users must have an email address') user = self.model( email=self.normalize_email(email), date_of_birth=date_of_birth, ) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, email, date_of_birth, password): """ Creates and saves a superuser with the given email, date of birth and password. """ user = self.create_user(email, password=password, date_of_birth=date_of_birth ) user.is_admin = True user.save(using=self._db) return userclass MyUser(AbstractBaseUser): email = models.EmailField( verbose_name='email address', max_length=255, unique=True, ) date_of_birth = models.DateField() is_active = models.BooleanField(default=True) is_admin = models.BooleanField(default=False) objects = MyUserManager() USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['date_of_birth'] def get_full_name(self): # The user is identified by their email address return self.email def get_short_name(self): # The user is identified by their email address return self.email def __str__(self): # __unicode__ on Python 2 return self.email def has_perm(self, perm, obj=None): "Does the user have a specific permission?" # Simplest possible answer: Yes, always return True def has_module_perms(self, app_label): "Does the user have permissions to view the app `app_label`?" # Simplest possible answer: Yes, always return True @property def is_staff(self): "Is the user a member of staff?" # Simplest possible answer: All admins are staff return self.is_admin可以看到manager定義了create_user()和create_superuser()方法,MyUser定義了USERNAME_FIELD,REQUIRED_FIELDS字段和get_full_name(),get_short_name()方法,為了能與admin一起使用,還定義了is_active,is_staff,has_perm(),has_module_perms()
要在admin中注冊自定義的MyUser,還需要在app的admin.py中重寫UserCreationForm和UserChangeForm:
from django import formsfrom django.contrib import adminfrom django.contrib.auth.models import Groupfrom django.contrib.auth.admin import UserAdminfrom django.contrib.auth.forms import ReadOnlyPasswordHashFieldfrom customauth.models import MyUserclass UserCreationForm(forms.ModelForm): """A form for creating new users. Includes all the required fields, plus a repeated password.""" password1 = forms.CharField(label='Password', widget=forms.PasswordInput) password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput) class Meta: model = MyUser fields = ('email', 'date_of_birth') def clean_password2(self): # Check that the two password entries match password1 = self.cleaned_data.get("password1") password2 = self.cleaned_data.get("password2") if password1 and password2 and password1 != password2: raise forms.ValidationError("Passwords don't match") return password2 def save(self, commit=True): # Save the provided password in hashed format user = super(UserCreationForm, self).save(commit=False) user.set_password(self.cleaned_data["password1"]) if commit: user.save() return userclass UserChangeForm(forms.ModelForm): """A form for updating users. Includes all the fields on the user, but replaces the password field with admin's password hash display field. """ password = ReadOnlyPasswordHashField() class Meta: model = MyUser fields = ('email', 'password', 'date_of_birth', 'is_active', 'is_admin') def clean_password(self): # Regardless of what the user provides, return the initial value. # This is done here, rather than on the field, because the # field does not have access to the initial value return self.initial["password"]class MyUserAdmin(UserAdmin): # The forms to add and change user instances form = UserChangeForm add_form = UserCreationForm # The fields to be used in displaying the User model. # These override the definitions on the base UserAdmin # that reference specific fields on auth.User. list_display = ('email', 'date_of_birth', 'is_admin') list_filter = ('is_admin',) fieldsets = ( (None, {'fields': ('email', 'password')}), ('Personal info', {'fields': ('date_of_birth',)}), ('Permissions', {'fields': ('is_admin',)}), ) # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin # overrides get_fieldsets to use this attribute when creating a user. add_fieldsets = ( (None, { 'classes': ('wide',), 'fields': ('email', 'date_of_birth', 'password1', 'password2')} ), ) search_fields = ('email',) ordering = ('email',) filter_horizontal = ()# Now register the new UserAdmin...admin.site.register(MyUser, MyUserAdmin)# ... and, since we're not using Django's built-in permissions,# unregister the Group model from admin.admin.site.unregister(Group)最后,別忘了在settings.py中定義AUTH_USER_MODEL:
AUTH_USER_MODEL = 'customauth.MyUser'
新聞熱點
疑難解答