一、前言
前面我們已經創建好數據模型了,並且在admin後臺中添加了一些測試用戶。下面我們就要設計好站點的url路由、對應的處理視圖函數以及使用的前端模板了。這個登錄註冊使用的是 Bootstrap 雖然 崔慶才 的博客是 Wordpress 主題也是使用的 Bootstrap 但是,抓取網頁源碼時把所有的樣式都變成 style.css 文件了,懶得整理,所有這裏註冊登錄頁面沒有繼承基礎模板 base.html
這裏有推薦一個方便又好用用戶管理包:django-allauth
如果想用django-allauth
推薦閱讀:使用django-allauth實現第三方賬號登錄 django-allauth官方文檔 推薦參考源碼
不過,這裏追求開發速度,就暫時先用着一個原有的註冊登錄代碼,後期視時間再做功能。
二、配置路由
雖然登錄系統屬於站點的一級功能,採用一級路由更直觀和更易於接受,但是這裏採用二級路由的方式,同樣使用反向解析名(name參數),便於以後維護升級用戶中心
一級路由
blog -> blog -> urls.py
from django.contrib import admin
from django.urls import path, include
app_name = 'fswy'
urlpatterns = [
# 後臺管理應用,django自帶
path('admin/', admin.site.urls),
# 用戶
path('accounts/', include(('apps.user.urls', "apps.user"), namespace="accounts")),
# fswy 應用
path('', include(('apps.fswy.urls', "apps.fswy"), namespace="blog")),
]
二級路由
blog -> apps -> user -> urls.py
from django.urls import path,re_path
from .views import login_view, logout_view, register_view, profile_view, change_profile_view
urlpatterns = [
path(r'login/', login_view, name='login'),
path(r'logout', logout_view, name='logout'),
path(r'register/', register_view, name='register'),
path(r'profile/', profile_view, name='profile'),
path(r'profile/change/', change_profile_view, name='change_profile'),
]
三、編寫視圖
blog -> apps -> user -> views.py
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
# Create your views here.
# 註冊
@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
實現註冊登錄後跳轉到註冊登錄前瀏覽頁面.
四、前端模板
在templates
文件中添加登錄註冊頁面文件,方便管理頁面account
、oauth
blog -> templates -> account -> base.html
{% 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/fswy" target="_blank" title="博客作者的Github">fswy</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">網站地圖</a>Sha
</p>
<!--站長統計-->
<div>
<script type="text/javascript">var cnzz_protocol = (("https:" == document.location.protocol) ? "https://" : "http://");
document.write(unescape("%3Cspan id='cnzz_stat_icon_1276375952'%3E%3C/span%3E%3Cscript src='" + cnzz_protocol + "s96.cnzz.com/z_stat.php%3Fid%3D1276375952%26online%3D1%26show%3Dline' type='text/javascript'%3E%3C/script%3E"));</script>
</div>
<!--站長統計結束-->
</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="login" method="POST" action="{% url 'accounts:login' %}">
{% csrf_token %}
{% if error %}
<div class="alert alert-block alert-danger"> <ul> <li>您提供的用戶名或密碼不正確。</li> </ul> </div>
{% endif %}
{% if inactive %}
<div class="alert alert-block alert-danger"> <ul> <li>您的賬號已被封禁。</li> </ul> </div>
{% endif %}
<div id="div_id_login" class="form-group"> <label for="id_login" class="form-control-label requiredField">
賬號<span class="asteriskField">*</span> </label> <div class=""> <input type="text" name="username" placeholder="用戶名或e-mail" autofocus="autofocus" value="{{ username }}" class="textinput textInput form-control" required id="id_login" /> </div> </div> <div id="div_id_password" class="form-group"> <label for="id_password" class="form-control-label requiredField">
密碼<span class="asteriskField">*</span> </label> <div class=""> <input type="password" value="{{ pwd }}" name="password" placeholder="密碼" class="textinput textInput form-control" required id="id_password" /> </div> </div> <div class="form-group"> <div id="div_id_remember" class="checkbox"> <label for="id_remember" class=""> <input type="checkbox" name="remember" class="checkboxinput" id="id_remember" />
記住我
</label> </div> </div>
<input type="hidden" name="next" value="{{ next_to }}"/>
<a class="secondaryAction" href="{% url 'accounts:register' %}">忘記密碼了?</a>
<button class="pull-right btn btn-info btn-sm rounded-0" type="submit">登錄</button>
<div class="login-title">
<span><a href="{% url 'accounts:register' %}">還沒賬號?去註冊</a></span>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</main>
{% endblock %}
blog -> templates -> base.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
blog->blog->settings.py
# 媒體文件收集
MEDIA_URL = "/media/" # 媒體文件別名(相對路徑) 和 絕對路徑
MEDIA_ROOT = (
os.path.join(BASE_DIR, 'media')
)
五、目前項目結構
.
|-- blog
| |-- fswy # 博客應用
| | |-- 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
六、運行效果
教程目錄
Django3.0+Python3.8+MySQL8.0 個人博客搭建一|前言
Django3.0+Python3.8+MySQL8.0 個人博客搭建二|創建虛擬環境
Django3.0+Python3.8+MySQL8.0 個人博客搭建三|創建博客項目
Django3.0+Python3.8+MySQL8.0 個人博客搭建四|創建第一個APP
Django3.0+Python3.8+MySQL8.0 個人博客搭建五|makemigrations連接MySQL數據庫的坑
Django3.0+Python3.8+MySQL8.0 個人博客搭建六|數據庫結構設計
Django3.0+Python3.8+MySQL8.0 個人博客搭建七|makemigrations創建數據庫的坑(第二彈)
Django3.0+Python3.8+MySQL8.0 個人博客搭建八|通過admin管理後臺
Django3.0+Python3.8+MySQL8.0 個人博客搭建九|博客首頁開發(一)
Django3.0+Python3.8+MySQL8.0 個人博客搭建十|整理項目結構
Django3.0+Python3.8+MySQL8.0 個人博客搭建十一|博客首頁開發(二)
Django3.0+Python3.8+MySQL8.0 個人博客搭建十二|博客首頁開發(三)
Django3.0+Python3.8+MySQL8.0 個人博客搭建十三|博客詳情頁面