1. 继承AbstractUser
  2. 扩展
  3. phone 电话号码
  4. avatar 用户头像
  5. create_time 创建时间
  6. 外键字段
  7. 一对一个人站点表

 

  1. site_name 站点名称
  2. site_title 站点标题
  3. site_theme 站点样式

 

  1. name 标签名
  2. 外键字段
  3. 一对多个人站点
  1. name 分类名
  2. 外键字段
  3. 一对多个人站点
  1. title 文章标题
  2. desc 文章简介
  3. content 文章内容
  4. create_time 发布时间

 

(虽然下述的三个字段可以从其他表里面跨表查询计算得出,但是频繁跨表效率)
up_num 点赞数
down_num 点踩数
comment_num 评论数

外键字段
一对多个人站点
多对多文章标签
一对多文章分类

  1. 记录哪个用户给哪篇文章点了赞还是点了踩
  2. user ForeignKey(to="User")
  3. article ForeignKey(to="Article")
  4. is_up BooleanField()
  1. 记录哪个用户给哪篇文章写了哪些评论内容
  2. user ForeignKey(to="User")
  3. article ForeignKey(to="Article")
  4. content CharField()
  5. comment_time DateField()
  6. # 自关联
  7. parent ForeignKey(to="Comment",null=True)
  8. # ORM专门提供的自关联写法
  9. parent ForeignKey(to="self",null=True)
  10. 7.评论表
  11. 根评论和子评论
  12. to=\'self\'

 

 modles.py

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

 

注册功能用到的技术点有以下

  • forms组件

  • 用户头像前端实时展示

  • ajax

我们之前写forms代码是直接写到views里面的 

为了贯彻面向对象思想,我们会把forms组件代码写到单独的一个py文件里,如果说这个项目从头到位就使用一个form组件,直接写在app下面的任意一个自己写的文件里就行,不过记得不要叫forms,这样在导入Django库里的form时会出现问题,可以叫myforms.py或者其他,只要补交forms就行

如果你项目中用到多个不同的forms组件时,那最好还是自己建一个文件夹,然后把对应的forms组件写上对应的名字,然后放在这个文件夹下面,这样也比较清晰

myforms.py

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

views.py的register视图函数

  1. def register(request):
  2. form_obj = MyRegForm()
  3. if request.method == \'POST\':
  4. back_dic = {"code": 1000, \'msg\': \'\'}
  5. # 校验数据是否合法
  6. form_obj = MyRegForm(request.POST)
  7. # 判断数据是否合法
  8. if form_obj.is_valid():
  9. # print(form_obj.cleaned_data) # {\'username\': \'jason\', \'password\': \'123\', \'confirm_password\': \'123\', \'email\': \'123@qq.com\'}
  10. clean_data = form_obj.cleaned_data # 将校验通过的数据字典赋值给一个变量
  11. # 将字典里面的confirm_password键值对删除
  12. clean_data.pop(\'confirm_password\') # {\'username\': \'jason\', \'password\': \'123\', \'email\': \'123@qq.com\'}
  13. # 用户头像
  14. file_obj = request.FILES.get(\'avatar\')
  15. """针对用户头像一定要判断是否传值 不能直接添加到字典里面去"""
  16. if file_obj:
  17. clean_data[\'avatar\'] = file_obj
  18. # 直接操作数据库保存数据
  19. models.UserInfo.objects.create_user(**clean_data)
  20. back_dic[\'url\'] = \'/login/\'
  21. else:
  22. back_dic[\'code\'] = 2000
  23. back_dic[\'msg\'] = form_obj.errors
  24. return JsonResponse(back_dic)
  25. return render(request,\'register.html\',locals())

register.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>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. $(\'#id_commit\').click(function () {
  55. // 发送ajax请求 我们发送的数据中即包含普通的键值也包含文件
  56. let formDataObj = new FormData();
  57. // 1.添加普通的键值对
  58. {#console.log($(\'#myform\').serializeArray()) // [{},{},{},{},{}] 只包含普通键值对#}
  59. $.each($(\'#myform\').serializeArray(),function (index,obj) {
  60. {#console.log(index,obj)#} // obj = {}
  61. formDataObj.append(obj.name,obj.value)
  62. });
  63. // 2.添加文件数据
  64. formDataObj.append(\'avatar\',$(\'#myfile\')[0].files[0]);
  65. // 3.发送ajax请求
  66. $.ajax({
  67. url:"",
  68. type:\'post\',
  69. data:formDataObj,
  70. // 需要指定两个关键性的参数
  71. contentType:false,
  72. processData:false,
  73. success:function (args) {
  74. if (args.code==1000){
  75. // 跳转到登陆页面
  76. window.location.href = args.url
  77. }else{
  78. // 如何将对应的错误提示展示到对应的input框下面
  79. // forms组件渲染的标签的id值都是 id_字段名
  80. $.each(args.msg,function (index,obj) {
  81. {#console.log(index,obj) // username ["用户名不能为空"]#}
  82. let targetId = \'#id_\' + index;
  83. $(targetId).next().text(obj[0]).parent().addClass(\'has-error\')
  84. })
  85. }
  86. }
  87. })
  88. })
  89. // 给所有的input框绑定获取焦点事件
  90. $(\'input\').focus(function () {
  91. // 将input下面的span标签和input外面的div标签修改内容及属性
  92. $(this).next().text(\'\').parent().removeClass(\'has-error\')
  93. })
  94. </script>
  95. </body>
  96. </html>

登录功能的技术点有以下

  • 实现图片验证码

  • ajax

版权声明:本文为heirenxilou原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/heirenxilou/p/13072011.html