登录        注册
本站基于Django开发,源码 Github 欢迎 Fork、Star。由于站点升级导致评论区留言信息丢失,欢迎前来发表新的评论

Django个人博客开发十三 | 注册登录

Django stormsha 8161浏览 216喜欢 0评论
本渣渣不专注技术,只专注使用技术,不是一个资深的coder,是一个不折不扣的copier

1、前言

前面我们已经创建好数据模型了,并且在admin后台中添加了一些测试用户。下面我们就要设计好站点的url路由、对应的处理视图函数以及使用的前端模板了。这个登录注册使用的是 Bootstrap 虽然 崔庆才 的博客是 Wordpress 主题也是使用的 Bootstrap 但是,抓取网页源码时把所有的样式都变成 style.css 文件了,懒得整理,所有这里注册登录页面没有继承基础模板 base.html

这里我并未采用:即方便有好用用户管理包django-allauth

因为之前写过一个网站,有现成的注册登录代码,直接就拷贝过来用了

如果想用django-allauth

推荐阅读:使用django-allauth实现第三方账号登录   django-allauth官方文档  推荐参考源码

2、配置路由

虽然登录系统属于站点的一级功能,采用一级路由更直观和更易于接受,但是这里采用二级路由的方式,同样使用反向解析名(name参数),便于以后维护升级用户中心

一级路由

blog -> blog -> urls.py

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 用户
    url(r'^accounts/', include('user.urls', namespace='accounts')),  
    # storm
    url('', include('storm.urls', namespace='blog')),  # blog
]

二级路由

blog -> user -> urls.py

from django.conf.urls import url
from .views import login_view, logout_view, register_view, profile_view, change_profile_view

urlpatterns = [
    url(r'^login/$', login_view, name='login'),
    url(r'^logout', logout_view, name='logout'),
    url(r'^register/$', register_view, name='register'),
    url(r'^profile/$', profile_view, name='profile'),
    url(r'^profile/change/$', change_profile_view, name='change_profile'),
]

3、编写视图

from django.contrib import auth
from .models import Ouser
from django.contrib.auth import authenticate
from django.contrib.auth.decorators import login_required
from django.contrib import messages
# 第四个是 auth中用户权限有关的类。auth可以设置每个用户的权限。
from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import render, redirect, HttpResponseRedirect
from .forms import UserForm, loginForm, ProfileForm
import re


# 注册
@csrf_exempt
def register_view(request):
    context = {}
    if request.method == 'POST':
        form = UserForm(request.POST)
        next_to = request.POST.get('next', 0)
        if form.is_valid():
            # 获得表单数据
            username = form.cleaned_data['username']
            password = form.cleaned_data['password']
            password2 = form.cleaned_data['password2']
            email = form.cleaned_data['email']
            context = {'username': username, 'pwd': password, 'email': email}
            if password.isdigit():
                context['pwd_error'] = 'nums'
                return render(request, 'account/signup.html', context)
            if password != password2:
                context['pwd_error'] = 'unequal'
                return render(request, 'account/signup.html', context)

            # 判断用户是否存在
            user = Ouser.objects.filter(username=username)
            Email = Ouser.objects.filter(email=email)
            pwd_length = len(password)
            if pwd_length < 8 or pwd_length > 20:
                context['pwd_error'] = 'length'
                return render(request, 'account/signup.html', context)

            user_length = len(username)

            if user_length < 5 or user_length > 20:
                context['user_error'] = 'length'
                return render(request, 'account/signup.html', context)
            if user:
                context['user_error']='exit'
                return render(request, 'account/signup.html', context)
            if not re.match('^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$', email):
                context['email_error'] = 'format'
                return render(request, 'account/signup.html', context)
            if Email:
                context['email_error'] = 'exit'
                return render(request, 'account/signup.html', context)
            # 添加到数据库(还可以加一些字段的处理)
            user = Ouser.objects.create_user(username=username, password=password, email=email)
            user.save()
            user = auth.authenticate(username=username, password=password)

            # 添加到session
            request.session['username'] = username
            request.session['uid'] = user.id
            request.session['nick'] = ''

            # 调用auth登录
            auth.login(request, user)
            # 重定向到首页
            if next_to == '':
                next_to = '/'
            return redirect(next_to)
    else:
        next_to = request.GET.get('next', '/')
        context = {'isLogin': False}
        context['next_to'] = next_to
    # 将req 、页面 、以及context{}(要传入html文件中的内容包含在字典里)返回
    return render(request, 'account/signup.html', context)


