本渣渣不专注技术,只专注使用技术,不是一个资深的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个人博客开发十三 | 注册登录