一、数据表结构设计
项目应用account的模型MyUser是项目中的核心数据,它与每个项目应用的模型都存在数据关联。模型MyUser继承内置模型User,在内置模型User的基础上添加新的字段,用于完善用户信息。打开项目应用account的models.py定义模型MyUser,代码如下:
class MyUser(AbstractUser):
name = models.CharField(verbose_name="姓名",max_length=50,default='匿名用户')
introduce = models.TextField(verbose_name='简介',default="暂无介绍")
company = models.CharField(verbose_name="公司",max_length=100,default='暂无信息')
profession = models.CharField(verbose_name='职业',max_length=100,default='暂无信息')
address = models.CharField(verbose_name='地址', max_length=100, default='暂无信息')
telephone = models.CharField(verbose_name='电话', max_length=11, default='暂无信息')
wx = models.CharField(verbose_name='微信', max_length=50, default='暂无信息')
qq = models.CharField(verbose_name='qq',max_length=50,default='暂无信息')
wb = models.CharField(verbose_name='微博', max_length=100, default='暂无信息')
photo = models.ImageField(verbose_name='头像', blank=True,upload_to='images/user/')
create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
class Meta:
verbose_name = '用户'
verbose_name_plural = verbose_name
db_table = 'MyUser'
def __str__(self):
return self.name
二、路由定义
采用路由下发的方式,我们在Myblog的urls.py下设置account的路由
# Myblog的 urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('user/', include('app.account.urls')),
]
# account的 urls.py
urlpatterns = [
path('register', Register.as_view(), name="register"),
path('login', UserLogin.as_view(), name="userLogin"),
]
三、编写视图类
我们在account创建一个一个forms.py用来创建UserForm类,用来进行表单认证:
from django import forms
from django.forms import Form
from app.account.models import MyUser
from captcha.fields import CaptchaField
class UserForm(Form):
username = forms.CharField(
widget=forms.TextInput
)
password = forms.CharField(
widget=forms.PasswordInput
)
cp = forms.CharField(
widget=forms.PasswordInput,
required=False
)
type = forms.CharField(
widget=forms.TextInput
)
captcha = CaptchaField()
def clean(self):
username = self.cleaned_data.get("username", '')
if self.cleaned_data.get("type", '') == 'register':
if not username:
raise forms.ValidationError("用户名不能为空")
else:
user = MyUser.objects.filter(username=username).exists()
if user:
raise forms.ValidationError("用户名已存在")
p = self.cleaned_data.get("password", '')
cp = self.cleaned_data.get("cp", '')
if p != cp:
raise forms.ValidationError("两次密码不一致")
else:
d = {
'username': username,
'password': p,
'is_superuser': 1,
'is_staff': 1
}
user = MyUser.objects.create_user(**d)# create_user自动将明文密码hash加密
user.save()
else:
if not username:
raise forms.ValidationError("用户名不能为空")
在account的view.py添加如下代码
from django.shortcuts import render, redirect, reverse
from django.views.generic import View
from .forms import *
from django.contrib.auth import authenticate, login
from captcha.models import CaptchaStore
from captcha.helpers import captcha_image_url
class Register(View):
info = {
'title': '注册博客',
'pageTitle': '用户注册',
"confirmPassword": True,
'button': '注册',
"urlText": '用户登录',
"submitUrl": 'register',
"urlName": 'userLogin',
"tips": '',
'form': '',
}
def get(self, request):
self.info['form'] = UserForm()
return render(request, 'user.html', self.info)
def post(self, request):
user_from = UserForm(request.POST)
if user_from.is_valid():
self.info["tips"] = '注册成功,请登录'
return redirect(reverse("register"), self.info)
else:
self.info["tips"] = user_from.errors.as_data().values()
return render(request, 'user.html', self.info)
class UserLogin(View):
info = {
'title': '登录博客',
'pageTitle': '用户登录',
'button': '登录',
'urlText': '用户注册',
'urlName': 'register',
'submitUrl': 'userLogin',
'tips': '',
'form': '',
'new_key': '',
'image_url': ''
}
def get(self, request):
self.info['new_key'] = CaptchaStore.pick() # 生成hashkey
self.info['image_url'] = captcha_image_url(self.info['new_key']) # 生成验证码图片url地址
self.info['form'] = UserForm()
return render(request, 'user.html', self.info)
def post(self, request):
user_from = UserForm(request.POST)
# 表单验证
if user_from.is_valid():
username = user_from.cleaned_data.get("username", '')
password = user_from.cleaned_data.get("password", '')
if not username or not password:
self.info['tips'] = "用户名或密码不能为空"
return render(request, 'user.html', self.info)
else:
if MyUser.objects.filter(username=username).exists():
user = authenticate(username=username, password=password)
if user:
if user.is_active:
'''
'''
login(request, user)
return redirect(reverse("index"), self.info)
else:
self.info['tips'] = '密码错误'
return render(request, 'user.html', self.info)
else:
self.info['tips'] = '用戶不存在'
return render(request, 'user.html', self.info)
else:
self.info["tips"] = user_from.errors.as_data().values()
return render(request, 'user.html', locals())
四、jinja2模板引擎
更换django自带的模板引擎,采用jinja2模板引擎,在Myblog的setting中,在TEMPLATES配置中设置jinja2,用Myblog创建jinja2.py添加jinja2环境
# Myblog的setting中
TEMPLATES = [
{
'BACKEND': 'django.template.backends.jinja2.Jinja2',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'APP_DIRS': True,
'OPTIONS': {
'environment': 'Myblog.jinja2.environment',
},
},
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'APP_DIRS': True,
'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',
],
},
},
]
# jinja2.py
from django.contrib.staticfiles.storage import staticfiles_storage
from django.urls import reverse
from jinja2 import Environment
# 将jinja2 模板设置到项目环境中
def environment(**options):
env = Environment(**options)
env.globals.update({
'static': staticfiles_storage.url,
'url': reverse,
})
return env
五、模板编写
jinja2模板引擎编写,跟django自带的模板用法类似
<!DOCTYPE html>
<html lang="en">
<head>
<title>{
{ title }}</title>
<link rel="stylesheet" href="{
{static('css/reset.css')}}">
<link rel="stylesheet" href="{
{static('css/user.css')}}">
<script src="{
{static('js/jquery.min.js')}}"></script>
<script src="{
{static('js/user.js')}}"></script>
</head>
<body>
<div class="page">
<div class="loginwarrp">
<div class="logo">{
{ pageTitle }}</div>
<div class="login_form">
<form name="Login" method="post" action="{
{ url(submitUrl) }}">
{# 解决跨越访问#}
<input type="hidden" name="csrfmiddlewaretoken" value="{
{ csrf_token }}">
<input type="hidden" name="type" value="{
{ submitUrl }}">
<li class="login-item">
<span>用户名:</span>
<input type="text" name="username" class="login_input" >
<span id="count-msg" class="error"></span>
</li>
<li class="login-item">
<span>密 码:</span>
<input type="password" name="password" class="login_input">
<span id="password-msg" class="error"></span>
</li>
{% if confirmPassword %}
<li class="login-item">
<span>确认密码:</span>
<input type="password" name="cp" class="login_input">
<span id="password-msg" class="error"></span>
</li>
{% endif %}
<li class="login-item">
<span>验证码:</span>
<input type="text" class='login_input' id="id_captcha_1" name="captcha_1" placeholder='验证码' placeholder-data="验证码" />
<input type="hidden" id="id_captcha_0" name="captcha_0" value="{
{ new_key }}"> <img src="{
{ image_url }}" alt="captcha" class="captcha"> <br>
<span id="password-msg" class="error"></span>
</li>
<div>{
{ tips }}</div>
<li class="login-sub">
<input type="submit" name="Submit" value="{
{ button }}">
<div class="turn-url">
<a style="color: #45B572;" href="{
{ url(urlName) }}">>>{
{ urlText }}</a>
</div>
</li>
</form>
</div>
</div>
</div>
<script type="text/javascript">
window.onload = function() {
var config = {
vx : 4,
vy : 4,
height : 2,
width : 2,
count : 100,
color : "121, 162, 185",
stroke : "100, 200, 180",
dist : 6000,
e_dist : 20000,
max_conn : 10
}
CanvasParticle(config);
}
</script>
<script>
//点击验证码刷新
$(function () {
$('.captcha').click(function () {
console.log("click");
$.getJSON("/captcha/refresh", function (result) {
$(".captcha").attr('src', result['image_url']);
$('#id_captcha_0').val(result['key'])
});});
});
</script>
<script src="{
{ static('js/canvas-particle.js') }}"></script>
</body>
</html>
六、验证码
七、效果图