• 实战之BBS(仿博客园写一个论坛)(一)


    需要知识:python,Django框架,前端HTML,CSS,JS,JQuery,Bootstarp,Django的ORM,Auth

    一.数据库表的分析:

    1.首先是用户表,用户表继承Django自带的Auth_user表,目的是可以使用Django封装好的方法,不用自己手动写cookie和session操作,还可以自定义添加字段

    2.个人站点表,和用户是一对一的关系,每个用户都有一个站点(不要脸的打广告)

    3.文章表,字段有标题和内容,并且还应该有文章的分类和标签,文章只能有一个分类,比如说这篇文章是Python,那篇是前端,但是一个文章可以有多个标签,所以应该还有文章分类表(和文章表是一对多的关系),文章标签表(和文章表是一对多的关系)

    4.文章分类表

    5.文章标签表

    6.评论表,和用户是一对一的关系

    7.论坛怎么能没有点赞评论,所以加一个点赞点踩表,一个用户一篇文章只能点一次赞或点一次踩,所以是一对多关系

    画个图:

    二.表的建立

    因为懒,所以直接使用Django自带的sqllite数据库

    from django.db import models
    #导入AbstractUser类,继承之后,Django自带的Auth_user就被替换了
    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)
       #用户上传头像,upload_to是头像保存的位置,default是用户默认的头像   avatar=models.FileField(upload_to='avatar/',default='avatar/default.jpg')
        # 关联个人页表
        blog=models.OneToOneField(to='Blog',null=True)
    class Blog(models.Model):
        site_name=models.CharField(max_length=32)
        site_tittle=models.CharField(max_length=32)
        theme=models.CharField(max_length=32)
    
    class Category(models.Model):
        name = models.CharField(max_length=32)
       #关联外键
        blog = models.ForeignKey(to='Blog')
    
    
    class Tag(models.Model):
        name = models.CharField(max_length=32)
       #关联外键
        blog = models.ForeignKey(to='Blog')
    
    
    class Article(models.Model):
        title = models.CharField(max_length=32)
        desc = models.CharField(max_length=256)
        # 存大段文本
        content = models.TextField()
        create_time = models.DateField(auto_now_add=True)
        # 文章的评论数,点赞数,点踩数,虽然有评论表,点赞表,点踩表,但是每次获取数据都是一次查询,速度会很慢,所以直接在文章表里添加
        comment_num = models.IntegerField(default=0)
        up_num = models.IntegerField(default=0)
        down_num = models.IntegerField(default=0)
    
        blog = models.ForeignKey(to='Blog',null=True)
        category = models.ForeignKey(to='Category',null=True)
        #半自动创建中间表,这样我们就可以自定义中间表的字段啦
        tags = models.ManyToManyField(to='Tag',through='Article2Tag',through_fields=('article','tag'))
    
    #中间表
    class Article2Tag(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')
        is_up = models.BooleanField()
    
    
    class Comment(models.Model):
        user = models.ForeignKey(to='UserInfo')
        article = models.ForeignKey(to='Article')
        content = models.CharField(max_length=128)
       #自己是自己的外键,回复如果有其他回复,那么他的parent就是第一个楼层,否则默认是null
        parent = models.ForeignKey(to='self',null=True)
    建表语句

    还要在setting文件里:添加你继承AbstractUser的表名

    AUTH_USER_MODEL='app01.UserInfo'

    建表语句完成,makemigrations,migrate

    数据库完成

    三.注册页面

    完成效果图:

    第一步:forms类的搭建

    上篇随便写了,我就不啰嗦了。。。(

    from django import forms
    from app01 import models
    from django.forms import widgets
    class RegForm(forms.Form):
        username=forms.CharField(max_length='8',min_length=3,label='用户名',error_messages={
            'max_length':'最长为8位',
            'min_length':'最短为3位',
            'required':'用户名不能为空'
        },widget=widgets.TextInput(attrs={'class':'form-control'}))
        password = forms.CharField(max_length='8', min_length=3, label='密码', error_messages={
            'max_length': '密码最长为8位',
            'min_length': '密码最短为3位',
            'required': '密码不能为空'
        }, widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
        re_password = forms.CharField(max_length='8', min_length=3, label='重复密码', error_messages={
            'max_length': '密码最长为8位',
            'min_length': '密码最短为3位',
            'required': '密码不能为空'
        }, 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)
            if user_obj:
                self.add_error('username','用户名已经存在')
            else:
                return username
        def clean(self):
            password=self.cleaned_data.get('password')
            re_password=self.cleaned_data.get('re_password')
            if password==re_password:
                return self.cleaned_data
            else:
                self.add_error('re_password','两次密码不一致')

    第二步:前端页面的搭建

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
        <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    </head>
    <body>
    <!--类是Bootstarp提供的类,可以让页面变得美观,不用也没关系-->
    
    <div class="container">
        <div class="row">
            <h1 class="text-center">注册</h1>
            <div class="col-md-6 col-md-offset-3">
                <form action="" id="myform" method="post">
                    <!-- 注意一定要写 {% csrf_token %},不然会报403,当然你可以在中间件中关掉csrf,但是那样就不能防止csrf跨站伪造了-->
                    {% csrf_token %}
                    <!--Django的模板语法 form_obj是forms组件对象,请看后端代码 -->
                    {% for foo in form_obj %}
                        <div class="form-group">
                            <p><label for="{{ foo.auto_id }}">{{ foo.label }}</label>{{ foo }}
                         <!--行元素由其本身大小决定,所以当你没有错误的时候,不占位置 -->
                                <span class="pull-right" style="color: red">
                        </span>
                            </p>
                        </div>
                    {% endfor %}
                    <div>
                      <!-- label 指向了下面的input标签,这样就可以点击头像或者图片提交图片了-->
                        <label for="myfile">头像
                            <img id="img_01" src="/static/default.jpg" alt="" style=" 80px;margin-left: 50px">
                        </label>
                     <!--我懒,所以直接在标签内写了样式,把原本的标签隐藏,原本的太丑了 -->
                        <input type="file" id="myfile" style="display: none">
                    </div>
                   <!-- 注意,这里type是button,为下面的ajax做准备,如果是submit,那就直接提交了,会刷新页面-->
                    <input id='mybutton' type="button" value="注册" class="pull-right btn-primary btn">
                </form>
            </div>
        </div>
    </div>
    </body>

    第三步:后端代码

    def register(request):
       #自定义状态码,等会ajax要用到
        respone_code={'code':100,'msg':''}
       #初始化forms组件
        form_obj=myforms.RegForm()
    #渲染注册页面,locals()把from_obj,response_code传到了前端,起码目前是这样 return render(request,'register.html',locals())

    当然了,肯定不能就这么点,这是get请求的时候进行的操作,等写完ajax再回来继续

    第四步:javascript和Ajax

    1.首先先把简单的头像上传搞定:

     这是初始的默认头像,现在实现的功能是上传头像之后,变成这样

    #这是jq,也可以用js写,但是比较麻烦
        #为input框添加事件,如果改变,那么。。
        $('#myfile').change(function () {
            #获取该文件,写法是固定写法
            var file = $(this)[0].files[0];
            #要用到文件阅读,实例化一下
            var read = new FileReader();
            #添加到里面
            read.readAsDataURL(file);
            #如果不加read.onload,图片不会显示,因为读取速度远慢于函数执行速度
            read.onload = function () {
                 #更改属性
                $('#img_01').attr('src', read.result);
            }
        });

    好了,现在头像搞定了

    现在搞比较困难的ajax异步提交:

     $('#mybutton').click(function () {
            {#实例化一个FormData对象#}
            var formData = new FormData();
            {#$('#myform').serializeArray()是form表单中的所有数据,自动获取form表单中所有input框键值对,用each循环取出来#}
            $.each($('#myform').serializeArray(), function (index, obj) {
                {#只是添加了普通的键值对,文件对象需要你手动添加#}
                formData.append(obj.name,obj.value);
            });
             {#手动添加文件对象#}
            formData.append('myfile', $('#myfile')[0].files[0]);
            {#ajax操作,现在不用看函数#}
            $.ajax({
                url: '',
                type: 'post',
                 {#因为有文件,所以要用formData#}
                data: formData,
                processData: false,
                contentType: false,
                success: function (data) {
                    if (data.code == 100) {
                        location.href = data.url
                    }
                    else {
                        $.each(data.msg, function (index, obj) {
                            var tagId = '#id_' + index;
                            $(tagId).next().html(obj[0]).parent().parent().addClass('has-error')
    
                        })
                    }
                }
            })
        });

     那么后端代码也要写相应的post请求时的代码了:

    完整后端代码:

    def register(request):
        respone_code={'code':100,'msg':''}
        form_obj=myforms.RegForm()
        if request.method=='POST':
            #Ajax请求,实例化forms组件
            form_obj = myforms.RegForm(request.POST)
           #如果校验没有问题
            if form_obj.is_valid():
                cleaned_data=form_obj.cleaned_data
                #数据库中的userinfo表中没有re_password所以把这个字段丢掉
                cleaned_data.pop('re_password')
                #看看有没有新头像,没有就不添加,直接使用默认的头像,减少数据库的工作量
                atatar=request.FILES.get('myfile')
                if atatar:
                    cleaned_data['avatar'] = atatar
                #插入数据
                models.UserInfo.objects.create_user(**cleaned_data)
                #修改respone_code['msg'],并且添加url,如果注册成功,直接跳到登录页面
                respone_code['msg']='注册成功'
                respone_code['url']='/login'
            else:
                #如果有校验错误,code变为101,同时msg改为错误信息。错误信息是一个个的键值对
                respone_code['code']=101
                respone_code['msg']=form_obj.errors
            #不管校验成功与否都返回一个字典
            return JsonResponse(respone_code)
        return render(request,'register.html',locals())

     这个时候再来看看ajax的函数:

     success: function (data) {
                    if (data.code == 100) {
                      #如果成功,那么跳转到url指定的路径
                        location.href = data.url
                    }
                    else {
                     # // 手动拼接处forms组件渲染的input的id值     id_字段的特点
                        $.each(data.msg, function (index, obj) {
                            var tagId = '#id_' + index;
                            $(tagId).next().html(obj[0]).parent().parent().addClass('has-error')
    
                        })

     注册页面完成

  • 相关阅读:
    Socket开发框架之消息的回调处理
    Socket开发框架之数据加密及完整性检查
    Socket开发框架之数据传输协议
    Socket开发框架之框架设计及分析
    C#进行Visio二次开发之文件导出及另存Web页面
    Winform混合式开发框架的特点总结
    代码生成工具Database2Sharp中增加视图的代码生成以及主从表界面生成功能
    基于Metronic的Bootstrap开发框架经验总结(9)--实现Web页面内容的打印预览和保存操作
    基于C#的MongoDB数据库开发应用(4)--Redis的安装及使用
    基于C#的MongoDB数据库开发应用(3)--MongoDB数据库的C#开发之异步接口
  • 原文地址:https://www.cnblogs.com/98WDJ/p/10765294.html
Copyright © 2020-2023  润新知