报账系统总结(1):路由分配/数据库/主页/注册/登陆

1. 路由分配

1.1 知识点

  1. 为区分主页index中不同的标签请求,使用正则路由匹配
  2. 路由匹配为空时,从上向下匹配时,若上面没有匹配到,则会执行空对应的路由函数

1.2 示例代码

  1. urls.py
  1. # filename: urls.py 
  2. from django.contrib import admin 
  3. from django.urls import path 
  4. from django.conf.urls import url 
  5. from app01 import views 
  6.  
  7. urlpatterns = [ 
  8. path('admin/', admin.site.urls), 
  9. # 正则别名 便于取数据 
  10. url(r'^all/(?P<type_id>\d+)', views.index), 
  11. url(r'^login/', views.login), 
  12. url(r'^register/', views.register), 
  13. url(r'^check_code/', views.check_code), 
  14. url(r'^upload_avatar/', views.upload_avatar), 
  15. url(r'^', views.index), 
  16. ] 

2. 数据库

2.1 知识点

  1. auto_now_add 与 auto_now的区别
  2. ManyToMany 正反向操作
  3. 联合唯一索引与ManyToMany混合使用
  4. OneToOneField的正反向操作
  5. 枚举的使用

2.2 示例代码

  1. models.py
  1. # filename: models.py 
  2. from django.db import models 
  3.  
  4.  
  5. class UserInfo(models.Model): 
  6. ''' 
  7. 用户信息表 
  8. ''' 
  9. nid = models.BigAutoField(primary_key=True) 
  10. username = models.CharField(verbose_name='用户名', max_length=32, unique=True) 
  11. password = models.CharField(verbose_name='密码', max_length=64) 
  12. nickname = models.CharField(verbose_name='昵称', max_length=32) 
  13. email = models.EmailField(verbose_name='邮箱', unique=True) 
  14. avatar = models.ImageField(verbose_name='头像') 
  15. # 注意创建时间 
  16. create_time = models.DateField(verbose_name='创建时间', auto_now_add=True) 
  17. # U2U 
  18. U2U = models.ManyToManyField(verbose_name='粉丝们', 
  19. to='UserInfo', 
  20. through='UserFans', 
  21. through_fields=('user', 'follwers'), 
  22. related_name='fans', 
  23. ) 
  24.  
  25.  
  26. class UserFans(models.Model): 
  27. ''' 
  28. 互粉关系表 
  29. ''' 
  30. nid = models.BigAutoField(primary_key=True) 
  31. user = models.ForeignKey(verbose_name='博主', to='UserInfo', to_field='nid', related_name='user', on_delete=models.CASCADE) 
  32. follwers = models.ForeignKey(verbose_name='粉丝', to='UserInfo', to_field='nid', related_name='follwers', on_delete=models.CASCADE) 
  33.  
  34. # 联合唯一索引 
  35. class Meta: 
  36. unique_together = [ 
  37. ('user', 'follwers'), 
  38. ] 
  39.  
  40.  
  41. class Blog(models.Model): 
  42. ''' 
  43. 博客信息表 
  44. ''' 
  45. nid = models.BigAutoField(primary_key=True) 
  46. title = models.CharField(verbose_name='个人博客标题', max_length=64) 
  47. theme = models.CharField(verbose_name='个人博客主题', max_length=32) 
  48. site = models.CharField(verbose_name='个人博客域名', max_length=32, unique=True) 
  49. user = models.OneToOneField(verbose_name='博客用户', to='UserInfo', to_field='nid', on_delete=models.CASCADE) 
  50.  
  51.  
  52. class Article(models.Model): 
  53. ''' 
  54. 文章信息表 
  55. ''' 
  56. nid = models.BigAutoField(primary_key=True) 
  57. title = models.CharField(verbose_name='文章标题', max_length=128) 
  58. summary = models.CharField(verbose_name='文章简介', max_length=255) 
  59. create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) 
  60. blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE) 
  61. category = models.ForeignKey(verbose_name='文章类型', to='Category', to_field='nid', null=True, on_delete=models.CASCADE) 
  62.  
  63. # 因为牵扯到多次从数据库中获取,会造成多次查询 故直接将常用信息放到article中 
  64. # 数据冗余 
  65. read_count = models.IntegerField(default=0) 
  66. comment_count = models.IntegerField(default=0) 
  67. up_count = models.IntegerField(default=0) 
  68. down_count = models.IntegerField(default=0) 
  69.  
  70. type_choice = [ 
  71. (1, 'Python'), 
  72. (2, 'Linux'), 
  73. (3, '人工智能'), 
  74. (4, '区块链'), 
  75. (5, 'Golang'), 
  76. ] 
  77.  
  78. # 主页文章分类 
  79. article_type_id = models.IntegerField(choices=type_choice, default=None) 
  80.  
  81. # article to tag 
  82. tags = models.ManyToManyField(verbose_name='标签类型', 
  83. to='Tag', 
  84. through='Article2Tag', 
  85. through_fields=('article', 'tag')) 
  86.  
  87.  
  88. class Category(models.Model): 
  89. ''' 
  90. 个人用户文章分类 
  91. ''' 
  92. nid = models.AutoField(primary_key=True) 
  93. title = models.CharField(verbose_name='分类标题', max_length=32) 
  94. blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE) 
  95.  
  96.  
  97. class Tag(models.Model): 
  98. ''' 
  99. 标签表 
  100. ''' 
  101. nid = models.AutoField(primary_key=True) 
  102. title = models.CharField(verbose_name='标签名称', max_length=32 ) 
  103. blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE) 
  104.  
  105.  
  106. class Article2Tag(models.Model): 
  107. ''' 
  108. 文章to标签 多对多 关系表 
  109. ''' 
  110. nid = models.BigAutoField(primary_key=True) 
  111. article = models.ForeignKey(verbose_name='文章', to='Article', to_field='nid', on_delete=models.CASCADE) 
  112. tag = models.ForeignKey(verbose_name='标签', to='Tag', to_field='nid', on_delete=models.CASCADE) 
  113.  
  114. class Meta: 
  115. unique_together = [ 
  116. ('article', 'tag'), 
  117. ] 
  118.  
  119.  
  120. class ArticleDetail(models.Model): 
  121. ''' 
  122. 文章详细表 
  123. ''' 
  124. content = models.TextField(verbose_name='文章内容') 
  125. article = models.OneToOneField(verbose_name='所属文章', to='Article', to_field='nid', on_delete=models.CASCADE) 
  126.  
  127.  
  128. class UpDown(models.Model): 
  129. ''' 
  130. 文章 顶/踩 
  131. ''' 
  132. article = models.ForeignKey(verbose_name='所属文章', to='Article', to_field='nid', on_delete=models.CASCADE) 
  133. user = models.ForeignKey(verbose_name='顶或踩的用户', to='UserInfo', to_field='nid', on_delete=models.CASCADE) 
  134. up = models.BooleanField(verbose_name='是否赞') 
  135.  
  136. class Meta: 
  137. unique_together = [ 
  138. ('article', 'user'), 
  139. ] 
  140.  
  141.  
  142. class Comment(models.Model): 
  143. ''' 
  144. 文章评论表 
  145. ''' 
  146. nid = models.BigAutoField(primary_key=True) 
  147. content = models.CharField(verbose_name='评论内容', max_length=255) 
  148. create_time = models.DateTimeField(verbose_name='评论时间', auto_now_add=True) 
  149. article = models.ForeignKey(verbose_name='被评论文章', to='Article', to_field='nid', on_delete=models.CASCADE) 
  150. user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid', on_delete=models.CASCADE) 
  151. reply = models.ForeignKey(verbose_name='被回复评论', to='self', to_field='nid', null=True, on_delete=models.CASCADE) 

