BBS博客系统(注册+登录)

BBS需求分析

1. 扩展auth_user表(继承AbstractUser)
    
    phone
    avatar
    create_time
    
    # 一对一站点表
    
 2. 站点表 blog
    site_name 站点名称
    site_title 站点标题
    site_theme 站点主题 css/mycss.css
3. 标签表
    name 标签名
    # 一对多站点表
4. 分类表
    name 分类名称
    # 一对多站点表
    
5. 文章表
    title 标题
    desc 简介
    content 内容
    create_time 创建时间
    
    #############优化字段#########################
    up_num       点赞数
    down_num      点踩数
    comment_num  评论数
    
    blog = ForeignKey(to='Blog')# 一对多站点表
    tags = ManyToMany()
    category = ForeignKey(to='Category')
    
    
6. 点赞点踩表
    # 哪个用户给那篇文章点了赞还是踩
    user ForeignKey(to='User')
    article ForeignKey(to='Article')
    is_up   BooleanField()  # 存0/1
    
7. 评论表
    # 那个人给那篇文章什么时间评论了什么内容
    user ForeignKey(to='User')
    article ForeignKey(to='Article')
    content 
    create_time
    # 自关联
    parent_id     ForeignKey(to='Comment')
    
   # ORM提供的
    parent_id     ForeignKey(to='self')
    
    id   user    article   parent_id
    1     1        1        0
    2     2        1        1
  
    
    
    根评论与子评论的概念
        # 根评论就是评论这篇文章的
         # 子评论
            1. PHP是世界上最好的语言
                1.1 python才是
                1.2 你滚蛋
    

 {{ form.auto_id }}  自动获取form组件渲染的input框的id值

步骤:

数据表设计
forms组件代码书写
注册页面搭建
用户头像实时展示
注册功能实现
登录页面搭建(图片验证码)
登录功能实现

 

创建数据表

models.py

from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class UserInfo(AbstractUser):
    phone = models.BigIntegerField(null=True)
    create_time = models.DateField(auto_now_add=True)
    # 该字段会将接受到文件自动存放到avatar文件夹下,只存该文件的路径 比如:avatar/111.png
    avatar = models.FileField(upload_to='avatar/',default='avatar/default.png')
    blog = models.OneToOneField(to='Blog',null=True)



class Blog(models.Model):
    site_name = models.CharField(max_length=32)
    site_title = models.CharField(max_length=64)
    # 个人站点的样式文件 存该样式文件的路径
    theme = models.CharField(max_length=64)


class Category(models.Model):
    name = models.CharField(max_length=64)
    blog = models.ForeignKey(to='Blog',null=True)

class Tag(models.Model):
    name = models.CharField(max_length=32)
    blog = models.ForeignKey(to='Blog',null=True)


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 Article2Tags(models.Model):
    article = models.ForeignKey(to='Article')
    tag = models.ForeignKey(to='Tag')



class UpAndDown(models.Model):
    user = models.ForeignKey(to='UserInfo')
    article = models.ForeignKey(to='Article')
    # 存0/1
    is_up = models.BooleanField()


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)

# 跟评论和子评论一对多的关系
# 自关联
# parent_id = models.ForeignKey(to='Comment', null=True)
# ORM提供的自关联


    parent = models.ForeignKey(to='self',null=True)

myform.py   注意需要在settings.py中添加

