Django实战1-权限管理功能实现-03:用户认证

栏目: Python · 发布时间: 5年前

内容简介:新建一个app, 名称叫system包含用户管理、菜单管理和权限管理等系统基础模块。sandboxMP项目使用的是自定义权限认证模型,模型说明:下面内容就是权限认证的模型详细内容,将如下内容复制到apps/system/models.py

新建一个app, 名称叫system包含用户管理、菜单管理和权限管理等系统基础模块。

  • 使用pycharm打开我们的项目,右键项目根目录,选择 New → Python Package, 在弹出的窗口输入apps,这个包就用来存放项目中创建的所有app.
  • 选择pycharm上方Tools,点击Runmanage.py Task..., 这时在pycharm下方会打开一个窗口,输入startapp system 回车创建app, 如下图:
Django实战1-权限管理功能实现-03:用户认证
  • 将刚刚创建的system 移动到 apps下
  • 为了能够顺利访问到我们新建的app,右键apps,选择Mark Directory as → Sources root
  • 修改sandboxMP/sandboxMP/settings.py 加入如下内容:
import sys
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
复制代码

1.2 创建权限认证模型

sandboxMP项目使用的是自定义权限认证模型,模型说明:

Menu: 菜单管理,用来存储系统可用的URL
Role: 角色组,通过外键关联Menu,角色组中的用户将继承Role关联菜单的访问权限
Structure:组织架构,包含单位和部门信息
UserProfile: 自定义用户认证模型,替换系统原有的User模型
复制代码

下面内容就是权限认证的模型详细内容,将如下内容复制到apps/system/models.py

from django.db import models
from django.contrib.auth.models import AbstractUser


class Menu(models.Model):
    """
    菜单
    """
    name = models.CharField(max_length=30, unique=True, verbose_name="菜单名")  # unique=True, 这个字段在表中必须有唯一值.
    parent = models.ForeignKey("self", null=True, blank=True, on_delete=models.SET_NULL, verbose_name="父菜单")
    icon = models.CharField(max_length=50, null=True, blank=True, verbose_name="图标")
    code = models.CharField(max_length=50, null=True, blank=True, verbose_name="编码")
    url = models.CharField(max_length=128, unique=True, null=True, blank=True)

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = '菜单'
        verbose_name_plural = verbose_name

    @classmethod
    def get_menu_by_request_url(cls, url):
        return dict(menu=Menu.objects.get(url=url))


class Role(models.Model):
    """
    角色:用于权限绑定
    """
    name = models.CharField(max_length=32, unique=True, verbose_name="角色")
    permissions = models.ManyToManyField("menu", blank=True, verbose_name="URL授权")
    desc = models.CharField(max_length=50, blank=True, null=True, verbose_name="描述")


class Structure(models.Model):
    """
    组织架构
    """
    type_choices = (("unit", "单位"), ("department", "部门"))
    name = models.CharField(max_length=60, verbose_name="名称")
    type = models.CharField(max_length=20, choices=type_choices, default="department", verbose_name="类型")
    parent = models.ForeignKey("self", null=True, blank=True, on_delete=models.SET_NULL, verbose_name="父类架构")

    class Meta:
        verbose_name = "组织架构"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class UserProfile(AbstractUser):
    name = models.CharField(max_length=20, default="", verbose_name="姓名")
    birthday = models.DateField(null=True, blank=True, verbose_name="出生日期")
    gender = models.CharField(max_length=10, choices=(("male", "男"), ("female", "女")),
                              default="male", verbose_name="性别")
    mobile = models.CharField(max_length=11, default="", verbose_name="手机号码")
    email = models.EmailField(max_length=50, verbose_name="邮箱")
    image = models.ImageField(upload_to="image/%Y/%m", default="image/default.jpg",
                              max_length=100, null=True, blank=True)
    department = models.ForeignKey("Structure", null=True, blank=True, on_delete=models.SET_NULL, verbose_name="部门")
    post = models.CharField(max_length=50, null=True, blank=True, verbose_name="职位")
    superior = models.ForeignKey("self", null=True, blank=True, on_delete=models.SET_NULL, verbose_name="上级主管")
    roles = models.ManyToManyField("role", verbose_name="角色", blank=True)

    class Meta:
        verbose_name = "用户信息"
        verbose_name_plural = verbose_name
        ordering = ['id']

    def __str__(self):
        return self.name