3. 主页

3.1 知识点

  1. 获取当前请求request的URL
  2. 数据库操作models.filter(**kwargs)为空字典时,将会获取全部数据
  3. 通过数据库中的枚举,实现显示主页的标签
  4. BootStrap中 class 默认navbar是圆角的 如果不想要 则需自己定制 类 no-radius
  5. container-fluid 表示100%平铺 如果改成container 就可以设置宽度 并且container中包括响应式
  6. 前端可直接调用request
  7. 前端中文字与图片的排版
  8. BootStrap中 class = ‘clearfix’ 相当于在类所在标签的最后增加一个空标签 clear:both 用于清除浮动

3.2 示例代码

  1. Views.py
  1. # filename: views.py 
  2. def index(request, *args, **kwargs): 
  3. ''' 
  4. 主页 
  5. :param request: 
  6. :param args: 
  7. :param kwargs: 
  8. :return: 
  9. ''' 
  10. result = request.session.get('username') 
  11. if result: 
  12.  
  13. # 分页 
  14. # 获取当前URL request.path_info 
  15. current_page = request.GET.get('page') 
  16. base_url = request.path_info 
  17.  
  18. # 标签分类 
  19. # 基本方式一 
  20. # type_id = int(kwargs.get('type_id')) if kwargs.get('type_id') else None 
  21. # if type_id: 
  22. # article_list = models.Article.objects.filter(article_type_id=type_id) 
  23. # else: 
  24. # article_list = models.Article.objects.all() 
  25.  
  26. # 方式二 
  27. content = {} 
  28. type_id = int(kwargs.get('type_id')) if kwargs.get('type_id') else None 
  29. if type_id: 
  30. content['article_type_id'] = type_id 
  31. # 注意此处 filter条件为空字典的时候获取全部数据 
  32. # ******分页 
  33. all_count = models.Article.objects.filter(**content).count() 
  34. page_info = PageInfo(current_page, all_count, 10, base_url, 11) 
  35. # ******分页 
  36. article_list = models.Article.objects.filter(**content)[page_info.start():page_info.end()] 
  37.  
  38. type_choice = models.Article.type_choice 
  39. login_flag = True 
  40. return render(request, 'index.html', { 
  41. 'type_choice': type_choice, 
  42. 'article_list': article_list, 
  43. 'type_id': type_id, 
  44. 'page_info': page_info, 
  45. 'login_flag': login_flag 
  46. }) 
  47. else: 
  48. return redirect('/login/') 
  1. index.html
  1. <!--filename:index.html --> 
  2. <!DOCTYPE html> 
  3. <html lang="en"> 
  4. <head> 
  5. <meta charset="UTF-8"> 
  6. <meta http-equiv="x-ua-compatible" content="IE=edge"> 
  7. <meta name="viewport" content="width=device-width, initial-scale=1"> 
  8. <title>Title</title> 
  9. <link rel="stylesheet" href="/static/plugins/bootstrap-3.3.7-dist/css/bootstrap.css"> 
  10. <link rel="stylesheet" href="/static/css/commons.css"> 
  11. <link rel="stylesheet" href="/static/plugins/fontawesome-free-5.1.0-web/css/fontawesome.css"> 
  12.  
  13. </head> 
  14.  
  15. <body> 
  16. <!-- 默认navbar是圆角的 如果不想要 则需自己定制 类 no-radius --> 
  17. <nav class="navbar navbar-default no-radius"> 
  18. <!-- ******** --> 
  19. <!-- container-fluid 表示100%平铺 如果改成container 就可以设置宽度 --> 
  20. <!-- 并且container中包括响应式 --> 
  21. <div class="container"> 
  22. <!-- Brand and toggle get grouped for better mobile display --> 
  23. <div class="navbar-header"> 
  24. <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" 
  25. data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> 
  26. <span class="sr-only">Toggle navigation</span> 
  27. <span class="icon-bar"></span> 
  28. <span class="icon-bar"></span> 
  29. <span class="icon-bar"></span> 
  30. </button> 
  31. <a class="navbar-brand" href="/">老子的技术论坛</a> 
  32. </div> 
  33.  
  34. <!-- Collect the nav links, forms, and other content for toggling --> 
  35. <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> 
  36. <ul class="nav navbar-nav"> 
  37. {% if type_id %} 
  38. <li><a href="/">全部</a></li> 
  39. {% else %} 
  40. <li class="active"><a href="/">全部</a></li> 
  41. {% endif %} 
  42. {% for item in type_choice %} 
  43. {% if type_id == item.0 %} 
  44. <li class="active"><a href="/all/{{ item.0 }}/">{{ item.1 }}</a></li> 
  45. {% else %} 
  46. <li><a href="/all/{{ item.0 }}/">{{ item.1 }}</a></li> 
  47. {% endif %} 
  48. {% endfor %} 
  49. </ul> 
  50. <ul class="nav navbar-nav navbar-right"> 
  51. {% if login_flag %} 
  52. <!-- 注意此处--> 
  53. <li><a href="#">{{ request.session.username }}</a></li> 
  54. <li><a href="#">我的博客</a></li> 
  55. <li><a href="#">管理</a></li> 
  56. <li><a href="#">退出</a></li> 
  57.  
  58. {% else %} 
  59. <li><a href="/login/">登陆</a></li> 
  60. <li><a href="#">注册</a></li> 
  61. {% endif %} 
  62. </ul> 
  63. </div><!-- /.navbar-collapse --> 
  64. </div><!-- /.container-fluid --> 
  65. </nav> 
  66.  
  67. <div class="container"> 
  68. <div class="col-md-8"> 
  69. <div class="article-list"> 
  70. {% for article in article_list %} 
  71. <div class="article-item"> 
  72. <h4 class="article-head"><a>{{ article.title }}</a></h4> 
  73. <div class="article-body clearfix"> 
  74. <a class="left"><img src="{{ article.blog.user.avatar }}"></a> 
  75. {{ article.summary }} 
  76. <!-- clearfix相当于在此处增加一个标签 clear:both --> 
  77. </div> 
  78. <div class="article-foot"> 
  79. <a> 
  80. <span class="glyphicon glyphicon-user"></span> 
  81. {{ article.blog.user.username }} 
  82. </a> 
  83. <span> 
  84. 发布于 {{ article.create_time|date:"Y-m-d H:i:s" }} 
  85. </span> 
  86. <a href="#"> 
  87. <span class="glyphicon glyphicon-pencil"></span> 
  88. {{ article.comment_count }} 
  89. </a> 
  90. <a href="#"> 
  91. <span class="glyphicon glyphicon-thumbs-up"></span> 
  92. {{ article.up_count }} 
  93. </a> 
  94. </div> 
  95.  
  96. </div> 
  97. {% endfor %} 
  98. </div> 
  99. <div> 
  100. <nav aria-label="Page navigation"> 
  101. <ul class="pagination"> 
  102. {{ page_info.page|safe}} 
  103. </ul> 
  104. </nav> 
  105. </div> 
  106. </div> 
  107. <div class="col-md-4"> 
  108. <div class="panel panel-default"> 
  109. <div class="panel-heading">Panel heading without title</div> 
  110. <div class="panel-body"> 
  111. <p>123</p> 
  112. <p>123</p> 
  113. <p>123</p> 
  114. <p>123</p> 
  115. <p>123</p> 
  116. </div> 
  117. </div> 
  118.  
  119. <div class="panel panel-default"> 
  120. <div class="panel-heading">Panel heading without title</div> 
  121. <div class="panel-body"> 
  122. Panel content 
  123. </div> 
  124. </div> 
  125. </div> 
  126. </div> 
  127.  
  128. </body> 
  129. </html> 