AUTH_USER_MODEL='app01.UserInfo'
from django import forms
from django.forms import widgets
from app01 import models
class MyForm(forms.Form):
    username = forms.CharField(max_length=8,min_length=3,label='用户名',error_messages={
                                                    'required':'用户名不能为空',
                                                    'max_length':'用户名最大8位',
                                                    'min_length':'用户名最小3位',
    },widget=widgets.TextInput(attrs={'class':'form-control'}))
    password = forms.CharField(max_length=8, min_length=3, label='密码',error_messages={
        'required': '密码不能为空',
        'max_length': '密码最大8位',
        'min_length': '密码最小3位',
    }, widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    confirm_password = forms.CharField(max_length=8, min_length=3, label='确认密码',error_messages={
        'required': '确认密码不能为空',
        'max_length': '确认密码最大8位',
        'min_length': '确认密码最小3位',
    }, widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(label='邮箱', error_messages={
        'required': '邮箱不能为空',
        'invalid': '邮箱格式错误',
    }, widget=widgets.EmailInput(attrs={'class': 'form-control'}))
    # 局部钩子校验用户名是否存在
    def clean_username(self):
        username = self.cleaned_data.get('username')
        user_obj = models.UserInfo.objects.filter(username=username).first()
        if user_obj:
            self.add_error('username','用户名已存在')
        return username


    # 全局钩子 校验密码是否一致
    def clean(self):
        password = self.cleaned_data.get('password')
        confirm_password = self.cleaned_data.get('confirm_password')
        if not password == confirm_password:
            self.add_error('confirm_password','两次密码不一致')
        return self.cleaned_data

 

views.py


from django.shortcuts import render,HttpResponse,redirect
from app01 import myforms
from app01 import models
from django.http import JsonResponse
from django.contrib import auth
from django.conf import settings
# Create your views here.
def register(request):
back_dic = {'code':100,'msg':''}
form_obj = myforms.MyForm()
if request.method == 'POST':
form_obj = myforms.MyForm(request.POST)
if form_obj.is_valid():
data = form_obj.cleaned_data
# 将confirm_password去掉
data.pop('confirm_password')
# 获取用户上传的文件对象
file_obj = request.FILES.get('myfile')
# 判断用户是否上传了自己的头像
if file_obj:
# 往data添加一组键值
data['avatar'] = file_obj
models.UserInfo.objects.create_user(**data)
back_dic['msg'] = '注册成功'
back_dic['url'] = '/login/'
else:
back_dic['code'] = 101
back_dic['msg'] = form_obj.errors
return JsonResponse(back_dic)
return render(request,'register.html',locals())


def login(request):
back_dic = {'code':100,'msg':''}
# 判断是ajax请求还是正常form表单请求 request.is_ajax()
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
code = request.POST.get('code')
# 先校验验证码(可以区分大小写也可以不区分 不区分:统一转大写或者小写进行比对即可)
if request.session.get('code').upper() == code.upper():
user_obj = auth.authenticate(username=username,password=password)
if user_obj:
# 登录成功记录当前用户状态
auth.login(request,user_obj)
back_dic['msg'] = '登录成功'
back_dic['url'] = '/home/'
else:
back_dic['code'] = 102
back_dic['msg'] = '用户名或密码错误'
else:
back_dic['code'] = 103
back_dic['msg'] = '验证码错误'
return JsonResponse(back_dic)
return render(request,'login.html')

from PIL import Image,ImageDraw,ImageFont,ImageFilter
# Image用来生成图片 ImageDraw在图片上写'字' ImageFont字体样式
import random
from io import BytesIO
# 能够帮你保存数据 并且在取的时候会以二进制的形式返回给你


# 随机生成rgb参数
def get_random():
return random.randint(0,255),random.randint(0,255),random.randint(0,255)


def get_code(request):
# 推导步骤1:打开本地文件发送二进制数据
# with open(r'D:\fullstack_s4\BBS\avatar\002_wLZfWbk.jpg','rb') as f:
# data = f.read()
# return HttpResponse(data)
# 推导步骤2:动态生成图片发送二进制数据
# img_obj = Image.new('RGB',(310,35),'green') # 第三个参数既可以传颜色英文也可以传rgb参数
# img_obj = Image.new('RGB',(310,35),(128,128,128)) # 第三个参数既可以传颜色英文也可以传rgb参数
# # 先保存成文件
# with open('demo.png','wb') as f:
# img_obj.save(f)
# # 再以二进制模式读取发送数据
# with open('demo.png','rb') as f:
# data = f.read()
# return HttpResponse(data)

# 推导步骤3:图片颜色动态变化 图片存放不再依赖于文件的形式
# img_obj = Image.new('RGB',(310,35),get_random())
# # 生成一个BytesIO对象
# io_obj = BytesIO() # 将这个对象看成文件句柄
# img_obj.save(io_obj,'png') # 将图片数据存入内存管理器中 需要指定图片格式
# return HttpResponse(io_obj.getvalue()) # 将保存的数据以二进制的数据返回出来

# 最终不删改版
img_obj = Image.new('RGB',(310,35),get_random())
# 生成一个画笔对象
img_draw = ImageDraw.Draw(img_obj) # 你的画笔就可以在该图片上为所欲为
# 生成一个字体对象
img_font = ImageFont.truetype('static/font/mo.ttf',35)

# 随机验证码: 数字+小写字母+大写字母
code = '' # 定义一个变量存储最终验证码
for i in range(5):
random_int = str(random.randint(0,9))
random_lower = chr(random.randint(97,122))
random_upper = chr(random.randint(65,90))
temp_code = random.choice([random_int,random_lower,random_upper])
# 将产生的字一个一个的写到图片上
img_draw.text((60+i*45,0),temp_code,get_random(),img_font)
# code记录
code += temp_code
print(code)
# 将code存放到session表中
request.session['code'] = code
# 生成io对象
io_obj = BytesIO()
# 图片模糊
# img_obj = img_obj.filter(ImageFilter.BLUR)
img_obj.save(io_obj,'png')
return HttpResponse(io_obj.getvalue())


def home(request):
return render(request,'home.html')

def logout(request):
auth.logout(request)
return redirect('/home/')

def set_password(request):
old_password = request.POST.get('old_password')
new_password = request.POST.get('new_password')
confirm_password = request.POST.get('confirm_password')
# 先判断旧密码是否正确
res = request.user.check_password(old_password)
if res:
# 再来比对新密码是否一致
if new_password == confirm_password:
request.user.set_password(new_password)
request.user.save()
return redirect('/login/')
return render(request,'set_password.html')


 

register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css">
    <script src="/static/bootstrap-3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h2 class="text-center">注册</h2>
            <hr>
            <form id="myform">
                {% csrf_token %}
                {% for form in form_obj %}
                    <div class="form-group">
                        <label for="{{ form.auto_id }}">{{ form.label }}</label>
                        {{ form }}
                        <span class="errors pull-right" style="color: red"></span>
                    </div>
                {% endfor %}
            </form>
            <div class="form-group">
                <label for="id_myfile">头像
                <img src="/static/img/default.png" alt="" width="80" style="margin-left: 20px" id="id_img">
                </label>
                <input type="file" name="myfile" id="id_myfile" style="display: none">
            </div>
            <button class="btn btn-primary pull-right" id="id_submit">注册</button>
        </div>
    </div>
</div>

<script>
    $('#id_myfile').change(function () {
        // 先获取用户上传的文件对象
        let fileObj = this.files[0];
        // 生成一个内置对象
        let fileReader = new FileReader();
        // 将文件对象传递给内置对象
        fileReader.readAsDataURL(fileObj);
        // 将读取出文件对象替换到img标签
        fileReader.onload = function(){  // 等待文件阅读器读取完毕再渲染图片
           $('#id_img').attr('src',fileReader.result)
        }
    });
    // ajax提交数据
    $('#id_submit').click(function () {
        // 生成一个Formdata对象
        let formData = new FormData();
        // 往Formdata对象中添加键值
        // console.log($('#myform').serializeArray());
        $.each($('#myform').serializeArray(),function (index,obj) {
            // console.log(index,obj)
            formData.append(obj.name,obj.value)
        });
        // 手动添加文件数据
        formData.append('myfile',$('#id_myfile')[0].files[0]);
        $.ajax({
            url:'',
            type:'post',
            data:formData,
            // 需要指定的两个参数
            processData:false,
            contentType:false,
            success:function (data) {
                if (data.code == 100){
                    // 跳转到登录页面
                    location.href = data.url
                } else{
                    $.each(data.msg,function (index,obj) {
                        // console.log(index,obj)
                        let targetId = '#id_' + index;  // id_username,id_password...
                        $(targetId).next().html(obj[0]).parent().addClass('has-error')
                    })
                }
            }
        })
    });
    $('input').focus(function () {
        $(this).next().html('').parent().removeClass('has-error')
    })
</script>
</body>
</html>

 login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css">
    <script src="/static/bootstrap-3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h2 class="text-center">登录</h2>
            {% csrf_token %}
            <div class="form-group">
                <label for="id_username">用户名</label>
                <input type="text" name="username" id="id_username" class="form-control">
            </div>
            <div class="form-group">
                <label for="id_password">密码</label>
                <input type="password" name="password" id="id_password" class="form-control">
            </div>
            <div class="form-group">
                <label for="id_code">验证码</label>
                <div class="row">
                    <div class="col-md-6">
                        <input type="text" name="code" id="id_code" class="form-control">
                    </div>
                    <div class="col-md-6">
                        <img src="/get_code/" alt="" width="310" height="35" id="id_img">
                    </div>
                </div>
            </div>
            <button class="btn btn-success" id="id_button">登录</button>
            <span class="errors" style="color: red" id="id_error"></span>
        </div>
    </div>
</div>

<script>
    $('#id_img').click(function () {
        // 获取图片src旧的路径
        let oldPath = $(this).attr('src');
        // 修改图片的src属性
        $(this).attr('src',oldPath += '?')
    });

    // ajax发送数据
    $('#id_button').click(function () {
        $.ajax({
            url:'',
            type:'post',
            data:{
                'username':$('#id_username').val(),
                'password':$('#id_password').val(),
                'code':$('#id_code').val(),
                // 'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val(),
                'csrfmiddlewaretoken':'{{ csrf_token }}',
            },
            success:function (data) {
                if(data.code == 100){
                    location.href = data.url
                }else{
                    $('#id_error').html(data.msg)
                }
            }
        })
    })



</script>


</body>
</html>

 

#我们计算机上所有能够输出各式各样的字体样式,是因为有.ttf文件(需要下载,放入到static里去)

 

posted @ 2021-12-09 21:50  甜甜de微笑  阅读(537)  评论(0)    收藏  举报