Django实现用户管理

Django实现简单博客系统

建议先看文章列表中的《Django实现简单博客系统》,毕竟得一步一步来嘛。当然有过 Django 开发经验的,那就不说了。
django 官网地址:https://docs.djangoproject.com/zh-hans/2.2/contents/
本节 github:https://github.com/champion-yang/django_blog/tree/dev/blog_02

前言

在 《Django实现简单博客系统》中,我实现了一个简单的博客系统,可以增加修改用户,可以增改文章,并且可以让别人看到我的博客。但是这么简单的东西怎么能体现出来我的技术呢,哈哈,必须得给他上难度。所以本文打算设计一个用户模块,允许用户注册登陆退出网站,并且超级管理员可以对用户进行管理。

Djanog 中用户管理 django.auth.contrib.auth 有默认的登录方法login(),但是没有注册方法。本文介绍了一种自己实现登陆的方法,当然也对既有的登陆方法做了说明(支持),采用的数据模型是 Django 自带的 User,也对用户信息做了扩展 UserProfile 。

其实针对 Django 中的用户注册,是有第三方的方法的。比如 django-registration 。django 登陆模块还可以与第三方的账号集成,比如公司的域账号,不过需要第三方提供相应的 api。可以通过 Social Auth 了解一下 https://github.com/omab/django-social-auth

第一节 - 自定义模版和静态文件

在这里插入图片描述

1. 自定义模版位置和静态文件

模版位置呢,就是我们定义好的 templates 这个位置,不建议把所有的模版都放在各自的app中。

还记得setting.py这个文件嘛?这个文件定义了项目中所有的配置,当然也会包括模版的位置。

# 模版位置
# BASE_DIR 指的是当前项目的跟路径
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': False,  # 不允许 Django 按照默认方式寻找模版文件
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
# 静态文件的位置
STATIC_URL = '/static/'
# 默认 setting 中是没有 STATICFILES_DIRS 的,自己加呗。
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "static"),
)

记得在根目录下创建 static 目录,然后丢一张图片进去试试看?
然后重点来了,访问地址 ‘http://127.0.0.1:8000/static/cqe.jpeg (记得替换为自己的图片名称)’ , 有么有发现你放进static的图片显示出来了~

**tips: **如果没有,检查static是否在根目录,检查static下的文件名称和url中的名称是否一致

如果 APP_DIRS = True,则找不到 app 下 static 中的照片~

2. 通用静态文件和模版设置

按照如下目录结构,在static下新增 css/ fonts/ images/ js 文件夹,不过里面具体的文件去哪里找呢?我觉得直接去我github复制吧,当然还可以去其他地方找,我就不贴具体方法了,得给百度一点发挥的地方,哈哈。(程序员最重要的其实不是编码的能力,而是获取信息的能力)

.
├── css
│   ├── bootstrap-theme.css
│   ├── bootstrap-theme.min.css
│   ├── bootstrap.css
│   ├── bootstrap.css.map
│   ├── bootstrap.min.css
│   ├── bootstrap.min.css.map
│   ├── imagecrop.css
│   └── mansory-style.css
├── fonts
│   ├── glyphicons-halflings-regular.eot
│   ├── glyphicons-halflings-regular.svg
│   ├── glyphicons-halflings-regular.ttf
│   ├── glyphicons-halflings-regular.woff
│   └── glyphicons-halflings-regular.woff2
├── images
└── js
    ├── bootstrap.js
    ├── bootstrap.min.js
    ├── jquery.js
    └── npm.js

我觉得我还可以发挥一下余热,再多说几句~关于上述文件夹的介绍

  • css:存放了所有的css文件,以css文件结尾的文件,被称为层叠样式表,定义了页面中表现的具体的样式,比如浮动,阴影,大小等。
  • fonts:字体文件。比如我想要页面中显示隶体,那这个字体文件就存放在这里
  • images:预计放所有的静态图片
  • js:存放的是 js 文件,js全称JavaScript,大学就学过的吧。可以说他撑起了前端的半边天。

模版设置:

其实就是设置一些头尾信息。
在templates目录中创建 header.html 和 footer.html, 内容如下:

# header.html
# 引入静态资源文件 load static 在 django 2.0之前的版本中 用的是 load staticfiles, 引入之后才可以进行静态资源的引用
{% load static %}
<div class="container">
    <nav class="navbar navbar-default" role="navigation">
        <div class="navbar-header">
            <a class="navbar-brand" href="https://www.baidu.com/"><img src="{% static '/images/cqe.jpeg' %}" width="100px"></a>
	    </div>
	<div>
	    <ul class="nav navbar-nav" role="navigation">
		<li><a href="{% url 'blog_title' %}">BLOG</a></li>
	    </ul>
	    <ul class="nav navbar-nav navbar-right" style="margin-right:10px">
		<li><a href="">LOGIN</a></li>
	    </ul>
	</div>
    </nav>
</div>
<script src="{% static 'js/jquery.js'%}"></script>
<script src="{% static 'js/bootstrap.js' %}"></script>

# footer.html
<div class="container">
    <p class="text-center">欲买桂花同载酒,终不似,少年游!</p>
