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. 在scrapy的spiders文件中设置请求时间间隔

    设置某个spider单独使用的设置项等等. 在spiders文件中写如下: custom_settings = { 'DOWNLOAD_DELAY': 0.2, 'CONCURRENT_REQUEST ...

  2. Azure AD(四)知识补充-服务主体

    一,引言 又到了新的一周了,也到了我新的分享的时间了,还记得上一周立得Flag,其中 “保证每周输出一篇文章” ,让我特别“在意”(这里用词不太恰当).主要是我的一个大学舍友,他突然问了我一个关于写博 ...

  3. 实验五 shell脚本编程

    项目 内容 这个作业属于哪个课程 课程链接 这个作业的要求在哪里 作业要求 学号-姓名 17041428-朱槐健 作业学习目标 1. 了解shell脚本的概念及使用 2.掌握shell脚本语言的基本语 ...

  4. 使用jdk1.8 stream特性对参数名称进行排序

    在对外对接的时候,通常会碰到签名方式, 然后签名的时候,要求按照参数名称进行排序. 比如参数为 c=22&a=1, 需要将结果排序为a=1&c=22, 然后再进行别的运算. 可以使用j ...

  5. SpringBoot爬坑系列

    1.日志篇 现象 由于日志配置采用原来SpringMVC项目中的log4j.properties 文件,日志采用springboot自带的jar包会出现打印不出日志的情况. 解决 引入原日志包 < ...

  6. Jquery封装: WebSocket插件

    1 $(function() { var websocket = null; //浏览器是否支持websocket if ("WebSocket" in window) { try ...

  7. Nice Jquery Validator 快速上手

    (1).直接引用 一行代码引入插件,local 参数用来加载对应的配置文件.如果不传 local 参数,配置以及样式就需要自行引入. <script src="path/to/nice ...

  8. php 替换模板中的 PHP源码标签字符方法

    //替换php代码function RepPhpAspJspcode($string){ global $public_r; if(!$public_r[candocode]){ //$string= ...

  9. @atcoder - ARC092F@ Two Faced Edges

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 给出一个有向图,对每条边都做一次询问: 翻转这条边后,对原图的强 ...

  10. Vue —— 精讲 VueRouter(1)

    最近被Boos调去给新人做培训去了,目前把自己整理的一些东西分享出来,希望对大家有所帮助 本章节为VueRouter前端 路由的章节部分 大纲 一.基本概念 路由就是通过网络把讯息从源地址传输到目的地 ...