Django学习 day72之BBS项目day1

一 博客项目需求

-多人博客
-博客首页
-登录:图片验证码
-注册:上传头像
-自己有自己的个人站点(根据分类,标签,时间,过滤文章)
-自己的后台管理
	-发表博客(富文本编辑器,xss攻击处理)
    -查看,删除
-文章分类
-随笔档案
-文章标签
-文章
-文章详情
-评论(根评论,子评论,被评论时邮件通知)
-点赞点踩

二 bbs项目表设计

表设计(8张)

1 用户表:UserInfo表
2 博客表:Blog表
3 分类表:Category
4 标签表:Tag
5 文章:Article(文章和详情一个表)
6 评论:Commit 
7 点赞点踩表:UpAndDown

表关系

在这里插入图片描述

表中字段

1 用户表:UserInfo表(通过继承auth_user扩展)
	-telephone
    -avatar(头像)
    -blog  关联字段
2 博客表:Blog表
	-site_title
    -site_name
    -site_style
3 分类表:Category
	-name
    -blog
4 标签表:Tag
	-name
    -blog
5 文章:Article(文章和详情一个表)
	-title
    -desc
    -content
    -create_time
    -blog
    -category
    -tag(在数据没有字段)
    -#最后更新时间
6 评论:Commit 
	-user
    -article
    -create_time
    -content
7 点赞点踩表:UpAndDown
	-user
    -article
    -is_up
    -create_time

三 数据库表创建及同步

子评论如何关联父评论?:自关联

如下表所示,commit字段关联了自身表的id字段,新的操作出现啦!自关联!!

# 根评论和子评论分析							 # 关联父评论的id号
id   user  articel   content                   commit
1     1     1         写的真好                     null
2     1     2         写的真烂                     null
3     2     2         你放屁,人家写的很好            2
4     1     2         你怎么骂人呢?                 3
5     2     2         就骂你怎么了                   4
6     3     2         你这个嘴太碎了                 2
7     4     2         碎嘴子                        2
  • 具体请看下方合集最后的Commit表的创建

表模型创建大合集:

from django.db import models

from django.contrib.auth.models import AbstractUser  # 拓展auth_user表用


class UserInfo(AbstractUser):  # 用户拓展表
    phone = models.CharField(max_length=32)
    # upload_to文件上传以后存放的路径,可以在settings.py中设置上传父目录
    # 存放路径必须是结尾有路径分隔符,而开头必须没有
    # FileField本质是varchar类型
    avatar = models.FileField(upload_to='avatar/', default='avatar/default.png')
    blog = models.OneToOneField(to='Blog',on_delete=models.CASCADE)


class Blog(models.Model):  # 博客样式表
    site_title = models.CharField(max_length=32)
    site_name = models.CharField(max_length=32)
    # 每个人样式不同(文件地址)
    site_style = models.CharField(max_length=32)


class Tag(models.Model):  # 标签表
    name = models.CharField(max_length=32)
    blog = models.ForeignKey(to='Blog',on_delete=models.CASCADE)


class Category(models.Model):  # 分类表
    name = models.CharField(max_length=32)
    blog = models.ForeignKey(to='Blog',on_delete=models.CASCADE)


class Article(models.Model):  # 文章页表
    title = models.CharField(max_length=32)  # 标题
    desc = models.CharField(max_length=128)  # 详情
    # 大文本
    content = models.TextField()  # 文章
    create_time = models.DateTimeField(auto_now_add=True)  # 创建时间

    # 关联关系
    blog = models.ForeignKey(to='Blog',on_delete=models.CASCADE)
    category = models.ForeignKey(to='Category',on_delete=models.CASCADE)
    # 多对多关系
    tag = models.ManyToManyField(to='Tag', through='TagToArticle', through_fields=('article', 'tag'))


class TagToArticle(models.Model):  # 标签与文章第三方关联表
    tag = models.ForeignKey(to='Tag',on_delete=models.CASCADE)
    article = models.ForeignKey(to='Article',on_delete=models.CASCADE)


class UpAndDown(models.Model):  # 点赞点踩表,一个用户对一篇文章只能点一次赞,或一次踩,不能多
    user = models.ForeignKey(to='UserInfo',on_delete=models.CASCADE)
    article = models.ForeignKey(to='Article',on_delete=models.CASCADE)
    # 实质存的时候,是0和1
    is_up = models.BooleanField()
    create_time = models.DateTimeField(auto_now_add=True)