4. 注册

4.1 知识点

4.2 示例代码

  1. forms.py
  2. register.html
  3. Views.py

5. 登陆

5.1 知识点

5.2 示例代码

  1. login.html
  1. <!DOCTYPE html> 
  2. <html lang="en"> 
  3. <head> 
  4. <meta charset="UTF-8"> 
  5. <meta http-equiv="x-ua-compatible" content="IE=edge"> 
  6. <meta name="viewport" content="width=device-width, initial-scale=1"> 
  7. <title>Title</title> 
  8. <link rel="stylesheet" href="/static/plugins/bootstrap-3.3.7-dist/css/bootstrap.css"> 
  9.  
  10. <style> 
  11. .login { 
  12. margin: 0 auto; 
  13. width: 600px; 
  14. padding: 20px; 
  15. margin-top: 80px; 
  16. } 
  17.  
  18. .head { 
  19. text-align: center; 
  20. padding: 10px; 
  21. margin-bottom: 20px; 
  22. margin-left: 90px; 
  23. margin-top: -60px; 
  24. } 
  25.  
  26. .logined, .register { 
  27. width: 460px; 
  28. } 
  29. </style> 
  30. </head> 
  31.  
  32. <body> 
  33. <div class="login"> 
  34. <div class="head"> 
  35. <h2><a href="/login/">用户登陆</a></h2> 
  36. </div> 
  37.  
  38. <div> 
  39. <form class="form-horizontal" novalidate method="post" action="/login/"> 
  40. {% csrf_token %} 
  41. {% for row in range %} 
  42. <div class="form-group"> 
  43. <label class="col-sm-2 control-label">用户名</label> 
  44. <div class="col-sm-10"> 
  45. <!-- <input type="email" class="form-control" placeholder="用户名">--> 
  46. {{ my_login_form.username }}{{ my_login_form.errors.username.0 }}{{ user_error }} 
  47. </div> 
  48. </div> 
  49. <div class="form-group"> 
  50. <label class="col-sm-2 control-label">密码</label> 
  51. <div class="col-sm-10"> 
  52. <!-- <input type="password" class="form-control" placeholder="密码">--> 
  53. {{ my_login_form.password }}{{ my_login_form.errors.password.0 }} 
  54. </div> 
  55. </div> 
  56. <div class="form-group"> 
  57. <label class="col-sm-2 control-label">验证码</label> 
  58. <div class="col-sm-3"> 
  59. <!-- <input type="text" class="form-control" placeholder="验证码">--> 
  60. {{ my_login_form.code }}{{ my_login_form.errors.code.0 }}{{ code_error }} 
  61. </div> 
  62. <div class="col-sm-5"> 
  63. <!--验证码刷新原理--> 
  64. <!--get方式请求 url后面加东西 不影响访问 但会刷新--> 
  65. <!--/check_code/?--> 
  66. <img id="check_code" style="width: 120px;height: 30px" src="/check_code/"> 
  67.  
  68. </div> 
  69. </div> 
  70. <div class="form-group"> 
  71. <div class="col-sm-offset-2 col-sm-10"> 
  72. <button type="submit" class="btn btn-default logined">登陆</button> 
  73. </div> 
  74. </div> 
  75. <div class="form-group"> 
  76. <div class="col-sm-offset-2 col-sm-10"> 
  77. <a href="/register/"> 
  78. <button type="button" class="btn btn-default register">注册</button> 
  79. </a>x 
  80. </div> 
  81. </div> 
  82. </form> 
  83. </div> 
  84. </div> 
  85. <script src="/static/jquery-3.3.1.js"></script> 
  86. <script> 
  87. // 验证码图片刷新 
  88. $('#check_code').click(function () { 
  89. $('#check_code')[0].src = $('#check_code')[0].src + '?' 
  90. }) 
  91.  
  92. </script> 
  93.  
  94. </body> 
  95. </html> 
  1. form.py
  1. # filename: form.py 
  2. class MyLoginForm(Form): 
  3. ''' 
  4. 用户登陆Form 
  5. ''' 
  6. username = fields.CharField(max_length=32, 
  7. error_messages={ 
  8. 'required': '用户名不能为空', 
  9. 'max_length': '用户名最多为32个字符', 
  10. }, 
  11. widget=widgets.TextInput( 
  12. attrs={ 
  13. 'class': "form-control", 
  14. 'placeholder': "用户名", 
  15. } 
  16. )) 
  17. password = fields.CharField( 
  18. max_length=32, 
  19. min_length=6, 
  20. error_messages={ 
  21. 'required': '密码不能为空', 
  22. 'max_length': '密码最多为32个字符', 
  23. 'min_length': '密码最多为6个字符', 
  24. }, 
  25. widget=widgets.PasswordInput( 
  26. attrs={ 
  27. 'name': 'password', 
  28. 'class': "form-control", 
  29. 'placeholder': "密码", 
  30. })) 
  31. code = fields.CharField(max_length=5, min_length=5, 
  32. error_messages={ 
  33. 'required': '验证码不能为空', 
  34. 'max_length': '验证码只能为5个字符', 
  35. 'min_length': '验证码只能为5个字符', 
  36. }, 
  37. widget=widgets.TextInput( 
  38. attrs={ 
  39.  
  40. 'class': "form-control", 
  41. 'placeholder': "验证码", 
  42. } 
  43. )) 
  1. Views.py
  1.  
posted @ 2018-07-09 18:23  LexSama  阅读(253)  评论(0)    收藏  举报