</div>

请求 http://127.0.0.1:8000/blog/ 发现没变化?什么鬼~ (如果报错的话,看下自己的路由映射的路径是不是存在,看下urls.py 下是否有这一行代码 path('', blog_title, name='blog_title')

缺了最后一步!!!
我们需要修改 base.html 文件。为什么?你看下你请求的url映射的视图函数访问的是哪里?是不是 title.html, 他是不是在第一行就写了 继承 base.html !!!

{% load static %}
<!DOCTYPE html>
<html lang="zh-cn">
    <head>
    	<meta charset="utf-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1">
    	<title>{% block title %}{% endblock %}</title>
    	<link rel="stylesheet" href="{% static 'css/bootstrap.css' %}">
    </head>
    <body>
        {% include "header.html" %}
        <div class="container">
        	{% block content %}{% endblock %}
        </div>
        {% include "footer.html" %}
        {% block javascritp %}{% endblock %}
    </body>
</html>

嘿,出来了!
在这里插入图片描述

3. 关于访问 /admin 报错的探究

此时当我请求http://127.0.0.1:8000/admin/ 发现,我靠,报错了,TemplateDoesNotExist at /admin/ 模版找不到~

别慌,有我在!(真希望有个女孩可以允许我和她说这句话,哈哈)

“为将之道,当先治心。泰山崩于前而色不变,麋鹿兴于左而目不瞬,然后可以制利害,可以待敌。”

这个是因为我们在setting.py 文件中,设置了 'APP_DIRS': False ,不允许 Django 按照默认方式寻找模版文件,所以 Django 自带的模版文件就找不到了,一个笨办法就是找到原先的文件,复制到我们当前目录下,就可以了。

第二节 - 用户登陆

前戏结束了,让我们正式开始用户登陆功能吧

现在进入admin界面,登出,发现除了管理员,建立的其他用户都登不进来。而且我希望非管理员用户只能从前台登陆,登陆之后只有特定的功能。

功能设计导览图