复制代码

1.3 使用模型

定义好模型后,还要告诉Django使用这些模型,我们需要修改settings.py文件,在INSTALLED_APPS中添加models.py所在应用的名称:

INSTALLED_APPS = [
     ...原内容省略...
     'system',
]
复制代码

想要使用自定义的认证模型UserProfile, 还需要在setting.py中添加下面内容:

AUTH_USER_MODEL = 'system.UserProfile'
复制代码

注意:

在定义用户模型的时候使用到了ImageField字段类型,在执行makemigrations前需要安装依赖包:pillow,打开CMD窗口,进入本项目的 python 虚拟环境,然后安装pillow:

C:\Users\RobbieHan>workon sandboxMP
(sandboxMP) C:\Users\RobbieHan>pip install pillow
复制代码

也可以在pycharm 的Terminal终端窗口执行安装命令: pip install pillow

最后执行makemigrations 和 migrate来生成数据表, 使用pycharm Tools,点击Runmanage.py Task..., 在manage.py窗口输入下面命令:

makemigrations
migrate
复制代码

1.4 模型(Models)相关知识点

字段类型:

在权限认证模型中使用到的字段类型如下:

CharField: 用来存储字符串,必须制定一个参数 max_length用来限定字段最大长度
Foreignkey: 是一个关联字段,创建多表之间的多对一关系,如果创建同表之间的递归关联关系,可以使用models.ForeignKey('self')
ManyToManyField: 用来实现多对多的关联关系
DateField: 日期时间字段
EmailField: email字段,用来检查email地址是否合法
ImageField: 图片字段,用来定义图片上传和图片检查,需要安装pillow库
复制代码

字段选项:

unique: 设置为True, 则表示这个字段必须有唯一值,这是从数据库级别来强制数据唯一,后面我们还会介绍通过form验证来确保数据输入的唯一
verbose_name:
blank: 默认值是False, 设置为True,则该字段润许为空
null: 默认值是False,如果为True,Django会在数据库中将空值转存为NULL
choices: 是一个可迭代结构(元祖),每个元组中的第一个元素,是存储在数据库中的值;第二个元素是使人容易理解的描述。
复制代码

on_delete :在django2.0版本以前,定关联字段时,on_delete选项并不是必须的,而在django2.0版本后,在定义关联字段时on_delete是必须要定义的,常用参数如下:

on_delete=models.CASCADE,     # 删除关联数据,与之关联也删除
on_delete=models.DO_NOTHING,  # 删除关联数据,什么也不做
on_delete=models.PROTECT,     # 删除关联数据,引发错误ProtectedError
on_delete=models.SET_NULL,    # 删除关联数据,与之关联的值设置为null
on_delete=models.SET_DEFAULT, # 删除关联数据,与之关联的值设置为默认值
复制代码

需要注意的是在使用SET_NULL的时候,该字段在模型定义的时候需要设置可为空,例如:

user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)
复制代码

同样在使用SET_DEFAULT的时候,需要预先定义default:

user = models.ForeignKey(User, on_delete=models.SET_DEFAULT, default='默认值')
复制代码

更多字段类型和字段选项请参考:

docs.djangoproject.com/en/1.11/ref…

2 用户认证和访问限制

用户登录认证的需求如下:

  • 用户登陆系统才可以访问某些页面,
  • 如果用户没有登陆而直接访问就会跳转到登陆界面,
  • 用户在跳转的登陆界面中完成登陆后,自动访问跳转到之前访问的地址,
  • 用户可以使用用户名、手机号码或者其他字段作为登陆用户名。

在pycharm中,选中sandboxMP/apps/system,右键,选择 New → Python File, 在弹出的窗口输入名称:views_user,在刚创建的页面中导入需要的模块:

from django.shortcuts import render
from django.views.generic.base import View
from django.http import HttpResponseRedirect
from django.contrib.auth import authenticate, login, logout
from django.urls import reverse
复制代码

说明: 以下创建的视图,都是写在sandboxMP/apps/system/views_user.py文件中

2.1 创建index页面视图

index页面视图,是本项目创建的第一个视图:

class IndexView(View):

    def get(self, request):
        return render(request, 'index.html')
复制代码

知识点介绍:

1、视图:Django官方文档对“视图”的介绍是用来封装处理用户请求和返回响应的逻辑。

我们可以定义视图函数,用来接受Web请求并返回Web响应,也可以使用基于类的视图对象,本项目的视图实现都是基于类创建的视图,和基于函数的视图相比据有一定的区别和优势:

  • 可以通过单独的方法编写与HTTP方法相关的代码(GET, POST等),无需通过条件分支来判断HTTP方法
  • 可将代码分解成可重用的组件,例如Mixin(多继承),发挥面向对象技术优势,使用更加灵活,易于扩展

2、render函数:Django的快捷函数,结合给定的模板和一个给定的上下文字典,并返回一个选然后的HttpRespose对象,语法:render(request, template_name, context=None, content_type=None, status=None, using=None),其中 request 和template_name必须参数,其它为可选参数。

2.2 创建用户登陆视图

在创建用户登陆视图前,先创建一个sandboxMP/apps/system/forms.py文件,用来做登陆用户的输入验证,内容如下:

from django import forms


class LoginForm(forms.Form):
    username = forms.CharField(required=True, error_messages={"requeired": "请填写用户名"})
    password = forms.CharField(required=True, error_messages={"requeired": "请填写密码"})

复制代码

创建用户登陆视图:

from .forms import LoginForm


class LoginView(View):

    def get(self, request):
        if not request.user.is_authenticated:
            return render(request, 'system/users/login.html')
        else:
            return HttpResponseRedirect('/')

    def post(self, request):
        redirect_to = request.GET.get('next', '/')
        login_form = LoginForm(request.POST)
        ret = dict(login_form=login_form)
        if login_form.is_valid():
            user_name = request.POST['username']
            pass_word = request.POST['password']
            user = authenticate(username=user_name, password=pass_word)
            if user is not None:
                if user.is_active:
                    login(request, user)
                    return HttpResponseRedirect(redirect_to)
                else:
                    ret['msg'] = '用户未激活!'
            else:
                ret['msg'] = '用户名或密码错误!'
        else:
            ret['msg'] = '用户和密码不能为空!'
        return render(request, 'system/users/login.html', ret)
复制代码
知识点介绍:

Django使用会话和中间件来拦截认证系统中的请求对象。它们在每一个请求上提供一个request.user属性,表示当前的用户。如果当前的用户没有登入,该属性将设置成AnonymousUser的一个实例,否则将会是User实例。

1、request.user.is_authenticated:用来判断用户是否登入,如LoginView中:

# 当用户访问登陆页面时,判断用户如果未登入则访问登陆页面,如果登入则跳转到首页
if not request.user.is_authenticated:
    return render(request, 'system/users/login.html')
else:
    return HttpResponseRedirect('/')
复制代码

2、is_valid():Form实力的一个方法,用来做字段验证,当输入字段值合法时,它将返回True,同时将表单的数据存放到cleaned_data属性中。

3、authenticate(request=None, **credentials):用来认证用户,credentials为关键字参数,默认为username和password,如果通过认证后端检查,则返回一个User对象。

4、login(request, user, backend=None):用来从视图中登陆一个用户,同时将用户的ID保存在session表中。注意:在调用login()之前必须使用authenticate()成功认证这个用户。

5、HttpResponseRedirect[source]:用来重定向访问,参数是重定向的地址,可以是完整的URL,也可以相想读与项目的绝对路径。

2.3 创建用户登出视图

class LogoutView(View):

    def get(self, request):
        logout(request)
        return HttpResponseRedirect(reverse('login'))
复制代码
知识点介绍:

1、logout(request):登出用户。

2、reverse(viewname):根据url name来进行url的反向解析。

2.4 配置用户URL路由

想要通过URL来访问视图应用,还需要配置URL路由,修改sandboxMP/sandboxMP/urls.py:

from django.contrib import admin
from django.urls import path

from system.views_user import IndexView, LoginView, LogoutView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', IndexView.as_view(), name='index'),
    path('login/', LoginView.as_view(), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'),
]

复制代码

2.5 创建认证用户

