1、环境准备css
一、配置文件html
settings.py文件:python
增长一项内容实现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', ], }, }, ]
二、表关系设计jquery
models.py文件内容:git
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
三、数据库实例化ajax
python3 manage.py makemigrations数据库
python3 manage.py migratedjango
2、登陆系统json
urls.py文件内容:bootstrap
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>
3、生成验证码的几种方式
一、方式一:这样的方式吧路径写死了,只能是那一张图片
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)
二、方式二:每次都显示不一样的图片,利用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)
三、方式三:咱们能够把图片保存到内存中,完了自动清除,那么就引入了方式三:利用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)
四、方式四:添加画笔,在图片上写上一些文字和噪点噪线
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)
4、注册相关内容
一、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"}) )
二、局部钩子函数
# 自定义用户名验证: 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
四、css中的三种隐藏:
(1)display:none #隐藏全部内容
(2)visibility:hidden #隐藏内容
(3)overflow:hidden #隐藏溢出内容
三者都是用来隐藏的:
区别在于:
visibility虽然隐藏了,可是被隐藏的内容依然占据这空间,这段隐藏了的内容却保留空间的位置会在网页中显示空白,而display:隐藏了不占用空间
五、 jQuery的属性操做相关的
attr: 一个参数是获取属性的值,两个参数是设置属性值 removeAttr(属性名): 删除属性值 prop: 适应于属性的返回值是布尔类型的(单选,反选,取消的例子) removePorp: 删除属性的值
六、提交二进制数据用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]) }) } } }) })
七、上传文件有一个固定的配置参数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目录下
5、用户注册系统
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.onload=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>