1. 创建应用 account

	 该应用要实现用户登陆,注册,登出功能。`python3 manage.py startapp account` 创建应用,在 settings.py 中注册, 还有配置 url。具体方式之前有过说明,在此不进行二次说明。[点击查看](https://blog.csdn.net/qq_35208583/article/details/116531545)

2. 创建表单

	在 /account 下新增文件 forms.py,定义好用户字段和密码,也就是表单需要的参数。
from django import forms
class LoginForm(forms.Form):
    username = forms.CharField()
    password = forms.CharField(widget=forms.PasswordInput)

3. 编写视图函数

在这里插入图片描述
图例说明:当用户通过浏览器请求某个网址时,显示登陆对话框,这种情况下肯定是get的请求呀,想啥呢!就比如分享的链接你直接打开。当用户通过post的方式向服务器发送自己的用户名和密码,该视图函数接收到后,判断是否为本站的用户,如果是则允许登陆,如果不是,拜拜吧您嘞。

# view.py
from django.shortcuts import render
from django.http import HttpResponse
from django.contrib.auth import authenticate, login  # 用户认证和管理
from .forms import LoginForm


def user_login(request):  # 视图函数必须使用 request 作为第一个参数
    if request.method == "POST":
        login_form = LoginForm(request.POST)
        if login_form.is_valid():
            cd = login_form.cleaned_data
            user = authenticate(username=cd['username'], password=cd['password'])

            if user:
                login(request, user)
                return HttpResponse("Welcome you. You have been authenticated successfully")
            else:
                return HttpResponse("Sorry. Your username or password is not right")

        else:
            return HttpResponse("Invalid login")

    if request.method == "GET":
        login_form = LoginForm()
        return render(request, "account/login.html", {"form": login_form})

4. 编写登陆页面的模版

在/template/account/login.html 中

{% extends "base.html" %}
{% block title %}Login{% endblock %}
{% block content %}
<div class="row text-center vertical-middle-sm">
	<h1>Login</h1>
	<p>Input your username and password</p>
	<form class="form-horizontal" action="." method="post">
		{% csrf_token %}
		{{ form.as_p }}
		<input type="submit" value="Login">
	</form>
</div>
{% endblock %}

ok! 现在访问 http://127.0.0.1:8000/account/login/ 可以进行登陆啦!用户可以使用之前创建的测试用户,如果没有测试用户,访问 http://127.0.0.1:8000/admin/ 新建一个。

第三节 - 用户登陆扩展知识(使用内置方法)

1. 内置用户权限

这是官网权威地址!!!
在 setting.py 中的 django.contrib.auth 可以看到,这是我们创建项目的时候的一个默认应用,即权限管理应用。这个可以在django的安装包内找到。

Django 验证系统处理验证和授权。简单来说,验证检验用户是否是他们的用户,授权决定已验证用户能做什么。具体的使用需要到项目中实践。

2. 内置的登陆和登出

写好自定义的登陆模版之后,在 /account/urls.py 下新增路由映射地址
path('login/', auth_views.LoginView.as_view(template_name='account/login.html'), name='user_login'),,就可以了。不需要写登陆的视图函数!!!

登陆之后我们会重定向到 http://127.0.0.1:8000/accounts/profile/ ,一般会报错。是因为Django默认的登陆函数会重定向到这里,我们没有配置过这个地址。so,我们得修改下默认登陆的重定向地址。

默认的登出也是一样的操作:

  • 添加路由 path('logout/', auth_views.LogoutView.as_view(template_name='account/logout.html'), name='user_logout'),
  • 新增 logout.html 页面
  • 在 header.html 中记得 在 LOGOUT 这里,a 链接到 user_logout

修改默认登陆方式的重定向地址

在setting.py中增加 LOGIN_REDIRECT_URL = '/blog' 就可以了。

第四节 - 用户注册

在此之前所有的用户都是超级管理员添加的,这种方式适合封闭性比较强的网站,比如企业内部,目的是审查用户身份。但是对于一般社会性的网站,我们一般允许通过自行注册的方式成为网站用户。
设计

1. 注册表单类


class RegistrationForm(forms.ModelForm):
    password = forms.CharField(label='Password', widget=forms.PasswordInput)
    password2 = forms.CharField(label='Confirm Password', widget=forms.PasswordInput)

    class Meta:
        # 一个内部类,声明我们需要的数据模型,使用 fields 来说明我们选用的属性,或者使用 exclude 来排除我们不需要的属性
        model = User
        fields = ("username", "email")

    def clean_password2(self):
        """
        以 clean_ 开头的方法,会在调用表单实例对象的 is_valid() 方法时被执行
        :return:
        """
        cd = self.cleaned_data
        if cd['password'] != cd['password2']:
            raise forms.ValidationError("passwords do not match.")
        return cd['password2']

2. 视图函数编写

def register(request):
    if request.method == "POST":
        user_form = RegistrationForm(request.POST)
        if user_form.is_valid():
            new_user = user_form.save(commit=False)
            new_user.set_password(user_form.cleaned_data['password'])
            new_user.save()
            #return HttpResponse("successfully")
            return HttpResponseRedirect(reverse("account:user_login"))
        else:
            return HttpResponse("sorry, your can not register.")
    else:
        user_form = RegistrationForm()
        return render(request, "account/register.html", {"form": user_form})

3. 附加注册内容

如果需要用户填写手机号,但是Django 自带的 User 类没有该字段该怎么办?

定义一个新的表模型. /account/models.py
然后命令行执行数据表结构迁移的操作

python manage.py makemigrations account python manage.py migrate account
查看数据库,生成了 account_userprofile 数据表

class UserProfile(models.Model):
    user = models.OneToOneField(User, unique=True, on_delete=models.SET_NULL)  # 声明与User之间的关系是一一对应的
    birth = models.DateField(blank=True, null=True)  # blank = true 默认可以为空
    phone = models.CharField(max_length=20, null=True)

    def __str__(self):
        return 'user {}'.format(self.user.username)

4. 管理新增的注册内容

登陆 /admin 后可以对新注册的内容进行管理。

tips: 如果你发现这个时候你的 admin 页面丢失了样式,只剩下几个黑白框的时候,不要急!这个是我们把admin的模版文件复制到了我们的templates下,但是却没有修改里面 css/js 文件的调用路径。我的做法是

  1. setting.py 设置 STATIC_ROOT = os.path.join(BASE_DIR, “static”), 注释 STATICFILES_DIRS
  2. 命令行执行 python manage.py collectstatic命令,这将从Django资源包中复制必须的静态文件到STATIC_ROOT指示的static文件夹中,这其中包括admin界面所必须的css/js/image等。
  3. 在html文件中将原有的 css 路径替换为当前的 css 路径

在我们新增用户信息后,想让超级管理员在界面中对该信息进行集中管理。需要修改 admin.py 文件

from django.contrib import admin
from .models import UserProfile

class UserProfileAdmin(admin.ModelAdmin):
    list_display = ('user', 'birth', 'phone')
    list_filter = ('phone',)
admin.site.register(UserProfile, UserProfileAdmin)

在这里插入图片描述

附加:请求/响应对象属性剖析

1. HttpRequest 对象

当客户端向服务器发送请求的时候,Django 会创建一个包含请求数据的 HttpResponse 对象,并以参数 request 传给视图函数,即参数 request 所引用的对象就是 HttpResponse 对象。

  • path:当前页面的请求路径
  • GET:返回字典类对象。也就是前端通过get方式传来的参数。
  • POST:前端通过post方式传来的参数。
  • REQUEST:包括了get和post请求的参数,不推荐使用。
  • FILES:包含上传的文件 {“filename”: “上传的文件名”, “content-type”: “上传文件的内容类型”, “content”: “上传文件的原始内容”}。记得修改请求方式为 post ,并且要包含 enctype=“multipart/form-data” 才可以哦。
  • COOKIES: 所有的cookie。
  • META:包含了所有可能的请求头。
  • user:返回 django.contrib.auth.users.User 对象,表示当前登陆用户。可以使用 user.is_annoymous() 判断当前用户是否登陆。

2. HttpResponse 对象

这个我觉得还是看官网好,哈哈。

结束

“路漫漫其修远兮,吾将上下而求索”