django用户自定义模型
新的模型拓展关联User方法
优点:
1)使用方便
2)不用删库重来影响整体构架
缺点:
1)存在不必要的字段
2)对比继承的方法,查询速度稍稍慢一丁点
\blog\users\models.py
class User(AbstractUser):
# 昵称
nickname = models.CharField(max_length=20, default='', verbose_name='昵称')
fieldsets里面内容可以在 "...\Lib\site-packages\django\contrib\auth\admin.py"里面找到
\blog\users\admin.py
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.utils.translation import gettext_lazy as _
# Register your models here.
# 用户自定义模型
User = get_user_model()
class UserAdmin(admin.ModelAdmin):
fieldsets = (
(None, {'fields': ('username', 'password')}),
(_('Personal info'), {'fields': ('nickname', 'email', 'mobile', 'avatar')}),
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser',
'groups', 'user_permissions')}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
)
list_display = ('username', 'nickname', 'mobile', 'email', 'is_staff', 'is_active', 'is_superuser')
admin.site.register(User, UserAdmin)
如下图
再nav.html设置只有管理员看到后台管理
\blog\templates\include\nav.html
{#超级管理员才显示#}
{% if user.is_staff or user.is_superuser %}
<a class="nav-link" href="{% url 'admin:index' %}" tabindex="-1" aria-disabled="true" style="color: red;font-weight:bold">后台管理</a>
{% endif %}
\blog\users\urls.py
# 修改昵称
path('change_nickname', views.change_nickname, name='change_nickname'),
def change_nickname(request):
redirect_to = request.GET.get('from', reverse('home:index'))
if request.method == 'POST':
form = ChangeNicknameForm(request.POST, user=request.user)
if form.is_valid():
nickname_new = form.cleaned_data['nickname_new']
username, created = User.objects.get_or_create(mobile=request.user)
# print(username, created)
username.nickname = nickname_new
username.save()
redirect_to = request.GET.get('from', reverse('home:index'))
return redirect(redirect_to)
else:
form = ChangeNicknameForm()
context = {
'page_title': '修改昵称',
'form_title': '修改昵称',
'submit_text': '修改',
'form': form,
'return_back_url': redirect_to
}
return render(request, 'form.html', context)
model.py判断用户是否有昵称如果有就显示昵称,没有就显示手机号,在评论回复可以看到,例如在detailhtml加入get_nickname,如下图
\blog\templates\detail.html
<span><strongid="replay_user_{{ comment.id }}">{{ comment.user.get_nickname }}</strong></span>
<span><strongid="replay_user_{{ reply.id }}">{{ reply.user.get_nickname }}</strong></span>
<span><strongid="replay_user_{{ reply.id }}">{{ reply.reply_to.get_nickname }}</strong></span>
\blog\users\models.py
class User(AbstractUser):
def get_nickname(self):
# 判断nikename是否存在
if User.objects.filter(mobile=self).exists():
username = User.objects.get(mobile=self)
if username.nickname:
return username.nickname
else:
return self.mobile
views.py也要改成get_nickname()要加括号,回复时候显示的,无需刷新页面情况,如下面图
\blog\home\views.py
class DetailView(View):
def post(self, request):
# 返回评论数据.get_nickname()是users:model里面的如果有昵称返回昵称,没有就返回手机号,是评论不刷新页面出现
data['user'] = comment.user.get_nickname()
# 返回回复数据
if parent:
data['reply_to'] = comment.reply_to.get_nickname()
通用的form.html模板
\blog\templates\form.html
{% extends 'include/base.html' %}
{% load static %}
{% block title %}
<title>{{ page_title }}</title>
{% endblock %}
{% block content %}
<!--content-->
<div class="container" style="height: 600px;margin-top: 20px">
<div class="row">
<div class="col-lg-6 col-xl-6 hidden-xs hidden-sm">
<div class="card">
<div class="card-header">{{ form_title }}</div>
<div class="card-body">
<form action="" method="POST">
{% csrf_token %}
{# {{ login_form }}#}
{% 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 %}
<spen id="tip" class="pull-left text-danger">{{ form.non_field_errors }}</spen>
<div>
{% block other_button %}{% endblock %}
</div>
<div class="modal-footer">
<input type="submit" value="{{ submit_text }}" class="btn btn-primary">
<button class="btn btn-secondary"
onclick="window.location.href='{{ return_back_url }}'">
返回
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
这个User是User = get_user_model()不然ERROR会报错
\blog\users\forms.py
# 修改用户昵称
class ChangeNicknameForm(forms.Form):
nickname_new = forms.CharField(
label="新的昵称",
required=False,
max_length=20,
widget=forms.TimeInput(attrs={'class': 'form-control', 'placeholder': '请输入新的昵称'})
)
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
\blog\templates\center.html
<ul>
<li>昵称: {{ user.nickname }} <a
href="{% url 'users:change_nickname' %}?from={{ request.get_full_path }}">修改昵称</a></li>
<li>邮箱:{% if user.email %} {{ user.email }} {% else %}未绑定
<a href="{% url 'users:bind_email' %}?from={{ request.get_full_path }}">绑定邮箱</a>{% endif %}
</li>
<li>上一次登录时间:{{ user.last_login | date:'Y-m-d H:i:s' }}</li>
<li><a href="#">修改密码</a></li>
</ul>
\blog\users\urls.py
# 绑定邮箱
path('bind_email', views.bind_email, name='bind_email'),
# 发送验证码
path('send_verification_code', views.send_verification_code, name='send_verification_code'),
\blog\settings.py
# 发送邮件设置
# https://docs.djangoproject.com/en/3.0/ref/settings/#email
# https://docs.djangoproject.com/en/3.0/topics/email/
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.qq.com'
EMAIL_PORT = 25
EMAIL_HOST_USER = '534640040@qq.com'
EMAIL_HOST_PASSWORD = '' # 授权码在qq邮箱可以得到
EMAIL_SUBJECT_PREFIX = '[杨永生的网站] '
EMAIL_USE_TLS = True # 与SMTP服务器通信时,是否启动TLS链接(安全链接)
\blog\users\views.py
def bind_email(request):
redirect_to = request.GET.get('from', reverse('home:index'))
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 = {
'page_title': '绑定邮箱',
'form_title': '绑定邮箱',
'submit_text': '绑定',
'form': form,
'return_back_url': redirect_to
}
return render(request, 'bind_email.html', context)
def send_verification_code(request):
email = request.GET.get('email', '')
data = {}
if email != '':
# 生成验证码
# 发送邮件
code = ''.join(random.sample(string.ascii_letters + string.digits, 4))
now = int(time.time())
send_code_time = request.session.get('send_code_time', 0)
if now - send_code_time < 30:
data['status'] = 'ERROR'
else:
request.session['bind_email_code'] = code
request.session['send_code_time'] = now
send_mail(
'绑定邮箱',
'验证码: %s,温馨提示:区分大小写!' % code,
'534640040@qq.com', # 发送者邮箱
[email], # 收件人的邮箱
fail_silently=False,
)
data['status'] = 'SUCCESS'
else:
data['status'] = 'ERROR'
return JsonResponse(data)
\blog\users\forms.pyclass BindEmailForm(forms.Form):
email = forms.EmailField(
label="邮箱",
widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': '请输入正确的邮箱'})
)
verification_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 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', '')
verification_code = self.cleaned_data.get('verification_code', '')
if not (code != '' and code == verification_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):
verification_code = self.cleaned_data.get('verification_code', '').strip()
if verification_code == '':
raise forms.ValidationError('验证码不能为空')
return verification_code
绑定邮箱html继承form通用模板,多个发送验证码按钮,所以单独写个
{#继承form.html#}
{% extends 'form.html' %}
{% block other_button %}
<button id="send_code" class="btn btn-primary">发送验证码</button>
{% endblock %}
{% block bottomjs %}
<script type="text/javascript">
$("#send_code").click(function () {
var email = $("#id_email").val();
if (email == "") {
$("#tip").text('邮箱不能为空');
return false;
}
//发送验证码
$.ajax({
url: "{% url 'users:send_verification_code' %}",
type: "GET",
data: {
'email': email
},
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);
});
</script>
{% endblock %}
\blog\templates\center.html
<!-- 提交按钮 -->
<ul>
<li>昵称: {{ user.nickname }} <a
href="{% url 'users:change_nickname' %}?from={{ request.get_full_path }}">修改昵称</a></li>
<li>邮箱:{% if user.email %} {{ user.email }} {% else %}未绑定
<a href="{% url 'users:bind_email' %}?from={{ request.get_full_path }}">绑定邮箱</a>{% endif %}
</li>
<li>上一次登录时间:{{ user.last_login | date:'Y-m-d H:i:s' }}</li>
<li><a href="#">修改密码</a></li>
</ul>
注意事项:腾讯云不能发送邮箱验证码解决办法
点击“25端口解封”
\python\blog\users\urls.py
# 注册
path('register/', RegisterView.as_view(), name='register'),
\blog\users\views.py
class RegisterView(View):
def get(self, request):
reg_form = RegisterForm
context = {
'reg_form': reg_form
}
return render(request, 'register.html', context)
def post(self, request):
if request.method == 'POST':
# 1.接收记住登录参数
reg_form = RegisterForm(request.POST, request=request)
if reg_form.is_valid():
mobile = reg_form.cleaned_data["mobile"]
email = reg_form.cleaned_data["email"]
password = reg_form.cleaned_data['password']
# 创建用户
# print(mobile, email, password)
user = User.objects.create_user(username=mobile, mobile=mobile, email=email, password=password)
user.save()
# 清除session
del request.session['register_code']
# # 登录用户
user = auth.authenticate(mobile=mobile, password=password)
auth.login(request, user)
response = redirect(reverse('home:index'))
return response
else:
reg_form = RegisterForm()
pass
context = {
'reg_form': reg_form
}
return render(request, 'register.html', context)
\blog\templates\register.html
{% extends 'include/base.html' %}
{% load static %}
{% block title %}
<title>注册</title>
{% endblock %}
{% block content %}
<!--content-->
<div class="container" style="height: 600px;margin-top: 20px">
<div class="row">
<div class="col-lg-6 col-xl-6 hidden-xs hidden-sm">
<div class="card">
<div class="card-header">注册</div>
<div class="card-body">
<form action="" method="POST">
{% csrf_token %}
{# {{ login_form }}#}
{% for field in reg_form %}
<!--去掉冒号-->
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
<!--显示错误标签-->
<p class="text-danger">{{ field.errors.as_text }}</p>
{% endfor %}
<spen class="pull-left text-danger">{{ reg_form.non_field_errors }}</spen>
<button id="send_code" class="btn btn-primary ">发送验证码</button>
<button class="primaryAction btn btn-primary " style="float:right" type="submit"
id="submit_login" @click="on_submit">提交
</button>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block bottomjs %}
<script type="text/javascript">
$("#send_code").click(function () {
var email = $("#id_email").val();
if (email == "") {
$("#tip").text('*邮箱不能为空');
return false;
}
//发送验证码
$.ajax({
url: "{% url 'users:send_verification_code' %}",
type: "GET",
data: {
'email': email,
{#register_code传入form#}
'send_for': 'register_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);
});
</script>
{% endblock %}
\blog\templates\include\base.html
\blog\templates\login.html
<small class="form-text text-muted ml-1">还没有账号?<a href="{% url 'users:register' %}"
style="color: cornflowerblue; ">注册新账号</a>
</small>
\blog\users\forms.py
class RegisterForm(forms.Form):
mobile = forms.CharField(label="用户名", max_length=11, min_length=3, widget=forms.TextInput(
attrs={'class': 'form-control', 'placeholder': '请输入手机号'}))
email = forms.EmailField(label='邮箱',
widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': '请输入邮箱'}))
verification_code = forms.CharField(
label='验证码',
required=False,
widget=forms.TextInput(
attrs={'class': 'form-control', 'placeholder': '点击“发送验证码”发送到邮箱'}
)
)
password = forms.CharField(label="密码", min_length=6, widget=forms.PasswordInput(
attrs={'class': 'form-control', 'placeholder': '请输入密码'}))
password_again = forms.CharField(label="再输入一次密码", min_length=6, widget=forms.PasswordInput(
attrs={'class': 'form-control', 'placeholder': '再输入一次密码'}))
# request是获取session
def __init__(self, *args, **kwargs):
if 'request' in kwargs:
self.request = kwargs.pop('request')
super(RegisterForm, self).__init__(*args, **kwargs)
def clean(self):
# 判断验证码register_code对应js里面的send_for
code = self.request.session.get('register_code', '')
verification_code = self.cleaned_data.get('verification_code', '')
if not (code != '' and code == verification_code):
raise forms.ValidationError('验证码不正确')
return self.cleaned_data
def clean_username(self):
mobile = self.cleaned_data['mobile']
if User.objects.filter(mobile=mobile).exists():
raise forms.ValidationError('用户名已存在')
return mobile
def clean_email(self):
email = self.cleaned_data['email']
if User.objects.filter(email=email).exists():
raise forms.ValidationError('邮箱已存在')
return email
def clean_password_again(self):
password = self.cleaned_data['password']
password_again = self.cleaned_data['password_again']
if password != password_again:
raise forms.ValidationError("两次密码不一致")
return password
def clean_verification_code(self):
verification_code = self.cleaned_data.get('verification_code', '').strip()
if verification_code == '':
raise forms.ValidationError('验证码不能为空')
return verification_code
\blog\users\urls.py
# 修改密码
path('change_password', views.change_password, name='change_password'),
def change_password(request):
# 和修改昵称类似,redirect_to跳转到登录页面
redirect_to = reverse('users:login')
if request.method == 'POST':
form = ChangePasswordForm(request.POST, user=request.user)
if form.is_valid():
user = request.user
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 = {
'page_title': '修改密码',
'form_title': '修改密码',
'submit_text': '修改',
'form': form,
'return_back_url': redirect_to
}
return render(request, 'form.html', context)
html继承的form.html
\blog\users\forms.py
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['new_password']
new_password_again = self.cleaned_data['new_password_again']
if new_password != new_password_again or new_password == '':
return 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
\blog\templates\include\nav.html
<a class="dropdown-item" href="{% url 'users:change_password' %}">修改密码</a>
\blog\templates\center.html
<li><a href="{% url 'users:change_password' %}">修改密码</a></li>
\blog\users\urls.py
# 忘记密码
path('forgot_password', views.forgot_password, name='forgot_password'),
\blog\users\views.py
def forgot_password(request):
redirect_to = reverse('users: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 = {
'page_title': '重置密码',
'form_title': '重置密码',
'submit_text': '重置',
'form': form,
'return_back_url': redirect_to
}
return render(request, 'forgot_password.html', context)
\blog\users\forms.py
class ForgotPasswordForm(forms.Form):
email = forms.EmailField(
label="邮箱",
widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': '请输入忘记密码的邮箱'})
)
verification_code = forms.CharField(
label='验证码',
required=False,
widget=forms.TextInput(
attrs={'class': 'form-control', 'placeholder': '点击“发送验证码”发送到邮箱'}
)
)
new_password = forms.CharField(label="新的密码", min_length=6, widget=forms.PasswordInput(
attrs={'class': 'form-control', 'placeholder': '请输入新的密码'}))
def clean_email(self):
email = self.cleaned_data["email"].strip()
if not User.objects.filter(email=email).exists():
return forms.ValidationError("你输入的邮箱不存在")
return email
def __init__(self, *args, **kwargs):
if 'request' in kwargs:
self.request = kwargs.pop('request')
super(ForgotPasswordForm, self).__init__(*args, **kwargs)
def clean_verification_code(self):
# 判断验证码是否为空
verification_code = self.cleaned_data.get('verification_code', '').strip()
if verification_code == '':
raise forms.ValidationError('验证码不能为空')
# 判断验证码register_code对应js里面的send_for
code = self.request.session.get('forgot_password_code', '')
verification_code = self.cleaned_data.get('verification_code', '')
if not (code != '' and code == verification_code):
raise forms.ValidationError('验证码不正确')
return verification_code
\blog\templates\forgot_password.html
{#继承form.html#}
{% extends 'form.html' %}
{% block other_button %}
<button id="send_code" class="btn btn-primary">发送验证码</button>
{% endblock %}
{% block bottomjs %}
<script type="text/javascript">
$("#send_code").click(function () {
var email = $("#id_email").val();
if (email == "") {
$("#tip").text('*邮箱不能为空');
return false;
}
//发送验证码
$.ajax({
url: "{% url 'users:send_verification_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);
});
</script>
{% endblock %}
\blog\templates\login.html
\blog\templates\include\base.html
<small class="form-text text-muted ml-1"><a class="secondaryAction layui-text"
href="{% url 'users:forgot_password' %}">忘记密码?</a>
</small>
\blog\home\views.py
# 发送邮件
comment.sen_email(request)
\blog\home\models.py
from django.core.mail import send_mail
from django.conf import settings
from django.template.loader import render_to_string
# 多线发邮件
class SendMail(threading.Thread):
def __init__(self, subject, text, email, fail_silently=False):
self.subject = subject
self.text = text
self.email = email
self.fail_silently = fail_silently
threading.Thread.__init__(self)
def run(self):
send_mail(self.subject,
self.text,
settings.EMAIL_HOST_USER,
[self.email],
fail_silently=self.fail_silently,
html_message=self.text
)
class Article(models.Model):
def get_url(self, request):
"""获得网站url绝对路径"""
return request.build_absolute_uri()
def get_email(self):
return self.author.email
class Comment(models.Model):
def sen_email(self, request):
if self.parent is None:
# 评论
subject = "有人评论我的文章"
email = self.article.get_email()
else:
# 回复
subject = "有人回复你的评论"
email = self.reply_to.email
print(self.article.get_url(request))
if email:
context = {
'comment_text': self.content,
"url": self.article.get_url(request)
}
# 发送给html模板
text = render_to_string('send_email.html', context)
send_mail = SendMail(subject, text, email)
send_mail.start()
\templates\send_email.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ comment_text|safe }}
<br>
<a href="{{ url }}">点击查看</a>
</body>
</html>
评论列表 (0 条评论)