# 登陆
@csrf_exempt
def login_view(req):
    context = {}
    if req.method == 'POST':
        form = loginForm(req.POST)
        next_to=req.POST.get('next','/')

        remember = req.POST.get('remember', 0)
        if form.is_valid():
            # 获取表单用户密码
            username = form.cleaned_data['username']
            password = form.cleaned_data['password']
            context={'username':username,'pwd':password}
            # 获取的表单数据与数据库进行比较
            user = authenticate(username = username,password = password)
            if next_to=='':
                next_to='/'
            if user:
                if user.is_active:
                    # 比较成功,跳转index
                    auth.login(req,user)
                    req.session['username'] = username
                    req.session['uid'] = user.id
                    req.session['nick'] = None
                    req.session['tid'] = None
                    reqs = HttpResponseRedirect(next_to)
                    if remember != 0:
                        reqs.set_cookie('username',username)
                    else:
                        reqs.set_cookie('username', '', max_age=-1)
                    return reqs
                else:
                    context['inactive'] = True
                    return render(req, 'account/login.html', context)
            else:
                # 比较失败,还在login
                context['error'] = True
                return render(req, 'account/login.html', context)
    else:
        next_to = req.GET.get('next', '/')

        context['next_to'] = next_to

    return render(req, 'account/login.html', context)


# 登出
def logout_view(req):
    # 清理cookie里保存username
    next_to = req.GET.get('next', '/')
    if next_to == '':
        next_to = '/'
    auth.logout(req)
    return redirect(next_to)


@login_required
def profile_view(request):
    return render(request, 'oauth/profile.html')


@login_required
@csrf_exempt
def change_profile_view(request):
    if request.method == 'POST':
        # 上传文件需要使用request.FILES
        form = ProfileForm(request.POST, request.FILES, instance=request.user)
        if form.is_valid():
            form.save()
            # 添加一条信息,表单验证成功就重定向到个人信息页面
            messages.add_message(request, messages.SUCCESS, '个人信息更新成功!')
            return redirect('accounts:profile')
    else:
        # 不是POST请求就返回空表单
        form = ProfileForm(instance=request.user)
    return render(request, 'oauth/change_profile.html', context={'form': form})

注意一个点,在视图里使用next_to实现注册登录后跳转到注册登录前浏览页面

4、前端模板

在templates文件中添加登录注册页面文件,方便管理页面account、oauth

blog -> templates -> account -> base.html

{% load staticfiles %}
{% load static %}
<!doctype html>
<html lang="en">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="referrer" content="origin">
    <!-- TDK and ICO -->
    {% block title %}{% endblock %}

    <meta name="description" content="用户账号管理,使用django-allauth社交用户系统,支持微博、Github等社交账号登录,加入TendCode,查看更多信息。">
    <meta name="keywords" content="django-allauth,社交用户系统,OAuth 2.0">


    <!-- Bootstrap and font-awesome CSS -->
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}" type='text/css'>
    <link href="{% static 'font-awesome-4.7.0/css/font-awesome.min.css' %}" rel="stylesheet">

</head>
<body>

<!--主要内容块-->
{% block main %}
{% endblock %}


<footer class="container-fluid mt-4">
    <div class="card-body text-center px-0 f-16">
        <p class="card-text mb-1">Copyright&nbsp;&copy;&nbsp;2017-<span id="year-info"></span>
            <script>function getnewYear(){var d = new Date();var x = document.getElementById("year-info");
            x.innerHTML=d.getFullYear();}getnewYear()</script>
            <a href="https://github.com/Hopetree" target="_blank" title="博客作者的Github">Hopetree</a>.&nbsp;Powered&nbsp;by&nbsp;Django.
        </p>
        <p class="mb-0">
            <a href="http://www.miibeian.gov.cn/" target="_blank">粤ICP备17114114号-3</a>&nbsp;|
            <a href="/sitemap.xml" target="_blank">Sitemap</a>
            |&nbsp;<script type="text/javascript">var cnzz_protocol = (("https:" == document.location.protocol) ? "https://" : "http://");document.write(unescape("%3Cspan id='cnzz_stat_icon_1275912930'%3E%3C/span%3E%3Cscript src='" + cnzz_protocol + "s96.cnzz.com/z_stat.php%3Fid%3D1275912930' type='text/javascript'%3E%3C/script%3E"));</script>
        </p>
    </div>
