Django Admin

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_staffis_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这个类

这个类中只含有passwordlast_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界面修改密码或添加用户数据已经不能再按照之前的表单系统啦

需要在appadmin.py中重写UserCreationFormUserChangeForm

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'
相关推荐
神奇侠202435 分钟前
快速入手-基于Django-rest-framework的serializers序列化器(二)
后端·python·django
神奇侠20242 小时前
快速入手-基于Django-rest-framework的第三方认证插件(SimpleJWT)权限认证(十)
django·simplejwt
唐古乌梁海2 小时前
【Django】教程-5-ModelForm增删改查+规则校验【正则+钩子函数】
django
WIN赢9 小时前
Spring Boot和Django的区别
spring boot·django·sqlite
神奇侠20249 小时前
快速入手-基于Django-rest-framework的APIView类升级版GenericAPIView(四)
django·genericapiview
星辰大海的精灵9 小时前
Django开发人员最常犯的错误及规避建议
后端·python·django
奔跑草-14 小时前
【服务端】使用conda虚拟环境部署Django项目
python·django·conda
Alfadi联盟 萧瑶19 小时前
Python-Django入手
开发语言·python·django
计算机徐师兄1 天前
Python Django基于人脸识别的票务管理系统(附源码,文档说明)
python·django·人脸识别·票务系统·票务管理系统·票务管理·人脸识别票务系统
暴力袋鼠哥1 天前
基于Django的巴马水晶宫旅游管理系统
python·django·旅游