Admin
Django-admin
界面是框架为我们提供的Web-APP
的管理工具
在之前的练习中,我们已经多次使用它进行APP
中表的DML
语句操作
接下来,我们可以继续来学习,如何将Admin
界面进行优化扩展,更加方便我们后台管理
admin注册设置
在admin
组件下,我们可以通过继承admin.ModelAdmin
类进行自定义admin
类的编写,用来控制展示后台所显示的数据,以及排序规则等等属性,基本语法像是下面这样
python
# models.py
class User(models.Model):
name = models.CharField(max_length=20)
age = models.IntegerField()
def __str__(self):
return self.name
python
#admin.py
class UserAdmin(admin.ModelAdmin):
fields = ('name','age')
# 控制admin界面模型类展示字段
admin.site.register(User, UserAdmin)
常用字段
-
list_display
:在admin
控制台数据列表页面展示更多的字段 -
list_display_links
:用来设置当前列表中由list_display
所设置的字段哪些可以点击 -
fieldsets
:将admin
界面下的表单输入栏进行分块
python
class UserAdmin(admin.ModelAdmin):
fields = ('name','age')
fieldsets = (
('第一个区域',
{
'fields': ('name',)
}
)
('第二个区域',
{
'fields': ('age',)
}
)
)
search_fields
:在admin
数据展示页,设置一个搜索框,设置的字段是可查找的属性
python
search_fields = ('name',)
list_filter
:以什么字段进行过滤,在admin
页面右侧展示ordering
:数据列表可以通过哪些字段进行排序
利用已有用户系统
from django.contrib.auth.models import User
用户字段
python
class User(AbstractUser):
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."),
},
)
# 必选。少于等于30个字符。 用户名可以包含字母、数字、_、@、+、.和-字符
first_name = models.CharField(_('first name'), max_length=30, blank=True)
# 可选。 少于等于30个字符
last_name = models.CharField(_('last name'), max_length=150, blank=True)
# 可选。少于30个字符
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.'),
)
# 布尔值。指示用户是否可以访问Admin站点
is_active = models.BooleanField(
_('active'),
default=True,
help_text=_(
'Designates whether this user should be treated as active. '
'Unselect this instead of deleting accounts.'
),
)
# 布尔值。用户的账号是否激活。常使用这个标志为False来代替删除账号
date_joined = models.DateTimeField(_('date joined'), default=timezone.now
# 账户创建的时间。当账号创建时,默认设置为当前的datetime
password = models.CharField(_('password'), max_length=128)
# 必选。 密码的哈希及元数据。(Django不保存原始密码)。原始密码可以无限长而且可以包含任意字符
last_login = models.DateTimeField(_('last login'), blank=True, null=True)
# 用户最后一次登录的时间。如果这个用户没有登录过,这个字段将会是null,默认设置为的当前的datetime
is_superuser = models.BooleanField(
_('superuser status'),
default=False,
help_text=_(
'Designates that this user has all permissions without '
'explicitly assigning them.'
),
)
# 布尔值。指定这个用户拥有所有的权限而不需要给他们分配明确的权限
用户实例方法
get_username()
获取到当前的用户的用户名属性
is_authenticated()
检查用户是否已通过认证
is_anonymous()
检查用户是否未通过认证
set_password(password)
设置保存用户的密码,并且会负责密码的哈希加密工作
通常修改密码之后还需要调用
save()
方法进行数据更新
用户表方法
create_user(username, email=None, password=None, **extra_fields)
django
的用户创建需要使用User
表管理器中的create_user
函数进行该函数可以创建并保存一个用户
create_superuser(username, email, password, **extra_fields)
与上一个创建用户函数功能相同,但是会额外设置用户的
is_staff
及is_superuser
属性为True
创建一个
admin
后台超级用户
其他方法
python
from django.contrib.auth import authenticate
authenticate(username,password)
# 验证是否为合法用户,如果用户存在合法则返回一个user对象
python
from django.contrib.auth import logout,login
logout(request)
# 清除当前浏览器下用户session
login(request,user)
# 登录并保存当前用户session,参数user必须为使用authenticate验证过后的User对象
用户登录
- 登陆所需表单类
python
#forms.py
from django import forms
from django.contrib.auth.models import User
import re
class LoginForm(forms.Form):
username = forms.CharField(label="用户名",required=True,)
password = forms.CharField(
widget=forms.PasswordInput(),
label='密码',
required=True,
)
def clean_username(self):
username = self.cleaned_data.get("username").strip()
try:
User.objects.get(username=username)
except User.DoesNotExist:
raise forms.ValidationError("用户名 %s 不存在" % username)
else:
return username
- 登录所需模板页面
jinja2
<!DOCTYPE html>
<html>
<head>
<title>登录</title>
</head>
<body>
<form action='/login/' method="POST">
{% csrf_token %}
{{ errors }}
{% for field in form %}
<label>
{% if field.errors %}
<ul>
{% for error in field.errors%}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</label>
<p>{{ field.label }}: {{ field }}</p>
{% endfor %}
<input type="submit" value="登录">
</form>
</body>
</html>
- 登陆主要视图逻辑
python
#views.py
from django.contrib.auth import authenticate,logout as auth_logout,login as auth_login
def login(request):
if request.method == 'GET':
form = userForm()
return render(request, "login.html",{'form':form})
form = userForm(request.POST)
username = form.data['username']
password = form.data['password']
user = authenticate(username=username,password=password)
# 验证当前账号密码是否匹配合法
if user and user.is_active:
auth_login(request, user)
return redirect('/')
else:
return render(request, "login.html",{'form':form,'errors':'用户名或密码错误'})
在用户登录成功之后,将会重定向到主页,主页展示当前登录用户
登录用户可以直接在request.user
属性中获取到,主页的模板内容如下
- 主页模板
html
<!DOCTYPE html>
<html>
<head>
<title>主页</title>
</head>
<body>
欢迎你: {{ user }}
</body>
</html>
主页视图函数可以通过装饰器@login_required
或判断is_authenticated
状态来查看用户是否登录
只有登录用户可以访问该站点
当使用装饰器@login_required
时,需要在settings
下设置LOGIN_URL = '/login/'
将该属性设置为登录路由
- 首页视图函数
python
from django.contrib.auth.decorators import login_required
#@login_required
def index(request):
if request.user.is_authenticated():
return render(request, "index.html")
else:
return redirect('/login/')
注册功能
- 注册所需表单
python
#forms.py
class RegisterForm(forms.Form):
username = forms.CharField(label="用户名",
max_length=30,
required=True,
error_messages={'max_length':'账号长度最长为30'},
)
password = forms.CharField(label="密码",
min_length=6, max_length=30,
widget=forms.PasswordInput(),
required=True,
error_messages={'min_length':'密码长度最短为6'},
)
check_password = forms.CharField(label="重复密码",
min_length=6, max_length=30,
widget=forms.PasswordInput(),
required=True,
error_messages={'min_length':'密码长度最短为6'},
)
def clean_check_password(self):
password = self.cleaned_data.get('password')
check_password = self.cleaned_data.get('check_password')
if password != check_password and password and check_password:
raise ValidationError("重复输入密码错误")
def clean_username(self):
username = self.cleaned_data.get('username')
if username[:1] == '_':
raise forms.ValidationError("用户名不能以下划线打头")
try:
User.objects.get(username=username)
except User.DoesNotExist:
return username
raise forms.ValidationError("用户名已存在")
表单中,将对用户两次输入的密码进行校验并判断是否为空,而且不允许下划线作为用户名的开头字符
- 注册主要视图函数逻辑
python
#views.py
def register(request):
if request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
User.objects.create_user(username=username,password=password)
user = authenticate(username=username,password=password)
auth_login(request, user)
return redirect('/')
else :
form = RegisterForm()
return render(request, "register.html",{'form':form})
通过表单验证,并获取到对应用户账号密码,创建成功之后,验证并登录,并且重定向到首页
- 主页模板页面
jinja2
<!DOCTYPE html>
<html>
<head>
<title>注册</title>
</head>
<body>
{% if request.user.is_authenticated %}
<p>您已登录:</p> {{ user }}
{% else %}
<form action='/register/' method="POST">
{% csrf_token %}
{% for field in form %}
<label>
{% if field.errors %}
<ul>
{% for error in field.errors%}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</label>
<p>{{ field.label }}: {{ field }}</p>
{% endfor %}
<input type="submit" value="注册">
</form>
{% endif %}
</body>
</html>
当用户已经登录时,显示当前登录用户,反之则展示表单,以供用户填写
重载admin用户系统
django
自带的admin
模块下的用户系统主要负责用户账户,组,权限和基于cookie
的会话等业务
认证系统由以下部分组成:
-
用户:包含用户数据字段,是实现业务功能逻辑的基础
-
权限:控制用户进否可以执行某项任务的二进制(是/否)标志。
-
组:一种为多个用户加上标签和权限的常用方式。
-
消息:一种为指定用户生成简单消息队列的方式
默认方法
在项目中,我们将使用email作为用户的用户名,那么可以在自带用户表中写明
python
USERNAME_FIELD = 'email'
# 该属性设置当前表的登陆字段
描述我们自定义用户模型上作唯一标识符字段名称的字符串,字段必须是唯一的,在定义中设置
python
unique=True
设置某个字段为必填项
python
REQUIRED_FIELDS = ['username']
is_authenticated()
判断用户是否已通过身份验证的方法,不意味任何权限,而且不检查用户的活动状态
get_username()
返回由USERNAME_FIELD制定的字段的值
get_full_name()
返回
first_name
加上last_name
中间加上一个空格,由于我们重新设置了表字段,那么这个默认函数需要我们重新给定返回值
get_short_name()
一个短的且非正式用户的标识符,返回
first_name
当你的项目重写该字段或直接去掉了first_name
,所以我们必须重新给定这个函数的返回值
set_password()
将用户的密码设置为给定的字符串
has_perm(perm, obj=None)
用户是否具有某个权限,如果给定
obj
,则需要根据特定对象实例检查权限
has_module_perms(app_label)
如果用户有权访问给定应用中的模型,则返回
True
这里我们这两个字段都设置为True
,可以让用户访问任一APP
is_staff()@property
如果用户允许访问管理网站,则返回
True
这里我们返回is_admin
的字段属性即可
关于字段属性,如果需要进行高度扩展,可以使用继承AbstractBaseUser
这个类
这个类中只含有password
,last_login
,以及is_active
三个字段属性
重载管理器
由于已经覆盖了默认的
admin
用户表,现在需要将其对应的管理器函数进行编写编写时主要关注一下两个父类函数的重写
python
create_user(username_field,password_filed=None,**other_fields)
normalize_email()将邮件地址规范化的类方法
user.save(using=self._db)
create_superuser(username, email, password)
#用户创建,is_admin设置为True即可
user.save(using=self._db)
python
from django.contrib.auth.models import BaseUserManager
class MyUserManager(BaseUserManager):
def create_user(self, username, email, password=None):
if not email :
raise ValueError('Users must have an email address')
if not username:
raise ValueError('Users must have an username')
#判断邮件和用户名是否具有
now = timezone.now()
#获取当前django的时间
user = self.model(
username=username,
email=self.normalize_email(email),
date_joined=now,
last_login=now,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, username, email, password):
user = self.create_user(username,
email,
password=password,
)
user.is_admin = True
user.save(using=self._db)
return user
重新定义新用户表的管理器之后,想要生效,还需要在对应的模型类表中覆盖
objects
属性
objects = MyUserManager()
重载表
下面是一个真实的重载自带用户表的模型类
python
from django.contrib.auth.models import AbstractBaseUser
class Member(AbstractBaseUser):
#AbstractBaseUser中只含有3个field: password, last_login和is_active.
email = models.EmailField(verbose_name='邮箱',max_length=255,unique=True,)
username = models.CharField(verbose_name="用户名", max_length=16, unique=True)
weibo_id = models.CharField(verbose_name="新浪微博", max_length=30, blank=True)
blog = models.CharField(verbose_name="个人网站", max_length=200, blank=True)
location = models.CharField(verbose_name="城市", max_length=10, blank=True)
profile = models.CharField(verbose_name="个人简介", max_length=140, blank=True)
avatar = models.CharField(verbose_name="头像", max_length=128, blank=True)
au = models.IntegerField(verbose_name="用户活跃度", default=0)
last_ip = models.IPAddressField(verbose_name="上次访问IP", default="0.0.0.0")
email_verified = models.BooleanField(verbose_name="邮箱是否验证", default=False)
date_joined = models.DateTimeField(verbose_name="用户注册时间", default=timezone.now)
topic_num = models.IntegerField(verbose_name="帖子数", default=0)
comment_num = models.IntegerField(verbose_name="评论数", default=0)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = MyUserManager()
#objects就是我们之前一直使用的管理器
#管理器用来维护我们的增删改查
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
def __str__(self):
return self.username
#标签中的数据实例
def is_email_verified(self):
return self.email_verified
#我们可以在模板中,通过实例出来的对象数据进行这个函数的调取,获取他是否验证过
def get_weibo(self):
return self.weibo_id
def get_username(self):
return self.username
#方法的圆括号在templates标签中必需省略!!
def get_email(self):
return self.email
#方法的圆括号在templates标签中必需省略!!
def get_full_name(self):
# The user is identified by their email address
return self.email
#get_full_name本来是获取first_name和last_name的
#但是由于我们重新设置了表结构,那么这个函数必须自定义
#方法的圆括号在templates标签中必需省略!!
def get_short_name(self):
# The user is identified by their email address
return self.username
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
return True
def has_module_perms(self, app_label):
"Does the user have permissions to view the app `app_label`?"
return True
def calculate_au(self):
"""
计算活跃度
公式:Topic * 5 + Comment * 1
"""
self.au = self.topic_num * 5 + self.comment_num * 1
return self.au
@property
#类中函数可以直接做为属性使用
def is_staff(self):
"Is the user a member of staff?"
# Simplest possible answer: All admins are staff
return self.is_admin
重载Admin表单
由于修改了默认的
django
表结构,此时在admin
界面修改密码或添加用户数据已经不能再按照之前的表单系统啦需要在
app
的admin.py
中重写UserCreationForm
和UserChangeForm
python
# 用户创建表单
from django import forms
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from people.models import Member, Follower
class UserCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label='密码', widget=forms.PasswordInput)
password2 = forms.CharField(label='确认密码', widget=forms.PasswordInput)
class Meta:
model = Member
fields = ('email', 'username')
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("两次输入验证失败")
return password2
#在form中的clean__field函数会在is_valid()函数验证时自动调用
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
#set_password将会采用django的加密算法将密码设置到对应的模型实例中
#在内存中创建的好的对象只有通过commit=True才被真正执行到数据库上
if commit:
user.save()
return user
python
# 密码改变时的展示表单
class UserChangeForm(forms.ModelForm):
password = ReadOnlyPasswordHashField()
class Meta:
model = Member
fields = ('email', 'password', 'username', 'is_active', 'is_admin',)
def clean_password(self):
return self.initial["password"]
#使用默认的save函数即可
python
# 真正的用户admin界面管理方式
class MyUserAdmin(UserAdmin):
form = UserChangeForm
add_form = UserCreationForm
list_display = ('id', 'email', 'username', 'email_verified', 'last_login','is_active','is_admin','last_ip')
list_display_links = ('id', 'email', 'username')
list_filter = ('email', 'email_verified',)
fieldsets = (
(None, {'fields': ('username', 'email', 'date_joined', 'password','is_active','is_admin','avatar')}),
('状态', {'fields': ('email_verified', 'last_ip', 'au', 'topic_num', 'comment_num')}),
('社交网络', {'fields': ('weibo_id', 'blog')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
#admin样式设置
#Fieldsets 使用 wide 样式将会有额外的水平空格.
'fields': ('email', 'username', 'password1', 'password2','is_active','is_admin')}
),
)
search_fields = ('id', 'email', 'username')
ordering = ('id', 'email', 'email_verified')
filter_horizontal = ()
#这个字段为了设置与groups关联的多选框
admin.site.register(Member, MyUserAdmin)
admin.site.register(Follower)
最终
还需要在
settings.py
文件下进行设置,覆盖默认的User
模型
AUTH_USER_MODEL = 'people.Member'