</footer>
</body>
</html>

blog -> templates -> account -> login.html

{% extends 'account/base.html' %}

{% block title %}
<title>注册</title>
{% endblock %}
<!--主要内容块-->
{% block main %}
<main>
    <div class="container">
    <div class="row">
        <div class="col-12 col-sm-8 col-md-6 offset-sm-2 offset-md-3 px-xl-5">
            <div class="card rounded-0 px-3 px-lg-4">
                <div class="card-header text-center bg-white py-2">
                    <h3 class="my-1 text-info">注册</h3>
                </div>
                <div class="card-body card-login">
                    <form class="signup" id="signup_form" method="post" action="{% url 'accounts:register' %}">
                        <div id="div_id_email" class="form-group"> 
                            <label for="id_email" class="form-control-label  requiredField">E-mail
                                <span class="asteriskField">*</span> 
                            </label> 
                            <div class=""> 
                                <input type="email" name="email" value="{{ email }}" placeholder="E-mail地址" autofocus="autofocus" class="textinput textInput form-control" required id="id_email" />
                                <p id="error_1_id_email" class="help-block">
                                    <strong>
                                            {% if email_error == 'exit' %}此e-mail地址已被其他用户注册
                                            {% elif email_error == 'format' %}请输入的有效邮箱地址
                                            {% endif %}
                                    </strong>
                                </p>
                            </div> 
                        </div> 
                        <div id="div_id_username" class="form-group has-danger"> 
                            <label for="id_username" class="form-control-label  requiredField">用户名
                                <span class="asteriskField">*</span> 
                            </label> 
                            <div class=""> 
                                <input type="text" name="username" value="{{ username }}" placeholder="用户名"  minlength="1" maxlength="150" class="textinput textInput form-control form-control-danger" required id="id_username" /> 
                                <p id="error_1_id_username" class="help-block">
                                    <strong>{% if user_error == 'exit' %}已存在一位使用该名字的用户
                                        {% elif user_error == 'length' %}用户名限制5~20个字符
                                        {% endif %}</strong>
                                </p> 
                            </div> 
                        </div> 
                        <div id="div_id_password1" class="form-group has-danger"> 
                            <label for="id_password1" class="form-control-label  requiredField">
                                        密码<span class="asteriskField">*</span> 
                            </label> 
                            <div class=""> 
                                <input type="password" name="password" value="{{ pwd }}" placeholder="密码" class="textinput textInput form-control form-control-danger" required id="id_password1" /> 
                                <p id="error_1_id_password1" class="help-block">
                                    <strong>{% if pwd_error == 'nums' %}密码不能使用纯数字
                                            {% elif pwd_error == 'length' %}密码限制8~20个字符
                                            {% endif %}
                                    </strong>
                                </p> 
                            </div> 
                        </div> 
                        <div id="div_id_password2" class="form-group"> 
                            <label for="id_password2" class="form-control-label  requiredField">
                                        密码(重复)<span class="asteriskField">*</span> 
                            </label> 
                            <div class=""> 
                                <input type="password" name="password2" placeholder="密码(重复)" class="textinput textInput form-control" required id="id_password2" /> 
                                <p id="error_1_id_password2" class="help-block">
                                    <strong>{% if pwd_error == 'unequal' %}两次输入的密码必须相同{% endif %}</strong>
                                </p>
                            </div> 
                        </div>
                            <input type="hidden" name="next" value="{{ next_to }}"/>
                            <a class="secondaryAction" href="/accounts/login?next=%2Faccounts%2Flogin%2F">已有账号登录</a>
                            <button class="pull-right btn btn-info btn-sm rounded-0" type="submit">注册</button>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
</main>
{% endblock %}

blog -> templates -> index.html

<!--登录-->
<!--目前登录功能只有注册登录其它没必要没做-->
<div class="pull-right">
    {% if request.session.username|default:'' == '' %}
            <i class="fa fa-power-off"></i>
            <a href="{% url 'accounts:login' %}?next={{ request.path }}">登录</a>
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  <i class="fa fa-user-plus"></i>
            <a href="{% url 'accounts:register' %}?next={{ request.path }}">注册</a>
    {% else %}
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<i class="fa fa-user"></i>
            <a href="/">投稿</a>
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<i class="fa fa-user-circle-o" aria-hidden="true"></i>
            <a href="{% url 'accounts:profile' %}?next={{ request.path }}">{{ request.session.username }}</a>

            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<i class="fa fa-sign-out" aria-hidden="true"></i>
            <a href="{% url 'accounts:logout' %}?next={{ request.path }}">退出</a>
    {% endif %}
