1、修改用户昵称
创建一个form.html页面,如下
{%extends 'base.html'%}
{%block title%}
{{page_title}}
{%endblock%}
{%block nav_home_active%}
active
{%endblock%}
{%block content%}
<div class="cotainer">
<div class="row">
<div class="col-xs-4 col-xs-offset-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{{form_title}}</h3>
</div>
<div class="panel-body">
<form action="#" method="POST">
{%csrf_token%}
{%for field in form%}
{%if not field.is_hidden%}
<label for="{{field.id_for_label}}">{{field.label}}</label>
{%endif%}
{{field}}
<p class="text-danger">{{field.errors.as_text}}</p>
{%endfor%}
<span id="tip" class="pull-left text-danger">{{form.non_field_errors}}</span>
<div class="clearfix"></div>
<div class="pull-left">
{%block other_buttons%}{%endblock%}
</div>
<div class="pull-right">
<input type="submit" value="{{submit_text}}" class="btn btn-primary ">
<button class="btn btn-default" onclick="window.location.href='{{return_back_url}}'">返回</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
修改user下对应的表单
class ChangeNicknameForm(forms.Form):
nickname_new = forms.CharField(label='昵称',
max_length=20,
min_length=3,
widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': '请输入3至20位昵称'}))
def __init__(self, *args, **kwargs):
if 'user' in kwargs:
self.user = kwargs.pop('user')
super(ChangeNicknameForm, self).__init__(*args, **kwargs)
def clean(self):
if self.user.is_authenticated:
self.cleaned_data['user'] = self.user
else:
raise forms.ValidationError('用户未登录')
return self.cleaned_data
def clean_nickname_new(self):
nickname_new = self.cleaned_data.get('nickname_new', '').strip()
if nickname_new == '':
raise forms.ValidationError('新昵称不能为空')
return nickname_new
对应views下的方法,传回所需参数
def change_nickname(request):
redirect_to = request.GET.get('from', reverse('home'))
if request.method == 'POST':
form = ChangeNicknameForm(request.POST, user=request.user)
if form.is_valid():
nickname_new = form.cleaned_data['nickname_new']
profile, created = Profile.objects.get_or_create(user=request.user)
profile.nickname = nickname_new
profile.save()
return redirect(redirect_to)
else:
form = ChangeNicknameForm()
context = {}
context['page_title'] = '修改昵称'
context['form_title'] = '修改昵称'
context['submit_text'] = '修改'
context['form'] = form
context['return_back_url'] = redirect_to
return render(request, 'form.html', context)
把博客页面对应位置改为昵称,没有昵称显示用户名
这里采用动态绑定方法
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
nickname = models.CharField(max_length=20, verbose_name='昵称')
def __str__(self):
return '<Profile: %s for %s>' % (self.nickname, self.user.username)
def get_nickname(self):
if Profile.objects.filter(user=self).exists():
profile = Profile.objects.get(user=self)
return profile.nickname
else:
return ''
def get_nickname_or_username(self):
if Profile.objects.filter(user=self).exists():
profile = Profile.objects.get(user=self)
return profile.nickname
else:
return self.username
def has_nickname(self):
return Profile.objects.filter(user=self).exists()
User.get_nickname = get_nickname
User.has_nickname = has_nickname
User.get_nickname_or_username=get_nickname_or_username
路由:
path('change_nickname/',views.change_nickname,name='change_nickname'),
对应blogdetail页面
{% extends 'base.html' %}
{#页面标题#}
{% block title %}
<h3>{{blog.title}}</h3>
{% endblock %}
{% load static %}
{%load comment_tags%}
{%block head_extend%}
<link rel="stylesheet" href="/static/blog/blog.css">
<script type="text/javascript" src="{% static 'ckeditor/ckeditor-init.js' %}"></script>
<script type="text/javascript" src="{% static 'ckeditor/ckeditor/ckeditor.js' %}"></script>
<script src="{% static 'ckeditor/ckeditor/plugins/prism/lib/prism/prism_patched.min.js' %}"></script>
<link rel="stylesheet" href="{% static 'prism/prism.css' %}">
{% endblock %}
{%block nav_blog_active%}
active
{%endblock%}
{%block content%}
<div class="container">
<div class="row">
<div class="col-xs-10 col-xs-offset-1">
<div class="panel panel-default">
<div class="panel-body">
<h3>{{blog.title}}</h3>
<ul class="blog_info_description">
<li>作者:{{blog.author}}</li>
<li>
分类:<a href="{%url 'blogs_with_type' blog.blog_type.pk%}">
{{blog.blog_type}}</a>
</li>
<li>发表日期:{{blog.created_time|date:"Y-m-d H:i:s"}}</li>
<li>阅读:({{blog.get_read_num}})</li>
<li>评论({%get_comment_count blog%})</li>
</ul>
<hr>
<div class="blog_content">{{blog.content|safe}}</div>
<hr>
<div class="blog_more">
<p>上一篇
{%if previous_blog %}
<a href="{% url 'blog_detail' previous_blog.pk %}">{{previous_blog.title}}</a>
{%else%}
没有了
{%endif%}
</p>
<p>下一篇
{%if next_blog %}
<a href="{% url 'blog_detail' next_blog.pk %}">{{next_blog.title}}</a>
{%else%}
没有了
{%endif%}
</p>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-10 col-xs-offset-1">
<div class="panel panel-default">
<div class="panel-body">
<div class="comment-area">
<h3 class="comment-area-title">提交评论区域</h3>
{%if user.is_authenticated %}
<form id="comment_form" action="{%url 'update_comment' %}" method="POST" style="overflow:hidden">
{%csrf_token%}
<label for="comment-text">{{user.get_nickname_or_username}},欢迎评论</label>
<div id="reply_content_container" style="display:none;">
<p id="reply_title">回复:</p>
<div id="reply_content"></div>
</div>
{%get_comment_form blog%}
<span id="comment_error" class="text-danger pull-left"></span>
<input type="submit" class="btn btn-primary pull-right" value="评论" >
</form>
{%else%}
您尚未登录登录后方可评论
<a class="btn btn-primary" href="{%url 'login'%}?from={{ request.get_full_path }}">登录</a>
<span>or</span>
<a class="btn btn-danger" href="{%url 'register'%}?from={{ request.get_full_path }}">注册</a>
{%endif%}
</div>
<div class="comment-area2">
<h3 class="comment-area-title">评论列表</h3>
<div id="comment_list">
{%get_comment_list blog as comments%}
{% for comment in comments %}
<div id="root_{{ comment.pk }}" class="comment">
<span>{{ comment.user.get_nickname_or_username}}</span>
<span>({{ comment.comment_time|date:"Y-m-d H:i:s" }}):</span>
<div id="comment_{{ comment.pk }}">
{{ comment.text|safe }}
</div>
<a href="javascript:reply({{ comment.pk }});">回复</a>
{% for reply in comment.root_comment.all %}
<div class="reply">
<span>{{ reply.user.get_nickname_or_username }}</span>
<span>({{ reply.comment_time|date:"Y-m-d H:i:s" }})</span>
<span>回复</span>
<span>{{ reply.reply_to.get_nickname_or_username }}:</span>
<div id="comment_{{ reply.pk }}">
{{ reply.text|safe }}
</div>
<a href="javascript:reply({{ reply.pk }});">回复</a>
</div>
{% endfor %}
</div>
{% empty %}
<span id="no_comment">暂无评论</span>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
div>
{% endblock %}
{%block script_extends %}
<script type="text/javascript">
function reply(reply_comment_id)
{
$('#reply_comment_id').val(reply_comment_id);
var html=$("#comment_"+reply_comment_id).html();
$('#reply_content').html(html);
$('#reply_content_container').show();
$('html').animate({scrollTop: $('#comment_form').offset().top - 60}, 300, function(){
CKEDITOR.instances['id_text'].focus();
});
}
function numFormat(num){
return ('00' + num).substr(-2);
}
function timeFormat(timestamp){
var datetime = new Date(timestamp * 1000);
var year = datetime.getFullYear();
var month = numFormat(datetime.getMonth() + 1);
var day = numFormat(datetime.getDate());
var hour = numFormat(datetime.getHours());
var minute = numFormat(datetime.getMinutes());
var second = numFormat(datetime.getSeconds());
return year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second;
}
$("#comment_form").submit(function(){
// 判断是否为空
$("#comment_error").text('');
if(CKEDITOR.instances["id_text"].document.getBody().getText().trim()==''){
$("#comment_error").text('评论内容不能为空');
return false;
}
// 更新数据到textarea
CKEDITOR.instances['id_text'].updateElement();
// 异步提交
$.ajax({
url: "{% url 'update_comment' %}",
type: 'POST',
data: $(this).serialize(),
cache: false,
success: function(data){
console.log(data);
if(data['status']=="SUCCESS"){
if($('#reply_comment_id').val()=='0')
{
var comment_html = '<div id="root_' + data['pk'] + '" class="comment"><span>' + data['username'] + '</span><span> (' + timeFormat(data['comment_time']) + '):</span><div id="comment_' + data['pk'] + '">' + data['text'] + '</div><a href="javascript:reply(' + data['pk'] + ');">回复</a></div>';
$("#comment_list").prepend(comment_html);
}
else
{ var reply_html ='<div class="reply"><span>' + data['username'] + '</span><span> (' + timeFormat(data['comment_time']) + ')</span><span> 回复 </span><span>' + data['reply_to'] + ':</span><div id="comment_' + data['pk'] + '">' + data['text'] + '</div><a href="javascript:reply(' + data['pk'] + ');">回复</a></div>';
$("#root_" + data['root_pk']).append(reply_html);
}
CKEDITOR.instances['id_text'].setData('');
$('#reply_content_container').hide();
$('reply_comment_id').val('0');
$('#no_comment').remove();
$('#comment_error').text('评论成功');
}else{
// 显示错误信息
$("#comment_error").text(data['message']);
}
},
error: function(xhr){
console.log(xhr);
}
});
return false;
});
</script>
{%endblock%}
效果显示:
2、绑定邮箱
对应form表单
class BindEmailForm(forms.Form):
email = forms.EmailField(label='邮箱',
widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': '请输入正确邮箱'}))
verify_code = forms.CharField(label='验证码',
required=False,
widget=forms.TextInput(
attrs={'class': 'form-control', 'placeholder': '点击"发送验证码"发送到邮箱'}))
def __init__(self, *args, **kwargs):
if 'request' in kwargs:
self.request = kwargs.pop('request')
super(BindEmailForm, self).__init__(*args, **kwargs)
def __init__(self, *args, **kwargs):
if 'request' in kwargs:
self.request = kwargs.pop('request')
super(BindEmailForm, self).__init__(*args, **kwargs)
def clean(self):
# 判断用户是否登录
if self.request.user.is_authenticated:
self.cleaned_data['user'] = self.request.user
else:
raise forms.ValidationError('用户尚未登录')
# 判断用户是否已绑定邮箱
if self.request.user.email != '':
raise forms.ValidationError('你已经绑定邮箱')
# 判断验证码
code = self.request.session.get('bind_email_code', '')
verify_code = self.cleaned_data.get('verify_code', '')
print(code,verify_code)
if not (code != '' and code == verify_code):
raise forms.ValidationError('验证码不正确')
return self.cleaned_data
def clean_email(self):
email = self.cleaned_data['email']
if User.objects.filter(email=email).exists():
raise forms.ValidationError('该邮箱已经被绑定')
return email
def clean_verification_code(self):
verify_code = self.cleaned_data.get('verify_code', '').strip()
if verify_code == '':
raise forms.ValidationError('验证码不能为空')
return verify_code
对应views 发送验证码方法,其中将生成验证码放在session中和输入的对比
def send_verify_code(request):
email = request.GET.get('email', '')
data = {}
if email != '':
# 生成验证码
code = ''.join(random.sample(string.ascii_letters + string.digits, 4))
request.session['bind_email_code'] = code
# 发送邮件
send_mail(
'绑定邮箱',
'验证码:%s' % code,
'3374399586@qq.com',
[email],
fail_silently=False,
)
data['status'] = 'SUCCESS'
else:
data['status'] = 'ERROR'
return JsonResponse(data)
settings邮箱对应配置
# https://docs.djangoproject.com/en/2.0/ref/settings/#email
# https://docs.djangoproject.com/en/2.0/topics/email/
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.qq.com'
EMAIL_PORT = 25
EMAIL_HOST_USER = '3374399586@qq.com'
EMAIL_HOST_PASSWORD = 'akndabykjedrdacf' # 授权码
EMAIL_SUBJECT_PREFIX = '[管伟鸿的博客] '
EMAIL_USE_TLS = True # 与SMTP服务器通信时,是否启动TLS链接(安全链接)
邮箱异步提交方法,这里新建了一个html页面,继承了forms.html
{%extends 'form.html' %}
{%block other_buttons %}
<button id="send_code" class="btn btn-primary">发送验证码</button>
{%endblock%}
{%block script_extends %}
<script type="text/javascript">
$("#send_code").click(function(){
var email = $("#id_email").val();
if(email==""){
$("#tip").text('* 邮箱不能为空');
return false;
}
// 发送验证码
$.ajax({
url: "{% url 'send_verify_code' %}",
type: "GET",
data: {
'email': email
},
cache: false,
success: function(data){
if(data['status']=='ERROR'){
alert(data['status']);
}
}
});
return false;
});
</script>
{%endblock%}
绑定邮箱的方法,这里传入的是request
def bind_email(request):
redirect_to = request.GET.get('from', reverse('home'))
if request.method == 'POST':
form = BindEmailForm(request.POST, request=request)
if form.is_valid():
email=form.cleaned_data['email']
request.user.email=email
request.user.save()
return redirect(redirect_to)
else:
form = BindEmailForm()
context = {}
context['page_title'] = '绑定邮箱'
context['form_title'] = '绑定邮箱'
context['submit_text'] = '绑定'
context['form'] = form
context['return_back_url'] = redirect_to
return render(request, 'user/bind_email.html', context)
相应路由
效果:
使按钮变灰
在模板页面添加
$(this).addClass('disabled');
$(this).attr('disabled', true);
var time = 30;
$(this).text(time + 's');
var interval = setInterval(() => {
if(time <= 0){
clearInterval(interval);
$(this).removeClass('disabled');
$(this).attr('disabled', false);
$(this).text('发送验证码');
return false;
}
time --;
$(this).text(time + 's');
}, 1000);
return false;
});
在后台进行对应验证
def send_verify_code(request):
email = request.GET.get('email', '')
data = {}
if email != '':
# 生成验证码
code = ''.join(random.sample(string.ascii_letters + string.digits, 4))
request.session['bind_email_code'] = code
now=int(time.time())
send_code_time=request.session.get('send_code_time',0)
if now-send_code_time<30:
data['staus']='ERROR'
else: # 发送邮件
request.session['send_code_time'] = now
request.session['bind_email_code'] = code
send_mail(
'绑定邮箱',
'验证码:%s' % code,
'3374399586@qq.com',
[email],
fail_silently=False,
)
data['status'] = 'SUCCESS'
else:
data['status'] = 'ERROR'
return JsonResponse(data)
3、利用邮箱进行登录
修改forms的对应表单
class LoginForm(forms.Form):
username_or_email = forms.CharField(label='用户名或邮箱',
widget=forms.TextInput(
attrs={'class': 'form-control', 'placeholder': '请输入用户名或邮箱'}))
password = forms.CharField(label='密码',
widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': '请输入密码'}))
def clean(self):
username_or_email = self.cleaned_data['username_or_email']
password = self.cleaned_data['password']
user = auth.authenticate(username=username_or_email, password=password)
if user is None: # 登录
if User.objects.filter(email=username_or_email).exists():
username = User.objects.get(email=username_or_email).username
user = auth.authenticate(username=username, password=password)
if not user is None:
self.cleaned_data['user'] = user
return self.cleaned_data
raise (forms.ValidationError('用户名或密码错误'))
else:
self.cleaned_data['user'] = user
return self.cleaned_data
4、修改密码
对应的form
class ChangePasswordForm(forms.Form):
old_password = forms.CharField(label='旧的密码',
widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': '请输入旧密码'}))
new_password = forms.CharField(label='新的密码',
widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': '请输入新密码'}))
new_password_again = forms.CharField(label='新的密码',
widget=forms.PasswordInput(
attrs={'class': 'form-control', 'placeholder': '请再次输入新密码'}))
def __init__(self, *args, **kwargs):
if 'user' in kwargs:
self.user = kwargs.pop('user')
super(ChangePasswordForm, self).__init__(*args, **kwargs)
def clean(self):
new_password = self.cleaned_data.get('new_password', '')
new_password_again = self.cleaned_data.get('new_password_again', '')
if new_password != new_password_again or new_password == '':
raise forms.ValidationError('两次输入的密码不一致')
return self.cleaned_data
def clean_old_password(self):
old_password = self.cleaned_data.get('old_password', '')
if not self.user.check_password(old_password):
raise forms.ValidationError('旧的密码错误')
return old_password
def change_password(request):
redirect_to = reverse('home')
if request.method == 'POST':
form = ChangePasswordForm(request.POST, user=request.user)
if form.is_valid():
user = request.user
# old_password=form.cleaned_data['old_password']
new_password = form.cleaned_data['new_password']
user.set_password(new_password)
user.save()
auth.logout(request)
return redirect(redirect_to)
else:
form = ChangePasswordForm()
context = {}
context['page_title'] = '修改密码'
context['form_title'] = '修改密码'
context['submit_text'] = '修改'
context['form'] = form
context['return_back_url'] = redirect_to
return render(request, 'form.html', context)
对应路由
5、忘记密码,通过邮箱进行验证
对应的表单
class ForgotPasswordForm(forms.Form):
email = forms.EmailField(label='邮箱',
widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': '请输入正确邮箱'}))
verify_code = forms.CharField(label='验证码',
required=False,
widget=forms.TextInput(
attrs={'class': 'form-control', 'placeholder': '点击"发送验证码"发送到邮箱'}))
new_password = forms.CharField(label='新的密码',
widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': '请输入新密码'}))
def __init__(self, *args, **kwargs):
if 'request' in kwargs:
self.request = kwargs.pop('request')
super(ForgotPasswordForm, self).__init__(*args, **kwargs)
def clan_email(self):
email= self.cleaned_data['email'].strip()
if not User.objects.filter(email=email).exists():
raise forms.ValidationError('邮箱不存在')
return email
def clean_verify_code(self):
verify_code = self.cleaned_data.get('verify_code', '').strip()
if verify_code == '':
raise forms.ValidationError('验证码不能为空')
code = self.request.session.get('forgot_password_code', '')
verify_code = self.cleaned_data.get('verify_code', '')
if not (code != '' and code == verify_code):
raise forms.ValidationError('验证码不正确')
return verify_code
对应的views方法
def forgot_password(request):
redirect_to = reverse('login')
if request.method == 'POST':
form = ForgotPasswordForm(request.POST, request=request)
if form.is_valid():
email = form.cleaned_data['email']
new_password = form.cleaned_data['new_password']
user = User.objects.get(email=email)
user.set_password(new_password)
user.save()
# 清除session
del request.session['forgot_password_code']
return redirect(redirect_to)
else:
form = ForgotPasswordForm()
context = {}
context['page_title'] = '重置密码'
context['form_title'] = '重置密码'
context['submit_text'] = '重置'
context['form'] = form
context['return_back_url'] = redirect_to
return render(request, 'user/forgot_password.html', context)
对应路由
对应的页面
{%extends 'form.html' %}
{%block other_buttons %}
<button id="send_code" class="btn btn-primary">发送验证码</button>
{%endblock%}
{%block script_extends %}
<script type="text/javascript">
$("#send_code").click(function(){
var email = $("#id_email").val();
if(email==""){
$("#tip").text('* 邮箱不能为空');
return false;
}
// 发送验证码
$.ajax({
url: "{% url 'send_verify_code' %}",
type: "GET",
data: {
'email': email,
'send_for': 'forgot_password_code'
},
cache: false,
success: function(data){
if(data['status']=='ERROR'){
alert(data['status']);
}
}
});
$(this).addClass('disabled');
$(this).attr('disabled', true);
var time = 30;
$(this).text(time + 's');
var interval = setInterval(() => {
if(time <= 0){
clearInterval(interval);
$(this).removeClass('disabled');
$(this).attr('disabled', false);
$(this).text('发送验证码');
return false;
}
time --;
$(this).text(time + 's');
}, 1000);
return false;
});
</script>
{%endblock%}
入口在user_info中
同时可以加入下拉表中
效果