服务端编程(十二)- Django - authentication 认识用户验证

前言 ´・ᴗ・`

  • 本节主要讲了
  • 本篇内容将会帮助你学习…
    • 1 学会添加用户
    • 2 了解 django的authentication (简写为auth)的概念 运用方式
    • 3 实现 登录后跳转回登录前的特定信息页 request.path 用法
    • 3 利用 auth 限制用户的访问
    • 5 自定义权限并运用

authentication

就是用户身份验证 是django的一个app 用于验证身份 另外授权
这里有user group的概念 就是为了统一管理 统一授权
比如一组人 统一授予 能够借书的权力

这个app早就启用了 和admin session 是一样的情况:在setting.py 能见到他们

INSTALLED_APPS = [
    ...
    'django.contrib.auth',  #Core authentication framework and its default models.
    'django.contrib.contenttypes',  #Django content type system (allows permissions to be associated with models).
    ....

MIDDLEWARE = [
    ...
    'django.contrib.sessions.middleware.SessionMiddleware',  #Manages sessions across requests
    ...
    'django.contrib.auth.middleware.AuthenticationMiddleware',  #Associates users with requests using sessions.
    ....

还记得我们当时 createsuperuser 嘛 那个超级用户是用来管理的
然而我们想模拟普通用户的操作环境 就需要 创立普通账户

创立账户

当然 做快捷的方法 admin
在这里插入图片描述 但是那是基于后台的
这明显不符合实际情况
一般用户只会得到:
在这里插入图片描述
(我不会告诉你我忘了root密码是啥了)

代码操作应当是更改user数据库:

from django.contrib.auth.models import User

# Create user and save to the database
user = User.objects.create_user('myusername', '[email protected]', 'mypassword')

# Update fields and then save again
user.first_name = 'John'
user.last_name = 'Citizen'
user.save()

总之 无论哪种方式 我们到后台都会看到:
在这里插入图片描述

设置url

我们之前是有两个url映射集合的

  • catalog/url.py catalog应用的url映射
  • url.py 网站的url映射
    我们用一句include 将catalog的映射放到网站的url映射集中 让catalog的url真正起作用:
path('catalog/', include('catalog.urls')),  

现在 同样的道理
我们将authentication这个应用的url映射集合 也放进去 locallibrary/locallibrary/urls.py(根目录的) 添加

path('accounts/', include('django.contrib.auth.urls'))

django帮我们已经集成了一堆url映射 如下:

accounts/ login/ [name='login']
accounts/ logout/ [name='logout']
accounts/ password_change/ [name='password_change']
accounts/ password_change/done/ [name='password_change_done']
accounts/ password_reset/ [name='password_reset']
accounts/ password_reset/done/ [name='password_reset_done']
accounts/ reset/<uidb64>/<token>/ [name='password_reset_confirm']
accounts/ reset/done/ [name='password_reset_complete']

创立模板

由于authentication是内建的 我们没必要像catalog一样另外立一个模板目录 而是在

locallibrary (django project folder)
   |_catalog
   |_locallibrary
   |_templates (new)
                |_registration

也就是根目录template那边新建registeration文件夹

然后 在settings.py 添加 模板文件的搜索路径
在这里插入图片描述
这样我们django就可以找到 我们创立的文件夹里面的模板了2333
如果所有模板都创立完成 大概是这个样子
在这里插入图片描述
下面开始弄模板啦

  1. /locallibrary/templates/registration/login.html
{% extends "base_generic.html" %}

{% block content %}

{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}

{% if next %}
    {% if user.is_authenticated %}
    <p>Your account doesn't have access to this page. To proceed,
    please login with an account that has access.</p>
    {% else %}
    <p>Please login to see this page.</p>
    {% endif %}
{% endif %}

<form method="post" action="{% url 'login' %}">
{% csrf_token %}

<div>
  <td>{{ form.username.label_tag }}</td>
  <td>{{ form.username }}</td>
</div>
<div>
  <td>{{ form.password.label_tag }}</td>
  <td>{{ form.password }}</td>
</div>

<div>
  <input type="submit" value="login" />
  <input type="hidden" name="next" value="{{ next }}" />
</div>
</form>

{# Assumes you setup the password_reset view in your URLconf #}
<p><a href="{% url 'password_reset' %}">Lost password?</a></p>

{% endblock %}

这时 如果你访问 http://127.0.0.1:8000/accounts/login/
在这里插入图片描述
就会有登录界面
问题在于 用户登录以后 第一个重定向的页面应当是主页才对 但django默认设置并不是
我们设置一波 settings.py: 添加:

# Redirect to home URL after login (Default redirects to /accounts/profile/)
LOGIN_REDIRECT_URL = '/'

这样 登录以后就直接到主页去了

  1. registration/logged_out.html 登出页面
{% extends "base_generic.html" %}

{% block content %}
<p>Logged out!</p>  

<a href="{% url 'login'%}">Click here to login again.</a>
{% endblock %}

你可以像我一样 把这玩意放到侧边栏sidebar
登录前
在这里插入图片描述
登录后
在这里插入图片描述
具体实现就是在 base_genric.html 那个模板的模板
sidebar 部分:

<ul class="sidebar-nav">
          <li></li>
          <li></li>
          {% if user.is_authenticated %}
            <li>Hello, {{ user.get_username }}</li>
            <li><a href="{% url 'my-borrowed' %}">My Borrowed</a></li>
            <li><a href="{% url 'logout'%}?next={{request.path}}">Logout?</a>
            </li>
          {% else %}
            <li><a href="{% url 'login'%}?next={{request.path}}">Hey, wanna Login?</a></li>
          {% endif %}
          <li><a href="{% url 'index' %}">Home</a></li>
          <li><a href="{% url 'books' %}">All books</a></li>
          <li><a href="{% url 'authors' %}">All authors</a></li>
      </ul>
      <ul>

这里 我们 根据 {{ user.is_authenticated }} 是否为 true(用户是否通过身份验证) ,来有条件地显示文本。
另外 怎么实现 我登录后要跳转回登陆前的信息页呢?
这里就搞定了! ?next={{}}指定 下一个跳转页面的地址 就是你点击login的地址
比如 我在显示books的时候
在这里插入图片描述
点击login
http://127.0.0.1:8000/accounts/login/?next=/catalog/books/
在这里插入图片描述
这样登陆以后 我们就会转到 books的页面 也就是我们登录前的页面 又回去了 不同的是 添加了身份
在这里插入图片描述
4. registration/password_reset_form.html 密码重置页面 就是拿到你的邮箱然后发email的

{% extends "base_generic.html" %}

{% block content %}
  <form action="" method="post">
  {% csrf_token %}
  {% if form.email.errors %}
    {{ form.email.errors }}
  {% endif %}
      <p>{{ form.email }}</p> 
    <input type="submit" class="btn btn-default btn-lg" value="Reset password">
  </form>
{% endblock %}
  1. registration/password_reset_done.html 密码重置完成页面
{% extends "base_generic.html" %}

{% block content %}
  <p>We've emailed you instructions for setting your password. If they haven't arrived in a few minutes, check your spam folder.</p>
{% endblock %}
  1. registration/password_reset_email.html 这是email的文本内容
Someone asked for password reset for email {{ email }}. Follow the link below:
{{ protocol}}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
  1. registration/password_reset_confirm.html 确认密码重置页面
{% extends "base_generic.html" %}

{% block content %}
    {% if validlink %}
        <p>Please enter (and confirm) your new password.</p>
        <form action="" method="post">
        {% csrf_token %}
            <table>
                <tr>
                    <td>{{ form.new_password1.errors }}
                        <label for="id_new_password1">New password:</label></td>
                    <td>{{ form.new_password1 }}</td>
                </tr>
                <tr>
                    <td>{{ form.new_password2.errors }}
                        <label for="id_new_password2">Confirm password:</label></td>
                    <td>{{ form.new_password2 }}</td>
                </tr>
                <tr>
                    <td></td>
                    <td><input type="submit" value="Change my password" /></td>
                </tr>
            </table>
        </form>
    {% else %}
        <h1>Password reset failed</h1>
        <p>The password reset link was invalid, possibly because it has already been used. Please request a new password reset.</p>
    {% endif %}
{% endblock %}
  1. password_reset_complete.html 密码重置完成页面
{% extends "base_generic.html" %}

{% block content %}
  <h1>The password has been changed!</h1>
  <p><a href="{% url 'login' %}">log in again?</a></p>
{% endblock %}

不过 其实这里我们不会发送email 因此这是框架 但并没有真正实现email验证 重设密码的操作 你可以试试 忘记密码 选项:就会报错
你可以参考官方文档 我这里就不展开了 感觉目前没啥用2333 后面会讲怎么实现 这里跳过

基于函数的视图 限制用户访问 提供登录跳转

对于 基于函数的视图view 也就是我们的主页那种 view.py 写了个def index(request)就完事的
我们用一个装饰符 实现 限制未登陆用户 并且还能在用户登陆完以后再跳转回去:)
只需要加两句话 (我自己都不相信django把这事变得这么简单2333)
from django.contrib.auth.decorators import login_required
然后index函数前面 上一行 加上@login_required:如图
在这里插入图片描述
然后你访问 http://127.0.0.1:8000/ 或者 http://127.0.0添加链接描述.1:8000/catalog (两个其实一样的view)
页面就会跳转
在这里插入图片描述
当然 如果没有装饰符的页面 当然就没有限制
比如 你点击那个all books 它就进入books里面了 正常显示
在这里插入图片描述
在这里插入图片描述

基于类的视图 限制用户访问 提供登录跳转

这次我们对all books 试一下 这个view的确是基于 类的
我们改成这个样子:

class BookListView(LoginRequiredMixin,generic.ListView):
    model = Book
    paginate_by = 4
    login_url = '/accounts/login/'
    redirect_field_name = 'miaomiaomiao'

login_url 就是要跳转的登录页面
然后redirect_field_name 就是 类似‘next’ 的工作
你可以自定义 不过为了和前面主页跳转的那个名字统一 还是用next为好
这里欢乐一下用了miaomiaomiao

然后未登录状态下 点击all books
在这里插入图片描述
然后登录 再成功跳转
在这里插入图片描述
注意 下面这种写法有什么问题?
在这里插入图片描述
你会发现 代码是无效的
super(继承) 类的时候 记得顺序是有讲究的(~ ̄▽ ̄)~

用户验证的应用——用户借书

除了上述两大重要的 页面显示的控制问题
针对我们图书馆的应用 我们可以弄个用户借书的功能实现

首先 数据库是关键 要建立 用户到书本实例的 外键 这样谁借了书 借了什么书 啥时候还 一目了然
model.py 添加:

from django.contrib.auth.models import User
from datetime import date

在book instance里面 添加borrow这个外键

borrower = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)

@property
def is_overdue(self):
    if self.due_back and date.today() > self.due_back:
        return True
    return False

然后 数据库迁移

python3 manage.py makemigrations
python3 manage.py migrate

然后 在admin添加上borrow字段
在这里插入图片描述
接下啦 我们就去创造数据咯
在admin添加就好

然后 我们就可以着手 设计“我的借阅”这种页面了
首先是view的设计:

class LoanedBooksByUserListView(LoginRequiredMixin,generic.ListView):

    model = BookInstance
    template_name ='catalog/bookinstance_list_borrowed_user.html'
    paginate_by = 10
    
    def get_queryset(self):
        return BookInstance.objects.filter(borrower=self.request.user).filter(status__exact='o').order_by('due_back')

·(status__exact='o') 这个o 指的是on load 被借走的状态 也就是筛选了借阅者和书本状态 两个因素 然后显示出来
接下来 url 映射添加:

path('mybooks/', views.LoanedBooksByUserListView.as_view(), name='my-borrowed'),

然后是模板:catalog/bookinstance_list_borrowed_user.html

{% extends "base_generic.html" %}

{% block content %}
    <h1>Borrowed books</h1>

    {% if bookinstance_list %}
    <ul>

      {% for bookinst in bookinstance_list %} 
      <li class="{% if bookinst.is_overdue %}text-danger{% endif %}">
        <a href="{% url 'book-detail' bookinst.book.pk %}">{{bookinst.book.title}}</a> ({{ bookinst.due_back }})        
      </li>
      {% endfor %}
    </ul>

    {% else %}
      <p>There are no books borrowed.</p>
    {% endif %}       
{% endblock %}

当然 catalog/templates/base_generic.html 添加一项 我的借阅(my borrow)
在这里插入图片描述
最后大概是这个样式:
在这里插入图片描述

图书管理员账户——个性化定制 权限

我们除了普通用户的管理 还有管理员账户的问题
这里就涉及 具体的权限问题
那么如何添加具体的权限呢?比如 图书管理员可以设置 这本书已经还了 然而普通用户不可以
我们在model.py book instance类 meta字段:

class BookInstance(models.Model):
    ...
    class Meta:
        ...
        permissions = (("Set_book_as_returned", "可以还书"),)

这里我们就添加一个权限

  • 名称 可以还书
  • 模板变量 Set_book_as_returned
    通过meta的 permission 字段
    注意 premission的 模板变量是perm 如图:
    在这里插入图片描述
    然后数据库迁移一下2333

之后 admin 设计另一个账户为 图书馆管理员账号
当然记得添加上我们刚刚那个“可以还书”的权限:) 我们直接添加user组 然后所有组里的都是图书管理员
如图
在这里插入图片描述
一定把超级用户权限关了
在这里插入图片描述
这里关上超级权限 否则没有实践我们权限控制的意义了

之后 模板中 我们就可以用{{perm}} 这个模板变量来筛选用户了 比如 这个是区分普通人与图书管理员的 其实就是可以标记图书还书的权限

{% if perms.catalog.Set_book_as_returned %}
    <!-- We can mark a BookInstance as returned. -->
    <!-- Perhaps add code to link to a "book return" view here. -->
{% endif %}

如何使用自己定制的权限

之前 我们设计过 基于函数 或者基于类 视图 的显示的控制
也就没登录的不能看什么页面之类的

现在我们设计了一些权限 比如 是否能够还书 也可以利用这些自定义权限 限制视图的显示
也就是限制一些没有权限的用户 比如:

  • 基于函数 我们用装饰器
from django.contrib.auth.decorators import permission_required

@permission_required('catalog.can_mark_returned')
@permission_required('catalog.can_edit')
def my_view(request):
    ...
  • 基于类 我们继承mixin以后 添加属性
class MyView(PermissionRequiredMixin, View):
    permission_required = ('catalog.can_mark_returned', 'catalog.can_edit')

用元祖放置多个权限 而且是同时满足的:)

真正运用 自定义权限

实现这个只有图书管理员能看的部分 只有图书管理员能看:)
在这里插入图片描述
实际上在base_generic.html 这么写就行了 if控制一波

{% if perms.catalog.Set_book_as_returned %}
          <ul>
            <li>-----staff management------</li>
            <li><a href="{% url 'borrowed' %}">staff manage books</a>
            </li>
          </ul>
          {% endif %}

总结 ´◡`

runserver 效果:
普通用户 user
在这里插入图片描述
将近1.5k 写博客到头秃:)
在这里插入图片描述
下一站 我们玩玩表单 图书馆应用还有一些功能没实现呢:)
服务端编程(十三)- Django - forms 表单的使用

我的专栏 希望能够帮到你 ( •̀ ω •́ )✧

发布了52 篇原创文章 · 获赞 57 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_43178828/article/details/104690916
今日推荐