</div>
<!--登录注册结束-->

其余页面不再展示,可以去拷贝源码研究 blog

【注意】——由于新增的页面使用了bootstrap所以需要在从源码里拷贝bootstrap.min.css到

blog -> static -> css 文件下

【提示】——用户中心,可以上传用户头像需要创建一个存储用户上传的文件的文件夹 media

# 媒体文件收集
MEDIA_URL = "/media/"   # 媒体文件别名(相对路径) 和 绝对路径
MEDIA_ROOT = (
    os.path.join(BASE_DIR, 'media')
)

5、目前项目结构

.
|-- blog
|   |-- storm                   # 博客应用
|   |   |-- migrations          # 数据库映射文件
|   |   |-- templatetags        # 自定义模板标签文件
|   |   |   |-- __init__.py     # 声明模块,内容默认为空
|   |   |   |-- blog_tags.py    # 自定义数据筛选函数
|   |   |-- __init__.py         # 声明模块,内容默认为空
|   |   |-- admin.py            # 该应用的后台管理系统
|   |   |-- apps.py             # 应用配置,Django-1.9以后自动生成
|   |   |-- models.py           # 数据模块,使用ORM框架
|   |   |-- tests.py            # 自动化测试的模块  
|   |   |-- urls.py   
|   |   |-- views.py            # 执行响应的代码所在模块,是代码逻辑处理的主要地点,项目中大部分代码在这里编写    
|   |-- user                    # 用户应用
|   |   |-- migrations  
|   |   |-- templatetags        # 自定义模板标签文件
|   |   |   |-- __init__.py     # 声明模块,内容默认为空
|   |   |   |-- oauth_tags.py   # 自定义数据筛选函数 
|   |   |-- __init__.py     
|   |   |-- admin.py        
|   |   |-- apps.py   
|   |   |-- forms.py   
|   |   |-- models.py       
|   |   |-- tests.py
|   |   |-- urls.py          
|   |   |-- views.py        
|   |-- blog                # 项目的容器
|   |   |-- __init__.py     # 声明模块,内容默认为空
|   |   |-- settings.py     # 该 Django 项目的设置/配置。
|   |   |-- urls.py         # 该 Django 项目的 URL 声明; 一份由 Django 驱动的网站"目录"。
|   `-- wsgi.py             # 一个 WSGI 兼容的 Web 服务器的入口,以便运行你的项目
|   |-- static              # 静态文件
|   |   |-- css
|   |   |-- fonts
|   |   |-- images
|   |   |-- js
|   |-- media               # 用户上传的文件
|   |   |-- avatar          # 用户头头像
|   |-- templates               # 模板文件
|   |   |-- account             # 登录注册页面文件
|   |   |   |-- base.html       # 登录注册基础模板
|   |   |   |-- login.html      # 登陆页面
|   |   |   |-- signup.html     # 注册页面
|   |   |-- oauth               # 用户中心页面文件夹
|   |   |   |-- tags            # 用户
|   |   |   |   |-- user_avatar.html
|   |   |   |-- change_profile.html # 修改个人信息页面
|   |   |   |-- profile.html        # 个人中心
|   |   |-- about.html
|   |   |-- donate.html
|   |   |-- exchange.html
|   |   |-- index.html
|   |   |-- message.html
|   |   |-- project.html
|   |   |-- question.html
|   |   |-- resources.html
|   |   |-- technique.html
|   |   |-- wp-login.html
`-- manage.py

6、运行效果

20361

【友情提示】——如果发现有表达错误,或者知识点错误,或者搞不懂的地方,请及时留言,可以在评论区互相帮助,让后来者少走弯路是我的初衷。我也是一步步摸着石头走过来的, 深知网络上只言片语的图文教程,给初学者带来的深深困扰。 【建议】——在对项目结构不太熟悉时,参照完整源码少走弯路

转载请注明: StormSha » Django个人博客开发十三 | 注册登录

发表我的评论

表情
(0)个小伙伴在吐槽|0条评论
暂时没有评论,欢迎来尬聊!