class Commit(models.Model):  # 评论表
    user = models.ForeignKey(to='UserInfo',on_delete=models.CASCADE)
    article = models.ForeignKey(to='Article',on_delete=models.CASCADE)
    content = models.CharField(max_length=256)
    create_time = models.DateTimeField(auto_now_add=True)

    # 存父评论的id号
    # commit_id=models.IntegerField()
    # commit=models.ForeignKey(to='Commit')
    # 自关联(不能叫表明小写)
    # 关联字段名不能与表名重复,不区分大小写,所以这里用的commit_id,之后在数据库中会自动生成commit_id_id,所以真正创建时选个更好听的名字,不要像这里带了_id
    commit_id=models.ForeignKey(to='self',on_delete=models.CASCADE)

    
   # 迁移(两条命令)

四 注册form组件编写

  1. 用户名,密码, 修改个人信息:邮箱,手机号,头像
  2. 用户名,密码,邮箱,头像
from django import forms
from django.forms import widgets  # 为form组件添加属性

from blog import models
from django.forms import ValidationError  # 数据匹配错误时抛出异常


class RegisterForm(forms.Form):  # 注册form组件

	# 账号
    username = forms.CharField(required=True, max_length=18, min_length=3, label='用户名',
                               error_messages={'required': '该字段必填',
                                               'max_length': '最大长度为18',
                                               'min_length': '最短为3'},
                               widget=widgets.TextInput(attrs={'class': 'form-control'}))

	# 密码
    password = forms.CharField(required=True, max_length=18, min_length=3, label='密码',
                               error_messages={'required': '该字段必填',
                                               'max_length': '最大长度为18',
                                               'min_length': '最短为3'},
                               widget=widgets.PasswordInput(attrs={'class': 'form-control'}))

	# 重复密码
    re_password = forms.CharField(required=True, max_length=18, min_length=3, label='确认密码',
                                  error_messages={'required': '该字段必填',
                                                  'max_length': '最大长度为18',
                                                  'min_length': '最短为3'},
                                  widget=widgets.PasswordInput(attrs={'class': 'form-control'}))

	# 邮箱,用于之后被评论时邮箱提示(day5)
    email = forms.EmailField(required=True, label='邮箱',
                             error_messages={'required': '该字段必填'},
                             widget=widgets.EmailInput(attrs={'class': 'form-control'}))

    # 用户名如果存在了,就不能注册(针对账号的局部钩子)
    def clean_username(self):
        username = self.cleaned_data.get('username')
        # 去数据库查询
        user = models.UserInfo.objects.filter(username=username).count()
        if user:  # 不合法
            raise ValidationError('用户名已经存在')
        else:
            return username

    # 校验两次密码是否一致(全局钩子)
    def clean(self):
        password = self.cleaned_data.get('password')
        re_password = self.cleaned_data.get('re_password')
        if re_password == password:
            return self.cleaned_data
        else:
            raise ValidationError('两次密码不一致')

五 注册功能页面搭建

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <script src="/static/jquery-3.3.1/jquery-3.3.1.min.js"></script>
    <title>注册</title>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <h1 class="text-center">注册功能</h1>
        <div class="col-md-6 col-md-offset-3">

            <form id="form">
            {% csrf_token %}
                {% for foo in form %}  <!--循环注册form组件-->
                    <div class="form-group">
                    <!--组件会自动给每个input标签生成id_name这样的id,而foo.auto_id就是将之取出-->
                    <!--label名取自于组件的label参数-->
                        <label for="{{ foo.auto_id }}">{{ foo.label }}</label>
                        <!--全自动input写法,香-->
                        {{ foo }}
                    </div>
                {% endfor %}
                <div class="form-group">
                <!--这里包裹图片标签的label标签指向的是下面的文件上传按钮-->
                    <label for="id_myfile">头像
                        <img src="/static/img/default.png" alt="" id="id_img" height="80" width="80"
                             style="margin-left: 10px">
                    </label>
					<!--而文件上传按钮则设置样式为隐藏,这样实现点击图片就能上传-->
                    <input type="file" name="myfile" id="id_myfile" style="display: none">
                </div>
                <div class="text-center">
                    <!--注意这个坑!!!如果用submit上传,则无法同时用于ajax,所以上传按钮我们选择用button,之后再用js语言写上传-->
                    <input type="button" value="注册" id="id_submit" class="btn btn-danger">
                </div>
            </form>
        </div>
    </div>