在pycharm选择 Tools,点击Runmanage.py Task..., 在打开的窗口中输入createsuperuser,根据提示输入用户名,邮箱和密码,操作过程如下:

manage.py@sandboxMP > createsuperuser
"C:\Program Files\JetBrains\PyCharm2017.3.2\bin\runnerw.exe" C:\Users\RobbieHan\Envs\sandboxMP\Scripts\python.exe "C:\Program Files\JetBrains\PyCharm2017.3.2\helpers\pycharm\django_manage.py" createsuperuser D:/ProjectFile/sandboxMP
用户名:  admin
邮箱:  robbie_han@outlook.com
Warning: Password input may be echoed.
Password:  !qaz@wsx
Warning: Password input may be echoed.
Password (again):  !qaz@wsx
Superuser created successfully.

复制代码

运行项目,访问系统: http://127.0.0.1:8000 ,我们并没有登入用户,直接可以访问首页,这和我们的要求不符。接下来实现页面访问限制,要求必须登入用户才能访问。

2.6 页面访问限制

页面访问限制的实现需求:

  • 用户登录系统才可以访问某些页面
  • 如果用户没有登陆而直接访问就会跳转到登陆界面
  • 用户在跳转的登陆页面完成登陆后,自动访问跳转前的访问地址

新建sandboxMP/apps/system/mixin.py,写入如下内容:

from django.contrib.auth.decorators import login_required


class LoginRequiredMixin(object):
    @classmethod
    def as_view(cls, **init_kwargs):
        view = super(LoginRequiredMixin, cls).as_view(**init_kwargs)
        return login_required(view)
复制代码

修改sandboxMP/sandboxMP/settings.py, 加入LOGIN_URL

LOGIN_URL = '/login/'
复制代码

需要登入用户才能访问的视图,只需要继承LoginRequiredMixin即可,修改后的IndexView视图如下:

from .mixin import LoginRequiredMixin

class IndexView(LoginRequiredMixin, View): 

    def get(self, request):
        return render(request, 'index.html')
复制代码

注意:LoginRequiredMixin位于继承列表最左侧位置

重启项目,我们再次访问首页,打开浏览器,输入http://127.0.0.1:8000,这时我们会发现,浏览器中的URL会变成: http://127.0.0.1:8000/login/?next=/, 需要我们先登陆后才会跳转到首页。

使用我们在2.5小节中创建的用户:admin,密码: !qaz@wsx登陆系统

2.7 媒体文件的访问

尽管在创建用户时设置了默认头像,并且已经放置了默认头像使用的图片,但是用户登录后还是无法显示头像,所以还需要配置媒体文件的访问。

Django实战1-权限管理功能实现-03:用户认证

媒体文件是由用户上传的文件,路径是变化的,比如用户上传的头像文件。

设置文件上传目录

修改sandboxMP/sandboxMP/settings.py文件,添加如下配置:

MEDIA_URL = '/media/'

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
复制代码

打开sandboxMP/sandboxMP/urls.py,新增如下配置:

from django.conf import settings
from django.urls import re_path
from django.views.static import serve

if settings.DEBUG:
    urlpatterns += [
        re_path(r'^media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}),
    
    ]
复制代码

刷新页面就可以看到用户头像了,需要注意的是,这里之所以使用if settings.DEBUG,是因为这种配置模式应该仅限用于开发模式,在生产环境应该通过web前端来处理这些媒体文件的访问。

最新最全文档发布在知识星球,可以通过微信搜索公众号“知识星球”,直接回复"52824366"获得访问入口

本节文档对应源码版本: github.com/RobbieHan/s…

非常欢迎感兴趣的朋友,到我的Github或掘金上做客,闲暇之余给个赞或Star,赠人玫瑰手留余香

文档配套项目地址: github.com/RobbieHan/s…

轻量级办公管理系统项目开源地址: github.com/RobbieHan/g…


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Haskell School of Music

The Haskell School of Music

Paul Hudak、Donya Quick / Cambridge University Press / 2018-10-4 / GBP 42.99

This book teaches functional programming through creative applications in music and sound synthesis. Readers will learn the Haskell programming language and explore numerous ways to create music and d......一起来看看 《The Haskell School of Music》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

html转js在线工具
html转js在线工具

html转js在线工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具