zoukankan      html  css  js  c++  java
  • 72-75-django-BBS项目开发

    今日内容概要

    bbs是一个前后端不分离的全栈项目,前端和后端都需要我们自己一步步的完成

    • 表创建及同步
    • 注册功能
      • forms组件
      • 用户头像前端实时展示
      • ajax
    • 登陆功能
      • 自己实现图片验证码
      • ajax

    今日内容详细

    BBS表关系图解

    数据库表创建及同步

     1 """
     2 由于django自带的sqlite数据库对日期不敏感,所以我们换成MySQL
     3 """
     4 from django.db import models
     5 
     6 # Create your models here.
     7 """
     8 先写普通字段
     9 之后再写外键字段
    10 """
    11 from django.contrib.auth.models import AbstractUser
    12 
    13 
    14 class UserInfo(AbstractUser):
    15     phone = models.BigIntegerField(verbose_name='手机号',null=True)
    16     # 头像
    17     avatar = models.FileField(upload_to='avatar/',default='avatar/default.png',verbose_name='用户头像')
    18     """
    19     给avatar字段传文件对象 该文件会自动存储到avatar文件下 然后avatar字段只保存文件路径avatar/default.png
    20     """
    21     create_time = models.DateField(auto_now_add=True)
    22 
    23     blog = models.OneToOneField(to='Blog',null=True)
    24 
    25 
    26 class Blog(models.Model):
    27     site_name = models.CharField(verbose_name='站点名称',max_length=32)
    28     site_title = models.CharField(verbose_name='站点标题',max_length=32)
    29     # 简单模拟 带你认识样式内部原理的操作
    30     site_theme = models.CharField(verbose_name='站点样式',max_length=64)  # 存css/js的文件路径
    31 
    32 
    33 class Category(models.Model):
    34     name = models.CharField(verbose_name='文章分类',max_length=32)
    35     blog = models.ForeignKey(to='Blog',null=True)
    36 
    37 
    38 class Tag(models.Model):
    39     name = models.CharField(verbose_name='文章标签',max_length=32)
    40     blog = models.ForeignKey(to='Blog', null=True)
    41 
    42 
    43 class Article(models.Model):
    44     title = models.CharField(verbose_name='文章标题',max_length=64)
    45     desc = models.CharField(verbose_name='文章简介',max_length=255)
    46     # 文章内容有很多 一般情况下都是使用TextField
    47     content = models.TextField(verbose_name='文章内容')
    48     create_time = models.DateField(auto_now_add=True)
    49 
    50     # 数据库字段设计优化
    51     up_num = models.BigIntegerField(verbose_name='点赞数',default=0)
    52     down_num = models.BigIntegerField(verbose_name='点踩数',default=0)
    53     comment_num = models.BigIntegerField(verbose_name='评论数',default=0)
    54 
    55     # 外键字段
    56     blog = models.ForeignKey(to='Blog', null=True)
    57     category = models.ForeignKey(to='Category',null=True)
    58     tags = models.ManyToManyField(to='Tag',
    59                                   through='Article2Tag',
    60                                   through_fields=('article','tag')
    61                                   )
    62 
    63 
    64 class Article2Tag(models.Model):
    65     article = models.ForeignKey(to='Article')
    66     tag = models.ForeignKey(to='Tag')
    67 
    68 
    69 class UpAndDown(models.Model):
    70     user = models.ForeignKey(to='UserInfo')
    71     article = models.ForeignKey(to='Article')
    72     is_up = models.BooleanField()  # 传布尔值 存0/1
    73 
    74 
    75 class Comment(models.Model):
    76     user = models.ForeignKey(to='UserInfo')
    77     article = models.ForeignKey(to='Article')
    78     content = models.CharField(verbose_name='评论内容',max_length=255)
    79     comment_time = models.DateTimeField(verbose_name='评论时间',auto_now_add=True)
    80     # 自关联
    81     parent = models.ForeignKey(to='self',null=True)  # 有些评论就是根评论
    数据库表创建及同步

    注册功能

    myforms.py

     1 # 书写针对用户表的forms组件代码
     2 from django import forms
     3 from app01 import models
     4 
     5 
     6 class MyRegForm(forms.Form):
     7     username = forms.CharField(label='用户名', min_length=3, max_length=8,
     8                                error_messages={
     9                                    'required': '用户名不能为空',
    10                                    'min_length': "用户名最少3位",
    11                                    'max_length': "用户名最大8位"
    12                                },
    13                                # 还需要让标签有bootstrap样式
    14                                widget=forms.widgets.TextInput(attrs={'class': 'form-control'})
    15                                )
    16 
    17     password = forms.CharField(label='密码', min_length=3, max_length=8,
    18                                error_messages={
    19                                    'required': '密码不能为空',
    20                                    'min_length': "密码最少3位",
    21                                    'max_length': "密码最大8位"
    22                                },
    23                                # 还需要让标签有bootstrap样式
    24                                widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'})
    25                                )
    26 
    27     confirm_password = forms.CharField(label='确认密码', min_length=3, max_length=8,
    28                                        error_messages={
    29                                            'required': '确认密码不能为空',
    30                                            'min_length': "确认密码最少3位",
    31                                            'max_length': "确认密码最大8位"
    32                                        },
    33                                        # 还需要让标签有bootstrap样式
    34                                        widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'})
    35                                        )
    36     email = forms.EmailField(label='邮箱',
    37                              error_messages={
    38                                  'required': '邮箱不能为空',
    39                                  'invalid': '邮箱格式不正确'
    40                              },
    41                              widget=forms.widgets.EmailInput(attrs={'class': 'form-control'})
    42                              )
    43 
    44     # 钩子函数
    45     # 局部钩子:校验用户名是否已存在
    46     def clean_username(self):
    47         username = self.cleaned_data.get('username')
    48         # 去数据库中校验
    49         is_exist = models.UserInfo.objects.filter(username=username)
    50         if is_exist:
    51             # 提示信息
    52             self.add_error('username', '用户名已存在')
    53         return username
    54 
    55     # 全局钩子:校验两次是否一致
    56     def clean(self):
    57         password = self.cleaned_data.get('password')
    58         confirm_password = self.cleaned_data.get('confirm_password')
    59         if not password == confirm_password:
    60             self.add_error('confirm_password', '两次密码不一致')
    61         return self.cleaned_data
    myforms.py

    views.py

     1 """
     2 我们之前是直接在views.py中书写的forms组件代码
     3 但是为了接耦合 应该将所有的forms组件代码单独写到一个地方
     4 
     5 如果你的项目至始至终只用到一个forms组件那么你可以直接建一个py文件书写即可
     6     myforms.py
     7 但是如果你的项目需要使用多个forms组件,那么你可以创建一个文件夹在文件夹内根据
     8 forms组件功能的不同创建不同的py文件
     9     myforms文件夹
    10         regform.py
    11         loginform.py
    12         userform.py
    13         orderform.py
    14         ...
    15 """
    16 def register(request):
    17     form_obj = MyRegForm()
    18     if request.method == 'POST':
    19         back_dic = {"code": 1000, 'msg': ''}
    20         # 校验数据是否合法
    21         form_obj = MyRegForm(request.POST)
    22         # 判断数据是否合法
    23         if form_obj.is_valid():
    24             # print(form_obj.cleaned_data)  # {'username': 'jason', 'password': '123', 'confirm_password': '123', 'email': '123@qq.com'}
    25             clean_data = form_obj.cleaned_data  # 将校验通过的数据字典赋值给一个变量
    26             # 将字典里面的confirm_password键值对删除
    27             clean_data.pop('confirm_password')  # {'username': 'jason', 'password': '123', 'email': '123@qq.com'}
    28             # 用户头像
    29             file_obj = request.FILES.get('avatar')
    30             """针对用户头像一定要判断是否传值 不能直接添加到字典里面去"""
    31             if file_obj:
    32                 clean_data['avatar'] = file_obj
    33             # 直接操作数据库保存数据
    34             models.UserInfo.objects.create_user(**clean_data)
    35             back_dic['url'] = '/login/'
    36         else:
    37             back_dic['code'] = 2000
    38             back_dic['msg'] = form_obj.errors
    39         return JsonResponse(back_dic)
    40     return render(request,'register.html',locals())
    41 
    42 
    43 # 扩展
    44 """
    45 一般情况下我们在存储用户文件的时候为了避免文件名冲突的情况
    46 会自己给文件名加一个前缀    
    47     uuid
    48     随机字符串
    49     ...
    50 """
    views.py

    register.html

      1 <!DOCTYPE html>
      2 <html lang="en">
      3 <head>
      4     <meta charset="UTF-8">
      5     <title>注册</title>
      6     <meta name="viewport" content="width=device-width, initial-scale=1">
      7     <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
      8     <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
      9     <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
     10 </head>
     11 <body>
     12 <div class="container-fluid">
     13     <div class="row">
     14         <div class="col-md-8 col-md-offset-2">
     15             <h1 class="text-center">注册</h1>
     16             <form id="myform">  <!--这里我们不用form表单提交数据 知识单纯的用一下form标签而已-->
     17                 {% csrf_token %}
     18                 {% for form in form_obj %}
     19                     <div class="form-group">
     20                         <label for="{{ form.auto_id }}">{{ form.label }}</label>
     21                         {{ form }}
     22                         <span style="color: red" class="pull-right"></span>
     23                     </div>
     24                 {% endfor %}
     25                 <div class="form-group">
     26                     <label for="myfile">头像
     27                         {% load static %}
     28                         <img src="{% static 'img/default.png' %}" id='myimg' alt="" width="100" style="margin-left: 10px">
     29                     </label>
     30                     <input type="file" id="myfile" name="avatar" style="display: none" >
     31                 </div>
     32 
     33                 <input type="button" class="btn btn-primary pull-right" value="注册" id="id_commit">
     34             </form>
     35         </div>
     36     </div>
     37 </div>
     38 
     39 <script>
     40     $("#myfile").change(function () {
     41         // 文件阅读器对象
     42         // 1 先生成一个文件阅读器对象
     43         let myFileReaderObj = new FileReader();
     44         // 2 获取用户上传的头像文件
     45         let fileObj = $(this)[0].files[0];
     46         // 3 将文件对象交给阅读器对象读取
     47         myFileReaderObj.readAsDataURL(fileObj)  // 异步操作  IO操作
     48         // 4 利用文件阅读器将文件展示到前端页面  修改src属性
     49         // 等待文件阅读器加载完毕之后再执行
     50         myFileReaderObj.onload = function(){
     51              $('#myimg').attr('src',myFileReaderObj.result)
     52         }
     53     })
     54 
     55     $('#id_commit').click(function () {
     56         // 发送ajax请求     我们发送的数据中即包含普通的键值也包含文件
     57         let formDataObj = new FormData();
     58         // 1.添加普通的键值对
     59         {#console.log($('#myform').serializeArray())  // [{},{},{},{},{}]  只包含普通键值对#}
     60         $.each($('#myform').serializeArray(),function (index,obj) {
     61             {#console.log(index,obj)#}  // obj = {}
     62             formDataObj.append(obj.name,obj.value)
     63         });
     64         // 2.添加文件数据
     65         formDataObj.append('avatar',$('#myfile')[0].files[0]);
     66 
     67         // 3.发送ajax请求
     68         $.ajax({
     69             url:"",
     70             type:'post',
     71             data:formDataObj,
     72 
     73             // 需要指定两个关键性的参数
     74             contentType:false,
     75             processData:false,
     76 
     77             success:function (args) {
     78                 if (args.code==1000){
     79                     // 跳转到登陆页面
     80                     window.location.href = args.url
     81                 }else{
     82                     // 如何将对应的错误提示展示到对应的input框下面
     83                     // forms组件渲染的标签的id值都是 id_字段名
     84                     $.each(args.msg,function (index,obj) {
     85                         {#console.log(index,obj)  //  username        ["用户名不能为空"]#}
     86                         let targetId = '#id_' + index;
     87                         $(targetId).next().text(obj[0]).parent().addClass('has-error')
     88                     })
     89                 }
     90             }
     91         })
     92     })
     93     // 给所有的input框绑定获取焦点事件
     94     $('input').focus(function () {
     95         // 将input下面的span标签和input外面的div标签修改内容及属性
     96         $(this).next().text('').parent().removeClass('has-error')
     97     })
     98 </script>
     99 </body>
    100 </html>
    register.html

    登录功能

    views.py

     1 """
     2 img标签的src属性
     3     1.图片路径
     4     2.url
     5     3.图片的二进制数据
     6 
     7 我们的计算机上面致所有能够输出各式各样的字体样式
     8 内部其实对应的是一个个.ttf结尾的文件
     9 
    10 http://www.zhaozi.cn/ai/2019/fontlist.php?ph=1&classid=32&softsq=%E5%85%8D%E8%B4%B9%E5%95%86%E7%94%A8
    11 """
    12 
    13 
    14 def login(request):
    15     return render(request,'login.html')
    16 
    17 """
    18 图片相关的模块
    19     pip3 install pillow
    20 """
    21 from PIL import Image,ImageDraw,ImageFont
    22 """
    23 Image:生成图片
    24 ImageDraw:能够在图片上乱涂乱画
    25 ImageFont:控制字体样式
    26 """
    27 from io import BytesIO,StringIO
    28 """
    29 内存管理器模块
    30 BytesIO:临时帮你存储数据 返回的时候数据是二进制
    31 StringIO:临时帮你存储数据 返回的时候数据是字符串
    32 """
    33 import random
    34 def get_random():
    35     return random.randint(0,255),random.randint(0,255),random.randint(0,255)
    36 def get_code(request):
    37     # 推导步骤1:直接获取后端现成的图片二进制数据发送给前端
    38     # with open(r'static/img/111.jpg','rb') as f:
    39     #     data = f.read()
    40     # return HttpResponse(data)
    41 
    42     # 推导步骤2:利用pillow模块动态产生图片
    43     # img_obj = Image.new('RGB',(430,35),'green')
    44     # img_obj = Image.new('RGB',(430,35),get_random())
    45     # # 先将图片对象保存起来
    46     # with open('xxx.png','wb') as f:
    47     #     img_obj.save(f,'png')
    48     # # 再将图片对象读取出来
    49     # with open('xxx.png','rb') as f:
    50     #     data = f.read()
    51     # return HttpResponse(data)
    52 
    53     # 推导步骤3:文件存储繁琐IO操作效率低  借助于内存管理器模块
    54     # img_obj = Image.new('RGB', (430, 35), get_random())
    55     # io_obj = BytesIO()  # 生成一个内存管理器对象  你可以看成是文件句柄
    56     # img_obj.save(io_obj,'png')
    57     # return HttpResponse(io_obj.getvalue())  # 从内存管理器中读取二进制的图片数据返回给前端
    58 
    59 
    60     # 最终步骤4:写图片验证码
    61     img_obj = Image.new('RGB', (430, 35), get_random())
    62     img_draw = ImageDraw.Draw(img_obj)  # 产生一个画笔对象
    63     img_font = ImageFont.truetype('static/font/222.ttf',30)  # 字体样式 大小
    64 
    65     # 随机验证码  五位数的随机验证码  数字 小写字母 大写字母
    66     code = ''
    67     for i in range(5):
    68         random_upper = chr(random.randint(65,90))
    69         random_lower = chr(random.randint(97,122))
    70         random_int = str(random.randint(0,9))
    71         # 从上面三个里面随机选择一个
    72         tmp = random.choice([random_lower,random_upper,random_int])
    73         # 将产生的随机字符串写入到图片上
    74         """
    75         为什么一个个写而不是生成好了之后再写
    76         因为一个个写能够控制每个字体的间隙 而生成好之后再写的话
    77         间隙就没法控制了
    78         """
    79         img_draw.text((i*60+60,-2),tmp,get_random(),img_font)
    80         # 拼接随机字符串
    81         code += tmp
    82     print(code)
    83     # 随机验证码在登陆的视图函数里面需要用到 要比对 所以要找地方存起来并且其他视图函数也能拿到
    84     request.session['code'] = code
    85     io_obj = BytesIO()
    86     img_obj.save(io_obj,'png')
    87     return HttpResponse(io_obj.getvalue())
    views.py

    login.html

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <title>登录</title>
     6     <meta name="viewport" content="width=device-width, initial-scale=1">
     7     <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
     8     <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
     9     <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    10     {% load static %}
    11 </head>
    12 <body>
    13 <div class="container-fluid">
    14     <div class="row">
    15         <div class="col-md-8 col-md-offset-2">
    16             <h1 class="text-center">登陆</h1>
    17             <div class="form-group">
    18                 <label for="username">用户名</label>
    19                 <input type="text" name="username" id="username" class="form-control">
    20             </div>
    21             <div class="form-group">
    22                 <label for="password">密码</label>
    23                 <input type="password" name="password" id="password" class="form-control">
    24             </div>
    25             <div class="form-group">
    26                 <label for="">验证码</label>
    27 
    28                 <div class="row">
    29                     <div class="col-md-6">
    30                         <input type="text" name="code" id="id_code" class="form-control">
    31                     </div>
    32                     <div class="col-md-6">
    33                         <img src="/get_code/" alt="" width="430" height="35" id="id_img">
    34                     </div>
    35                 </div>
    36 
    37             </div>
    38             <input type="button" class="btn btn-success" value="登陆">
    39         </div>
    40     </div>
    41 </div>
    42 <script>
    43     $("#id_img").click(function () {
    44         // 1 先获取标签之前的src
    45         let oldVal = $(this).attr('src');
    46         $(this).attr('src',oldVal += '?')
    47     })
    48 </script>
    49 </body>
    50 </html>
    login.html
  • 相关阅读:
    安全管道工具SSF
    Dumpzilla工具第615行bug的解决办法
    火狐浏览器信息提取工具Dumpzilla
    通过构造函数来创建新对象
    利用canvas绘制序列帧动画
    canvas的图片绘制案例
    使用canvas绘制饼状图
    模仿制作京东的侧边提示栏
    使用canvas绘制扇形图
    使用canvas制作简单表格
  • 原文地址:https://www.cnblogs.com/wgwg/p/13089951.html
Copyright © 2011-2022 走看看