</div>
</body>

<script>
	// 实现头像实时显示
    //当放文件的标签发生变化时触发,我们把文件导出来,放到img标签中
    $("#id_myfile").change(function () {
        //借助于文件阅读器
        // const:常量,必须初始化(赋值起步)
        const filereader = new FileReader()
        //$('#id_myfile')[0].files[0] 拿到文件的写法
        //把图片读到filereader对象中,背就完事了
        filereader.readAsDataURL($('#id_myfile')[0].files[0])
        //$('#id_img').attr('src','https://account.cnblogs.com/images/registersideimg.png')
        //$('#id_img').attr('src',filereader.result) //这样不行,文件没读完
        filereader.onload = function () {
            //onload的作用是等文件完全读到文件阅读器以后,再执行
            
            $('#id_img').attr('src', filereader.result)
        }
    })
	
	//之前的上传botton,点击触发函数
    $("#id_submit").click(function () {
        var formdata = new FormData()
        formdata.append('avatar', $('#id_myfile')[0].files[0])
        //第一种方式:(笨比方式,完全不会用)
        /*
        formdata.append('username', $('#id_name').val())
        formdata.append('password', $('#id_password').val())
        formdata.append('re_password', $('#id_re_password').val())
        formdata.append('email', $('#id_email').val())
         */
        //第二种方式:(用这个没错)
        var ser = $('#form').serializeArray()
        // serializeArray()是将form表单中所有输入的东西拿出来存成字典,字典格式如下
        //[{name:name,value:lqz}, {name:password,value:123}, {name:re_password,value:123}, {name:email,value:123@qq.com}]
        //console.log(ser)
		
		// jq的遍历变量ser的语法
        $.each(ser, function (k, v) {
            //console.log(v.name)
            //console.log(v.value)
            formdata.append(v.name, v.value)  // 一斩千击!
        })
		
        $.ajax({
            url: '/register/',
            method: 'post',
            processData: false,
            contentType: false,
            data: formdata,
            success: function (data) {
                if (data.code == 100) {
                    console.log(data.msg)
                    //js控制的跳转
                    location.href=data.url
                } else {
                    //有错误,需要渲染页面,明天再讲
                }
            }
        })
    })
</script>
</html>

六 头像实时显示

从五的注册页面特地复制出来,划重点

<script>
	$("#id_myfile").change(function () {
	    //借助于文件阅读器
	    const filereader = new FileReader()
	    //把图片读到filereader对象中
	    //$('#id_myfile')[0].files[0]
	    filereader.readAsDataURL($('#id_myfile')[0].files[0])
	    //$('#id_img').attr('src','https://account.cnblogs.com/images/registersideimg.png')
	    //等同于$('#id_img').attr('src',filereader.result) //但这样不行,文件没读完
	    filereader.onload = function () {
	        //文件完全读到文件阅读器以后,再执行
	        $('#id_img').attr('src', filereader.result)
	    }
	})
</script>

七 注册功能完成(后端)

def register(request):
    if request.method == 'GET':
        form = RegisterForm()
        return render(request, 'register.html', context={'form': form})
    elif request.method == 'POST':
        # 校验数据是否合法
        res = {'code': 100, 'msg': '注册成功'}  # 针对ajax部分需要的信息,传输的字典
        form = RegisterForm(data=request.POST)
        if form.is_valid():

            # 保存到数据库
            data = form.cleaned_data  # {username:lqz,password:123,re_password:123,email:3@qq.com}
            data.pop('re_password')# {username:lqz,password:123,email:3@qq.com}
            file=request.FILES.get('avatar')
            if file:
                data['avatar']=file # {username:lqz,password:123,email:3@qq.com,'avatar':文件对象}
            print(data)
            models.UserInfo.objects.create_user(**data)
            # res['url']='/index/'
            res['url']='http://www.baidu.com'
            '''
            FileField自动干了这个事
            with open('media/avatar/%s'%file.name,'wb') as f:
                for line in file:
                    f.write(line)
            path='media/avatar/%s'%file.name
        
            '''
            # models.UserInfo.objects.create_user(username=username,password=password,email=email,avatar=path)
            # 返回,会被ajax接收到
            return JsonResponse(res)
        else:
            # 数据校验不通过
            res['code'] = 101  # 101表示注册失败
            res['msg'] = '数据验证失败'
            res['error'] = form.errors
            return JsonResponse(res)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值