本渣渣不专注技术,只专注使用技术,不是一个资深的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 © 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>. Powered by Django.
</p>
<p class="mb-0">
<a href="http://www.miibeian.gov.cn/" target="_blank">粤ICP备17114114号-3</a> |
<a href="/sitemap.xml" target="_blank">Sitemap</a>
| <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>
<i class="fa fa-user-plus"></i>
<a href="{% url 'accounts:register' %}?next={{ request.path }}">注册</a>
{% else %}
<i class="fa fa-user"></i>
<a href="/">投稿</a>
<i class="fa fa-user-circle-o" aria-hidden="true"></i>
<a href="{% url 'accounts:profile' %}?next={{ request.path }}">{{ request.session.username }}</a>
<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、运行效果
【友情提示】——如果发现有表达错误,或者知识点错误,或者搞不懂的地方,请及时留言,可以在评论区互相帮助,让后来者少走弯路是我的初衷。我也是一步步摸着石头走过来的, 深知网络上只言片语的图文教程,给初学者带来的深深困扰。 【建议】——在对项目结构不太熟悉时,参照完整源码少走弯路
转载请注明: StormSha » Django个人博客开发十三 | 注册登录