BBS项目
BBS表关系:

静态文件配置与auth默认表配置
# 静态文件配置 STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')] # auth默认表 AUTH_USER_MODEL = 'app01.UserInfo'
注册:
form主键
1 from django import forms 2 from django.forms import widgets 3 from app01 import models 4 5 6 # 定义form主件 7 class MyForm(forms.Form): 8 username = forms.CharField(max_length=8, min_length=3, label='用户名:', 9 error_messages={'required': '用户名不能为空', 'max_length': '用户名最大为8位', 10 'min_length': '用户名最小为3位'}, 11 widget=widgets.TextInput(attrs={'class': 'form-control'})) 12 password = forms.CharField(max_length=8, min_length=3, label='密码:', 13 error_messages={'required': '密码不能为空', 'max_length': '密码最大为8位', 'min_length': '密码最小为3位'}, 14 widget=widgets.PasswordInput(attrs={'class': 'form-control'})) 15 re_password = forms.CharField(max_length=8, min_length=3, label='确认密码:', 16 error_messages={'required': '确认密码不能为空', 'max_length': '确认密码最大为8位', 17 'min_length': '确认密码最小为3位'}, 18 widget=widgets.PasswordInput(attrs={'class': 'form-control'})) 19 email = forms.EmailField(label='邮箱', error_messages={'required': '邮箱不能为空:', 20 'invalid': '邮箱格式错误'}, 21 widget=widgets.EmailInput(attrs={'class': 'form-control'})) 22 23 # 定义局部钩子 校验用户名是否存在 24 25 def clean_username(self): 26 username = self.cleaned_data.get('username') 27 user_obj = models.UserInfo.objects.filter(username=username).first() 28 if user_obj: 29 self.add_error('username', '用户名已经存在') 30 return username 31 32 # 局部钩子函数是检验一个字段的 而全局钩子函数的话是校验多个字段的 比如说 只校验用户名是否存在这个就是要用到钩子函数 33 # ↓↓↓↓↓↓↓↓ 34 # 而两个字段以上或多个 比如说 密码 确认密码 校验 密码是否一致 35 # 定义全局钩子 校验密码是否一致 36 def clean(self): 37 password = self.cleaned_data.get('password') 38 re_password = self.cleaned_data.get('re_password') 39 if password == re_password: 40 return self.cleaned_data 41 self.add_error('re_password', '两次密码不一致')
model
from django.contrib.auth.models import AbstractUser # 重写原Django的user表给其添加一些自己的字段 class UserInfo(AbstractUser): phone = models.BigIntegerField(null=True) # 电话号码 # 该字段会将接受到文件自动存放到avatar文件夹下,只存该文件的路径 比如:avatar/default.png create_time = models.DateField(null=True, auto_now_add=True) # 用户创建时间 avatar = models.FileField(upload_to='avatar/', default='/avatar/default.png/') # 头像 blog = models.OneToOneField(to='Blog', null=True) # 一对一关联字段 相当于就是一张表拆分出去的表 # 个人站点 class Blog(models.Model): title = models.CharField(max_length=32) # 标题 content = models.CharField(max_length=32) # 内容 theme = models.CharField(max_length=255) # 主题 样式 # 标签 class Tag(models.Model): name = models.CharField(max_length=32) # 标签名称 blog = models.ForeignKey(to='Blog', null=True) # 分类 class Category(models.Model): name = models.CharField(max_length=32) # 分类名称 blog = models.ForeignKey(to='Blog', null=True) class Article2Tags(models.Model): article = models.ForeignKey(to='Article') tag = models.ForeignKey(to='Tag') # 文章 class Article(models.Model): title = models.CharField(max_length=64) # 标题 desc = models.CharField(max_length=255) # 简介 描述 content = models.TextField() # 文章内容 create_time = models.DateField(auto_now_add=True) # 文章的创建时间 # 查询优化 comment_num = models.IntegerField() # 评论数 up_num = models.IntegerField() # 点赞数 down_num = models.IntegerField() # 点踩数 blog = models.ForeignKey(to='Blog', null=True) # 个人站点与文章一对多字段 category = models.ForeignKey(to='Category', null=True) # 分类与文章的一对多字段 tags = models.ManyToManyField(to='Tag', through='Article2Tags', through_fields=('article', 'tag')) # 点赞 点踩表 class UpAndDown(models.Model): user = models.ForeignKey(to='UserInfo') article = models.ForeignKey(to='Article') is_up = models.IntegerField() # 0点赞 1点踩 # 评论 class Comment(models.Model): user = models.ForeignKey(to='UserInfo') article = models.ForeignKey(to='Article') content = models.CharField(max_length=255) # 评论内容 create_time = models.DateField(auto_now_add=True, null=True) # 评论的创建时间 parent = models.ForeignKey(to='self', null=True) # 自关联 用self
前端页面
1 <div class="container"> 2 <div class="row"> 3 <div class="col-md-6 col-md-offset-3 col-sm-6 "> 4 <h1>注册界面</h1> 5 <form id="id_form"> 6 {% csrf_token %} 7 {% for foo in myform %} 8 <div class="form-group"> 9 <label for="{{ foo.auto_id }}">{{ foo.label }}</label> 10 {{ foo }} 11 <span class="errors pull-right" style="color: red"></span> 12 </div> 13 14 {% endfor %} 15 16 </form> 17 <div class="form-group"> 18 <label for="id_file">请选择头像: 19 <img src="/static/img/default.png" alt="avatar" title="用户头像" 20 id="id_img"> 21 </label> 22 <input type="file" name="myfile" id="id_file"> 23 </div> 24 <input type="submit" name="" id="id_register" value="注册" class="btn btn-primary pull-right"> 25 </div> 26 27 </div> 28 </div> 29 <script> 30 $('#id_file').change(function () { 31 // 获取用户上传的文件 32 let file = this.files[0]; 33 {#alert(file)#} 34 // 生成内置对象 35 let fileReder = new FileReader(); 36 // 将获取到的文件传给内置对象 37 fileReder.readAsDataURL(file); 38 // 这一步操作就是让上传的图片先加载完成 然后在把新图片覆盖旧图片 39 fileReder.onload = function () { 40 $('#id_img').attr('src', fileReder.result) // 获取img标签替换src属性 fileReder.result就是一张图片的路径 41 }; 42 43 }); 44 $('#id_register').on('click', function () { 45 // 由于是有图片 所以送fromdata发送数据 实例化一个fromdata对象 46 let fromdata = new FormData(); 47 $.each($('#id_form').serializeArray(), function (index, obj) { 48 fromdata.append(obj.name, obj.value) 49 }); 50 fromdata.append('myfile', $('#id_file')[0].files[0]); 51 $.ajax({ 52 url: '/register/', 53 type: 'post', 54 data: fromdata, // {'username':'zk','password':'123','re_password,'123','email':'123@qq.com'} 55 processData: false, 56 contentType: false, 57 success: function (data) { 58 if (data.code == 100) { 59 location.href = data.url 60 61 } else { 62 $.each(data.msg, function (index, obj) { 63 console.log(index, obj); 64 // index,obj 打印出来的结果就是 index==标签里面name的名字 obj 等于后端传过来的msg 65 let targetId = '#id_' + index; // 这里拼接就是 拼接出一个#id_username的形式 方便找到标签 66 {#$(targetId).next().html(obj[0].parent().addClass('has-error'))#} 67 // 这里找到input标签然后取他下面一个标签 并且把值赋给span的文本 然后找到当前的父标签给他增加样式 input框就标红了 68 $(targetId).next().html(obj[0]).parent().addClass('has-error') 69 70 }) 71 } 72 } 73 }) 74 }); 75 $('input').focus(function () { 76 $(this).next().html('').parent().removeClass('has-error') 77 }) 78 79 </script>
路由
url(r'^register/', views.register, name='register'),
登录
前端
<div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <h1 class="text-center">登录</h1> {% csrf_token %} <div class="form-group"> <label for="id_username"> </label> 用户名:<input type="text" name="" id="id_username" class="form-control"> </div> <div class="form-group"> <label for="id_password"> </label> 密码:<input type="password" name="" id="id_password" class="form-control"> </div> <div class="row"> <div class="col-sm-6"> <div class="form-group"> <label for="id_code"> </label> 验证码:<input type="text" name="" id="id_code" class="form-control"> </div> </div> <div class="col-sm-6"> <div class="form-group"> <img src="/get_code/" alt="" id="id_img" width="260px" height="35px" style="margin-top: 20px" title="点击刷新验证码"> </div> </div> <span style="margin-left: 20px;color: red" id="id_span"></span> </div> <div class="form-group"> <input type="submit" name="" id="id_login" class="btn btn-primary" value="登录"> </div> </div> </div> </div> <script> $('#id_img').click(function () { let imge = $(this).attr('src'); $(this).attr('src', imge += '?') }); $('#id_login').on('click', function () { $.ajax({ url: '/login/', type: 'post', data: { 'username': $('#id_username').val(), 'password': $('#id_password').val(), 'code': $('#id_code').val(), 'csrfmiddlewaretoken': '{{ csrf_token }}' }, success: function (data) { if (data.code == 100) { location.href = data.url } else if (data.code == 101) { $('#id_span').html(data.error) } else { $('#id_span').html(data.msg) } } }) }) </script>
后端
from PIL import Image, ImageDraw, ImageFont # 登录功能 def login(request): back_dic = {'code': 100, 'msg': '', 'error': '', 'url': ''} if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') code = request.POST.get('code') if code.lower() == request.session.get('code').lower(): user_obj = auth.authenticate(username=username, password=password) if user_obj: auth.login(request, user_obj) back_dic['msg'] = '登录成功' back_dic['url'] = '/index/' else: back_dic['code'] = 101 back_dic['error'] = '用户名密码错误' else: back_dic['code'] = 102 back_dic['msg'] = '验证码错误' return JsonResponse(back_dic) return render(request, 'login.html') # 随机产生RGB def get_img_rgb(): return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)
补充
def get_code(request): # with open(r'D:\python\CodePath\BBS\avatar\timg.gif','rb')as f: # data = f.read() img_obj = Image.new('RGB', (260, 35), color=get_img_rgb()) # 生成一个画笔对象 img_draw = ImageDraw.Draw(img_obj) # 生成一个字体对象 img_font = ImageFont.truetype('static/font/lj.ttf', 30) code = '' # 循环产生几个验证码 for i in range(5): # 随机生成0,9的数字 random_int = str(random.randint(0, 9)) # 随机生成小写的英文字母 random_lower = str(chr(random.randint(97, 122))) # 随机生成大写的英文字母 random_upper = str(chr(random.randint(65, 90))) # 选择一个字母 temp_code = random.choice([random_int, random_lower, random_upper]) # 写入图片中 text第一个参数是X轴Y轴坐标,第二个参数是文字,第三个参数是颜色 第四个参数是字体 img_draw.text((30 + i * 45, 0), temp_code, get_img_rgb(), img_font) code += temp_code # 把拿到的验证码放入session表中方便后面的校验 request.session['code'] = code bio = BytesIO() img_obj.save(bio, 'png') return HttpResponse(bio.getvalue())
注销
url
url(r'^logout/', views.logout,name='logout'),
后端
def logout(request): auth.logout(request) return redirect('/index/')
修改密码
后端
def edit_password(request): back_dic = {'code': 101, 'error': ''} if request.method == 'POST': old_password = request.POST.get('id_old_password') new_password = request.POST.get('id_new_password') re_password = request.POST.get('id_re_password') res = request.user.check_password(old_password) if res: if new_password == re_password: request.user.set_password(new_password) request.user.save() back_dic['error'] = '修改成功' back_dic['url'] = '/login/' else: back_dic['code'] = 102 back_dic['error'] = '两次密码不一致' else: back_dic['code'] = 103 back_dic['error'] = '旧密码与原密码不一致' return JsonResponse(back_dic) return render(request, 'edit_password.html', locals())
前端
<div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <h1 class="text-center">修改密码</h1> {% csrf_token %} <div class="form-group"> <label for="id_username"> </label> 用户名:<input type="text" name="" id="id_username" class="form-control" value="{{ request.user.username }}" readonly> </div> <div class="form-group"> <label for="id_old_password"> </label> 旧密码:<input type="password" name="" id="id_old_password" class="form-control"/> <span style="color: red" id="old_span"></span> </div> <div class="form-group"> <label for="id_new_password"> </label> 新密码:<input type="password" name="" id="id_new_password" class="form-control"> </div> <div class="form-group"> <label for="id_re_password"> </label> 确认密码:<input type="password" name="" id="id_re_password" class="form-control"> <span style="color: red" id="new_span"></span> </div> <div class="row"> <span style="margin-left: 20px;color: red" id="id_span"></span> </div> <div class="form-group"> <input type="submit" name="" id="id_login" class="btn btn-primary" value="修改密码"> </div> </div> </div> </div> <script> $('#id_login').on('click', function () { $.ajax({ url: '/edit_password/', type: 'post', data: { 'id_old_password': $('#id_old_password').val(), 'id_new_password': $('#id_new_password').val(), 'id_re_password': $('#id_re_password').val(), 'csrfmiddlewaretoken': '{{ csrf_token }}' }, success: function (data) { if (data.code == 101) { location.href = data.url } else if (data.code == 102) { $('#new_span').html(data.error) } else { $('#old_span').html(data.error) } } }) }) </script>
主页面
<nav class="navbar navbar-inverse"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">博客园系统</a> </div> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="#">文章 <span class="sr-only">(current)</span></a></li> <li><a href="#">随笔</a></li> </ul> <ul class="nav navbar-nav navbar-right"> {% if request.user.is_authenticated %} <li><a href="#">{{ request.user.username }}</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">更多操作 <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="{% url 'edit_password' %}">修改密码</a></li> <li><a href="#">修改头像</a></li> <li><a href="{% url 'logout' %}">注销</a></li> </ul> </li> {% else %} <li><a href="{% url 'login' %}">登录</a></li> <li><a href="{% url 'register' %}">注册</a></li> {% endif %} </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> </div>

浙公网安备 33010602011771号