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

  • 表创建及同步
  • 注册功能
    • forms组件
    • 用户头像前端实时展示
    • ajax
  • 登陆功能
    • 自己实现图片验证码
    • ajax
  • 搭建bbs首页
    • 导航条根据用户是否登陆展示不同的内容

数据库表创建及同步

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

注册功能

  1. """
  2. 我们之前是直接在views.py中书写的forms组件代码
  3. 但是为了接耦合 应该将所有的forms组件代码单独写到一个地方
  4. 如果你的项目至始至终只用到一个forms组件那么你可以直接建一个py文件书写即可
  5. myforms.py
  6. 但是如果你的项目需要使用多个forms组件,那么你可以创建一个文件夹在文件夹内根据
  7. forms组件功能的不同创建不同的py文件
  8. myforms文件夹
  9. regform.py
  10. loginform.py
  11. userform.py
  12. orderform.py
  13. ...
  14. """
  15. def register(request):
  16. form_obj = MyRegForm()
  17. if request.method == 'POST':
  18. back_dic = {"code": 1000, 'msg': ''}
  19. # 校验数据是否合法
  20. form_obj = MyRegForm(request.POST)
  21. # 判断数据是否合法
  22. if form_obj.is_valid():
  23. # print(form_obj.cleaned_data) # {'username': 'jason', 'password': '123', 'confirm_password': '123', 'email': '123@qq.com'}
  24. clean_data = form_obj.cleaned_data # 将校验通过的数据字典赋值给一个变量
  25. # 将字典里面的confirm_password键值对删除
  26. clean_data.pop('confirm_password') # {'username': 'jason', 'password': '123', 'email': '123@qq.com'}
  27. # 用户头像
  28. file_obj = request.FILES.get('avatar')
  29. """针对用户头像一定要判断是否传值 不能直接添加到字典里面去"""
  30. if file_obj:
  31. clean_data['avatar'] = file_obj
  32. # 直接操作数据库保存数据
  33. models.UserInfo.objects.create_user(**clean_data)
  34. back_dic['url'] = '/login/'
  35. else:
  36. back_dic['code'] = 2000
  37. back_dic['msg'] = form_obj.errors
  38. return JsonResponse(back_dic)
  39. return render(request,'register.html',locals())
  40. <script>
  41. $("#myfile").change(function () {
  42. // 文件阅读器对象
  43. // 1 先生成一个文件阅读器对象
  44. let myFileReaderObj = new FileReader();
  45. // 2 获取用户上传的头像文件
  46. let fileObj = $(this)[0].files[0];
  47. // 3 将文件对象交给阅读器对象读取
  48. myFileReaderObj.readAsDataURL(fileObj) // 异步操作 IO操作
  49. // 4 利用文件阅读器将文件展示到前端页面 修改src属性
  50. // 等待文件阅读器加载完毕之后再执行
  51. myFileReaderObj.onload = function(){
  52. $('#myimg').attr('src',myFileReaderObj.result)
  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. // 3.发送ajax请求
  67. $.ajax({
  68. url:"",
  69. type:'post',
  70. data:formDataObj,
  71. // 需要指定两个关键性的参数
  72. contentType:false,
  73. processData:false,
  74. success:function (args) {
  75. if (args.code==1000){
  76. // 跳转到登陆页面
  77. window.location.href = args.url
  78. }else{
  79. // 如何将对应的错误提示展示到对应的input框下面
  80. // forms组件渲染的标签的id值都是 id_字段名
  81. $.each(args.msg,function (index,obj) {
  82. {#console.log(index,obj) // username ["用户名不能为空"]#}
  83. let targetId = '#id_' + index;
  84. $(targetId).next().text(obj[0]).parent().addClass('has-error')
  85. })
  86. }
  87. }
  88. })
  89. })
  90. // 给所有的input框绑定获取焦点事件
  91. $('input').focus(function () {
  92. // 将input下面的span标签和input外面的div标签修改内容及属性
  93. $(this).next().text('').parent().removeClass('has-error')
  94. })
  95. </script>
  96. ### 扩展
  97. """
  98. 一般情况下我们在存储用户文件的时候为了避免文件名冲突的情况
  99. 会自己给文件名加一个前缀
  100. uuid
  101. 随机字符串
  102. ...
  103. """

登陆功能

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

Django---进阶13的更多相关文章

  1. Python之路,Day16 - Django 进阶

    Python之路,Day16 - Django 进阶   本节内容 自定义template tags 中间件 CRSF 权限管理 分页 Django分页 https://docs.djangoproj ...

  2. Django进阶篇【1】

    注:本篇是Django进阶篇章,适合人群:有Django基础,关于Django基础篇,将在下一章节中补充! 首先我们一起了解下Django整个请求生命周期: Django 请求流程,生命周期: 路由部 ...

  3. python 自动化之路 day 20 Django进阶/BBS项目【一】

    一.django进阶 1.django orm 增删改查 1.1.创建表: 1 2 3 >>> from blog.models import Blog >>> b ...

  4. [.net 面向对象程序设计进阶] (13) 序列化(Serialization)(五) Json 序列化利器 Newtonsoft.Json 及 通用Json类

    [.net 面向对象程序设计进阶] (13) 序列化(Serialization)(五) Json 序列化利器 Newtonsoft.Json 及 通用Json类 本节导读: 关于JSON序列化,不能 ...

  5. django进阶补充

    前言: 这篇博客对上篇博客django进阶作下补充. 一.效果图 前端界面较简单(丑),有两个功能: 从数据库中取出书名 eg: 新书A 在form表单输入书名,选择出版社,选择作者(多选),输入完毕 ...

  6. django进阶-3

    先看效果图: 登陆admin后的界面: 查看作者: 当然你也可以定制admin, 使界面更牛逼 数据库表结构: app01/models.py from django.db import models ...

  7. django进阶-4

    前言: 下篇博客写关于bootstrap... 一.如何在脚本测试django from django.db import models class Blog(models.Model): name ...

  8. Django进阶知识

    drf学习之Django进阶点 一.Django migrations原理 1.makemigrattions: 相当于在每个app下的migrations文件夹下生成一个py脚本文件用于创建表或则修 ...

  9. django进阶-查询(适合GET4以上人群阅读)

    前言: 下篇博客写关于bootstrap... 一.如何在脚本测试django from django.db import models class Blog(models.Model): name ...

  10. django进阶-modelform&admin action

    先看效果图: 登陆admin后的界面: 查看作者: 当然你也可以定制admin, 使界面更牛逼 数据库表结构: app01/models.py from django.db import models ...

随机推荐

  1. apollo与springboot集成实现动态刷新配置

    分布式apollo简介 Apollo(阿波罗)是携程框架部门研发的开源配置管理中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限.流程治理等特性. 本 ...

  2. JVM中堆的介绍

    一.堆的概述 一个JVM实例只有一个堆内存,堆也是Java内存管理的核心区域,堆在JVM启动的时候创建,其空间大小也被创建,是JVM中最大的一块内存空间,所有线程共享Java堆,物理上不连续的逻辑上连 ...

  3. 查询局域网指定段内存活IP

    目录 批量ping 输出到指定文件 批量ping for /L %i IN (起始,扫描间距,结束) DO ping -w 2 -n 1 10.224.131.%i 如 for /L %i IN (5 ...

  4. 在Asp.NET Core中如何优雅的管理用户机密数据

    在Asp.NET Core中如何优雅的管理用户机密数据 背景 回顾 在软件开发过程中,使用配置文件来管理某些对应用程序运行中需要使用的参数是常见的作法.在早期VB/VB.NET时代,经常使用.ini文 ...

  5. c# 不同单例的不同意义

    前言 在c#,可能有很多五花八门的单例给你选择,分什么懒汉模式等等什么模式,其实不同的写法对程序是有一定影响的. 正文 为什么需要单例呢?其实我们自己是可以控制单例的,只是单例模式给了我们一个好的设计 ...

  6. Magic Line【坐标点排序方法】

    Magic Line 题目链接(传送门) 来源:牛客网 题目描述 There are always some problems that seem simple but is difficult to ...

  7. Spark GraphX从入门到实战

      第1章 Spark GraphX 概述 1.1 什么是 Spark GraphX   Spark GraphX 是一个分布式图处理框架,它是基于 Spark 平台提供对图计算和图挖掘简洁易用的而丰 ...

  8. Android学习笔记ActionView

    概念 案例 1.布局文件 activity_main.xml <?xml version="1.0" encoding="utf-8"?> < ...

  9. position中的四种属性

    Position有四个属性值,分别是static .fixed. relative .absolute. 第一个属性值是static,这是position的默认属性,一般我们都不会用到它,所以也很少提 ...

  10. 【原创】强撸基于 .NET 的 Redis Cluster 集群访问组件

    Hello 大家好,我是TANZAME,我们又见面了.今天我们来聊聊怎么手撸一个 Redis Cluster 集群客户端,纯手工有干货,您细品. 随着业务增长,线上环境的QPS暴增,自然而然将当前的单 ...