一、环境准备
1、配置文件
settings.py文件:
增加一项内容实现UserInfo表继承Django用户认证的表
AUTH_USER_MODEL="blog.UserInfo"
TEMPLATES = [
{
'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',
],
},
},
]
2、表关系设计
models.py文件内容:
from django.db import models
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
"""
用户信息
"""
nid = models.AutoField(primary_key=True)
telephone = models.CharField(max_length=11, null=True, unique=True)
avatar = models.FileField(upload_to='avatars/', default="/avatars/default.png")
create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
blog = models.OneToOneField(to='Blog', to_field='nid', null=True)
def __str__(self):
return self.username
class Blog(models.Model):
"""
博客(个人站点)信息
"""
nid = models.AutoField(primary_key=True)
title = models.CharField(verbose_name='个人博客标题', max_length=64)
site = models.CharField(verbose_name='个人博客后缀', max_length=32, unique=True)
theme = models.CharField(verbose_name='博客主题', max_length=32)
def __str__(self):
return self.title
3、数据库实例化
python3 manage.py makemigrations
python3 manage.py migrate
二、登录系统
urls.py文件内容:
from django.conf.urls import url
from django.contrib import admin
from blog import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^login/', views.login),
url(r'^valid_img/', views.valid_img),
url(r'^log_out/$', views.log_out),
url(r'^index/', views.index),
]
views.py文件内容:
from django.shortcuts import render,HttpResponse,redirect
import json
def login(reqeust):
if reqeust.is_ajax():
res={"user":None,"msg":None}
user=reqeust.POST.get("user")
pwd=reqeust.POST.get("pwd")
valid=reqeust.POST.get("valid")
print(reqeust.POST)
random_str=reqeust.session.get("random_str") #取出session保存的random_str对应的验证码字符串
if valid.upper()==random_str.upper():
from django.contrib import auth
user=auth.authenticate(username=user,password=pwd)
if user:
auth.login(request, user)
res["user"]=user.username
else:
res["msg"]="用户名或者密码错误"
else:
res["msg"]="验证码失败" #先判断验证码是否正确,正确后接着判断用户名和密码
return HttpResponse(json.dumps(res))
return render(reqeust,"login.html")
def valid_img(request):
from PIL import Image #需要安装pillow模块:pip3 install pillow
from PIL import ImageDraw,ImageFont
from io import BytesIO
image=Image.new("RGB",(250,36),color=get_random_color()) #随机创建图片,长度为250px,宽度为36px
draw=ImageDraw.Draw(image)
font = ImageFont.truetype("blog/static/font/kumo.ttf", size=32) #定义验证码的字体格式
random_str=""
for i in range(5): #生成一个5位的字符串
random_num=str(random.randint(0,9))
random_low_alpha=chr(random.randint(97,122))
random_up_alpha=chr(random.randint(65,90))
random_char=random.choice([random_num,random_low_alpha,random_up_alpha])
draw.text((35+i*40,0),random_char,get_random_color(),font=font)
random_str+=random_char
print(random_str)
request.session["random_str"]=random_str #得到的验证码字符串设置session以用来保存在内存
# 噪点噪线
width=250
height=36
for i in range(10):
x1=random.randint(0,width)
x2=random.randint(0,width)
y1=random.randint(0,height)
y2=random.randint(0,height)
draw.line((x1,y1,x2,y2),fill=get_random_color())
for i in range(100):
draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color())
x = random.randint(0, width)
y = random.randint(0, height)
draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color())
f=BytesIO()
image.save(f,"png")
data=f.getvalue()
return HttpResponse(data)
def index(request):
#验证是不是当前进来的那个用户,如果用户已经登录了就可以看到页面
# 如果没有登录就不让看见主页面,就直接返回登录页面
if not request.user.is_authenticated():
return redirect("/login/")
else:
return render(request, "index.html")
def log_out(request):
auth.logout(request)
return redirect("/login/")
login.html文件内容:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/bs/css/bootstrap.css">
<script src="/static/js/jquery-3.2.1.min.js"></script>
<style>
.error{
color: red;
margin-left: 20px;
}
</style>
</head>
<body>
<h3>登录页面</h3>
<div>
<div>
<div class="col-md-6 col-md-offset-3">
<form enctype="application/x-www-form-urlencoded">
{% csrf_token %}
<div>
<label for="">用户名</label>
<input type="text" id="user">
</div>
<div>
<label for="">密码</label>
<input type="password" id="pwd" >
</div>
<div>
<label for="">验证码</label>
<div>
<div>
<input type="text"id="valid" >
</div>
<div>
<img width="250" height="36" src="/valid_img/" alt="">
</div>
</div>
</div>
<input type="button" value="submit" class="login_btn btn btn-default">
<span></span>
</form>
</div>
</div>
</div>
<script>
// 登录验证
$(".login_btn").click(function () {
$.ajax({
url:"",
data:{
user:$("#user").val(), {# 取到用户输入的用户名 #}
pwd:$("#pwd").val(), {# 取到用户输入的密码 #}
valid:$("#valid").val(), {# 取到用户输入的验证码 #}
csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val() {# 防止出现404错误 #}
},
type:"post",
success:function (data) {
console.log(data);
var data=JSON.parse(data); {# 接收到的data进行反序列化 #}
if(data.user){
location.href = "/index/" {#跳转到内容页#}
}else{
$(".error").html(data.msg)
}
}
})
});
// 验证码局部刷新
$(".valid_img").click(function () {
$(this)[0].src+="?"
})
</script>
</body>
</html>
index.html文件内容:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width">
<title>Title</title>
</head>
<body>
<h1>hello{{ request.user.username }}</h1>
<button><a href="/log_out/">注销</a></button>
</body>
</html>
三、生成验证码的几种方式
1、方式一:这样的方式吧路径写死了,只能是那一张图片
import os
path = os.path.join(settings.BASE_DIR,"static","image","3.jpg")
with open(path,"rb") as f:
data = f.read()
return HttpResponse(data)
2、方式二:每次都显示不同的图片,利用pillow模块,安装一个pillow模块,图片保存到硬盘上
from PIL import Image
img = Image.new(mode="RGB",size=(120,40),color="green") #首先自己创建一个图片,参数size=(120,40) 代表长和高
f = open("validcode.png","wb") #然后把图片放在一个指定的位置
img.save(f,"png") #保存图片
f.close()
with open("validcode.png","rb") as f:
data = f.read()
return HttpResponse(data)
3、方式三:我们可以把图片保存到内存中,完了自动清除,那么就引入了方式三:利用BytesIO模块
from io import BytesIO
from PIL import Image
img = Image.new(mode="RGB",size=(120,40),color="blue")
f = BytesIO() #内存文件句柄
img.save(f,"png") #保存文件
data = f.getvalue() #打开文件(相当于python中的f.read())
return HttpResponse(data)
4、方式四:添加画笔,在图片上写上一些文字和噪点噪线
from PIL import Image # 需要安装pillow模块:pip3 install pillow
from PIL import ImageDraw, ImageFont
from io import BytesIO
image = Image.new("RGB", (250, 36), color=get_random_color()) # 随机创建图片,长度为250px,宽度为36px
draw = ImageDraw.Draw(image)
font = ImageFont.truetype("blog/static/font/kumo.ttf", size=32) # 定义验证码的字体格式
random_str = ""
for i in range(5): #生成一个5位的字符串
random_num = str(random.randint(0, 9)) # 随机取出一个数字
random_low_alpha = chr(random.randint(97, 122)) # 随机取出一个小写字母
random_up_alpha = chr(random.randint(65, 90)) # 随机取出一个大写字母
random_char = random.choice([random_num, random_low_alpha, random_up_alpha]) # 随机取出一个小写字母或者大写字母或者数字
draw.text((35 + i * 40, 0), random_char, get_random_color(), font=font) # 文字间距是40px,第一个到左边框距离是35px
random_str += random_char # 将取到的字符串内容放到random_str值的后面,每次循环加一个字符串
print(random_str)
request.session["random_str"] = random_str # 得到的验证码字符串设置session以用来保存在内存
# 噪点噪线
width=250
height=36
for i in range(10):
x1=random.randint(0,width)
x2=random.randint(0,width)
y1=random.randint(0,height)
y2=random.randint(0,height)
draw.line((x1,y1,x2,y2),fill=get_random_color())
for i in range(100):
draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color())
x = random.randint(0, width)
y = random.randint(0, height)
draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color())
f = BytesIO()
image.save(f, "png")
data = f.getvalue()
return HttpResponse(data)
四、注册相关内容
1、Form组件
我们一般写Form的时候都是把它写在views视图里面,还可以在应用下面建一个forms.py的文件来存放
from django.forms import widgets
from blog import models
from django import forms
from django.core.validators import ValidationError
class UserForm(forms.Form):
user=forms.CharField(
required=True,
max_length=16,
min_length=3,
error_messages={
"required": "用户名不能为空",
"max_length": "长度不能大于16",
"min_length": "长度不能小于3",
},
widget=widgets.TextInput({"placeholder":"用户名","class":"form-control"}))
pwd=forms.CharField(
required=True,
max_length=16,
min_length=3,
error_messages={
"required": "密码不能为空",
"max_length": "长度不能大于16",
"min_length": "长度不能小于3",
},
widget=widgets.PasswordInput({"placeholder": "密码", "class": "form-control"})
)
tel = forms.CharField(
required=True,
max_length=11,
min_length=11,
error_messages={
"required": "手机号码不能为空",
"max_length": "长度必须是11位,请你正确输入",
"min_length": "长度必须是11位,请你正确输入",
},
widget=widgets.TextInput({"placeholder": "手机号码", "class": "form-control"})
)
email=forms.EmailField(
required=True,
error_messages={
"required": "邮箱不能为空",
"invalid": "邮箱格式有误"
},
widget=widgets.EmailInput({"placeholder": "邮箱", "class": "form-control"})
)
2、局部钩子函数
# 自定义用户名验证:
def clean_user(self):
user = self.cleaned_data.get("user")
valid = models.UserInfo.objects.filter(username=user).first()
if valid:
raise ValidationError("用户名已存在")
return user
# 自定义手机号码验证:
def clean_tel(self):
tel = self.cleaned_data.get("tel")
if len(tel) == 11:
return tel
else:
raise ValidationError("手机号码格式错误")
3、全局钩子函数
# 自定义密码验证:
def clean_pwd(self):
password = self.cleaned_data.get("pwd")
if password.isdigit():
raise ValidationError("密码需要数字字母组合")
else:
return password
4、css中的三种隐藏:
(1)display:none #隐藏所有内容
(2)visibility:hidden #隐藏内容
(3)overflow:hidden #隐藏溢出内容
三者都是用来隐藏的:
区别在于:
visibility虽然隐藏了,但是被隐藏的内容依然占据这空间,这段隐藏了的内容却保留空间的位置会在网页中显示空白,而display:隐藏了不占用空间
5、 jQuery的属性操作相关的
attr:
一个参数是获取属性的值,两个参数是设置属性值
removeAttr(属性名):
删除属性值
prop:
适应于属性的返回值是布尔类型的(单选,反选,取消的例子)
removePorp:
删除属性的值
6、提交二进制数据用FormData
$(".reg_btn").click(function () {
var formdata=new FormData();
formdata.append("user",$("#user").val());
formdata.append("pwd",$("#pwd").val());
formdata.append("email",$("#email").val());
formdata.append("tel",$("#tel").val());
formdata.append("repeat_pwd",$("#repeat_pwd").val());
formdata.append("avatar_img",$("#avatar")[0].files[0]);
$.ajax({
url:"",
type:"post",
contentType:false,
processData:false,
data:formdata, {#ajax上传有文件用到formdata#}
success:function (data) {
// console.log(data);
var data=JSON.parse(data); {#反序列化json#}
if (data.user){
console.log("OK")
}else{
$("form span.error").html("")
$.each(data.msg,function (filed,error_list) {
$("#"+filed).next().html(error_list[0])
})
}
}
})
})
7、上传文件有一个固定的配置参数media
(1)首先在settings中配置:
MEDIA_URL="/media/" #别名
MEDIA_ROOT=os.path.join(BASE_DIR,"app01","media","uploads") #具体路径
(2)在url中配置:
url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
(3)作用
avatar = models.FileField(verbose_name='头像', upload_to='avatar', default="/avatar/default.png")
会把接收的文件放在media指代的路径与upload_to的拼接:BASE_DIR+blog+media+uploads+avatar/a.png
avatar字段在数据库中保存的是:avatar/a.png
<img src="/media/avatar/a.png">
(4)文件保存的位置:app01项目下------->media目录下------->uploads目录下------->avatar目录下
五、用户注册系统
views.py文件内容:
from django.shortcuts import render,HttpResponse
from django.forms import widgets
from blog import models
from django import forms
import json
from django.core.validators import ValidationError
class UserForm(forms.Form):
user=forms.CharField(
required=True,
max_length=16,
min_length=3,
error_messages={
"required": "用户名不能为空",
"max_length": "长度不能大于16",
"min_length": "长度不能小于3",
},
widget=widgets.TextInput({"placeholder":"用户名","class":"form-control"}))
pwd=forms.CharField(
required=True,
max_length=16,
min_length=3,
error_messages={
"required": "密码不能为空",
"max_length": "长度不能大于16",
"min_length": "长度不能小于3",
},
widget=widgets.PasswordInput({"placeholder": "密码", "class": "form-control"})
)
repeat_pwd=forms.CharField(
required=True,
max_length=16,
min_length=3,
error_messages={
"required": "密码不能为空",
"max_length": "长度不能大于16",
"min_length": "长度不能小于3",
},
widget=widgets.PasswordInput({"placeholder": "确认密码", "class": "form-control"})
)
tel = forms.CharField(
required=True,
max_length=11,
min_length=11,
error_messages={
"required": "手机号码不能为空",
"max_length": "长度必须是11位,请你正确输入",
"min_length": "长度必须是11位,请你正确输入",
},
widget=widgets.TextInput({"placeholder": "手机号码", "class": "form-control"})
)
email=forms.EmailField(
required=True,
error_messages={
"required": "邮箱不能为空",
"invalid": "邮箱格式有误"
},
widget=widgets.EmailInput({"placeholder": "邮箱", "class": "form-control"})
)
#自定义用户名验证:局部钩子
def clean_user(self):
user = self.cleaned_data.get("user")
valid = models.UserInfo.objects.filter(username = user).first()
if valid:
raise ValidationError("用户名已存在")
return user
#自定义手机号码验证:
def clean_tel(self):
tel = self.cleaned_data.get("tel")
if len(tel)== 11:
return tel
else:
raise ValidationError("手机号码格式错误")
#自定义密码验证:
def clean_pwd(self):
password = self.cleaned_data.get("pwd")
if password.isdigit():
raise ValidationError("密码需要数字字母组合")
else:
return password
#自定义全局钩子:验证两次密码是否一致
def clean(self):
if self.cleaned_data.get("pwd") == self.cleaned_data.get("repeat_pwd"):
return self.cleaned_data
else:
raise ValidationError("两次密码不一致")
def reg(request):
if request.method=="POST":
print(request.POST)
print(request.FILES)
form=UserForm(request.POST)
res={"user":None,"msg":None}
if form.is_valid():
username=form.cleaned_data.get("user")
password=form.cleaned_data.get("pwd")
email=form.cleaned_data.get("email")
telephone=form.cleaned_data.get("tel")
avatar_img = request.FILES.get("avatar_img")
print(username,password,telephone)
models.UserInfo.objects.create_user(user=username, pwd=password, email=email, avatar=avatar_img)
res["user"]=username
else:
res["msg"]=form.errors
return HttpResponse(json.dumps(res))
return render(request,"reg.html")
reg.html文件内容:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/bs/css/bootstrap.css">
<script src="/static/js/jquery-3.2.1.min.js"></script>
<style>
#avatar{
display: none; {#隐藏input文件选择框#}
}
.avatar_img{
width: 60px;
height: 60px;
margin-left: 10px;
}
.error{
color: red;
}
</style>
</head>
<body>
<h3>注册页面</h3>
<div>
<div>
<div class="col-md-6 col-md-offset-3">
<form action="/reg/" method="post" novalidate enctype="multipart/form-data">
{% csrf_token %}
<div>
<label for="user">用户名</label>
<input type="text" id="user"><span class="error pull-right"></span>
</div>
<div>
<label for="pwd">密码</label>
<input type="password" id="pwd" ><span class="error pull-right"></span>
</div>
<div>
<label for="repeat_pwd">确认密码</label>
<input type="password" id="repeat_pwd" ><span class="error pull-right"></span>
</div>
<div>
<label for="email">手机</label>
<input type="email" id="tel" ><span class="error pull-right"></span>
</div>
<div>
<label for="email">邮箱</label>
<input type="email" id="email" ><span class="error pull-right"></span>
</div>
<div>
<label for="avatar">头像 <img src="/static/img/default.png" alt=""></label>
<input type="file" id="avatar" >
</div>
<input type="button" value="submit" class="reg_btn btn btn-default">
<span></span>
</form>
</div>
</div>
</div>
<script>
// 头像预览
$("#avatar").change(function () { {#绑定change事件#}
var reader=new FileReader(); {#文件阅读器实例化对象 #}
var choose_file=$(this)[0].files[0]; {#取到用户选中的文件对象#}
reader.readAsDataURL(choose_file); {#拿到文件url路径#}
reader.οnlοad=function(){ {#等reader阅读器加载完后才执行里面的代码#}
$(".avatar_img").attr("src",reader.result)
};
})
// 注册
$(".reg_btn").click(function () {
var formdata=new FormData();
formdata.append("user",$("#user").val());
formdata.append("pwd",$("#pwd").val());
formdata.append("email",$("#email").val());
formdata.append("tel",$("#tel").val());
formdata.append("repeat_pwd",$("#repeat_pwd").val());
formdata.append("avatar_img",$("#avatar")[0].files[0]);
$.ajax({
url:"",
type:"post",
contentType:false,
processData:false,
data:formdata, {#ajax上传有文件用到formdata#}
success:function (data) {
// console.log(data);
var data=JSON.parse(data); {#反序列化json#}
if (data.user){
location.href = "/login/"
}else{
// 清空操作
$("form span.error").html("")
console.log(data.msg)
$.each(data.msg,function (filed,error_list) { {#循环data.msg,其中filed是键,对应的值是一个字典类型的数据#}
$span = $("<span>");
$span.addClass("pull-right").css("color","red");
$span.html(error_list[0]);
$("#"+filed).next().html(error_list[0]) {# "#"+filed拼接标签,然后在下一个span标签填充取到的错误信息#}
if (filed=="__all__"){ {#filed=="__all__"表示全局钩子取到了错误信息 #}
$("#repeat_pwd").next().html($span)
}
})
}
}
})
})
</script>
</body>
</html>
转载于:https://blog.51cto.com/qidian510/2117570