第二十 BBS
1.创建新项目
2.数据表设计
2.1.表结构规划
https://docs.djangoproject.com/en/2.0/ref/models/instances/#django.db.models.Model.clean
from django.db import models from django.contrib.auth.models import User from django.core.exceptions import ValidationError import datetime # Create your models here. class Article(models.Model): #文章标题 title = models.CharField(max_length=255) #文章简介 brief = models.CharField(null=True,blank=True,max_length=255) #文章板坏 category = models.ForeignKey("Category") #文章内容 content = models.TextField("文章内容") #作者 author = models.ForeignKey("UserProfile") #文章发布时间,auto_now_add自动发布为发布时的时间 pub_date = models.DateTimeField(blank=True,null=True) #最后修改时间为修改的当前时间 last_modify = models.DateTimeField(auto_now=True) #文章置顶,默认是1000 priority = models.IntegerField("优先级",default=1000) #文章状态可选项 status_choice = (('draft',"草稿"), ('published',"已发布"), ('hidden','隐藏'), ) #文章状态 status = models.CharField(choices=status_choice,default='published',max_length=64) def __str__(self): return self.title def clean(self): # Don't allow draft entries to have a pub_date. if self.status == 'draft' and self.pub_date is not None: raise ValidationError(('Draft entries may not have a publication date.')) # Set the pub_date for published items if it hasn't been set already. if self.status == 'published' and self.pub_date is None: self.pub_date = datetime.date.today() class Comment(models.Model): #关联所属文章 article = models.ForeignKey(Article,verbose_name="所属文章") #顶级评论 parent_comment = models.ForeignKey('self',related_name='my_children',null=True) #评论及点赞可选项 comment_choice = ((1,'评论'), (2,'点赞')) #评论类型 comment_type = models.IntegerField(choices=comment_choice,default=1) #评论用户 user = models.ForeignKey("UserProfile") #评论内容 comment = models.TextField(blank=True,null=True) # 添加评论时间 date = models.DateField(auto_now_add=True) def __str__(self): return "%s,%s,%s" %(self.article,self.comment,self.date) def clean(self): if self.comment_type == 1 and self.comment is None: raise ValidationError("评论写点什么吧") class Category(models.Model): #板块名 name = models.CharField(max_length=64,unique=True) #板块简介 brief = models.CharField(null=True,blank=True,max_length=255) #顶级菜单 set_as_top_menu = models.BooleanField(default=False) #版主设置,可设置为空 admins = models.ManyToManyField("UserProfile",blank=True) #菜单位置 position_index = models.SmallIntegerField() def __str__(self): return self.name class UserProfile(models.Model): #关联到django的的User user = models.OneToOneField(User) #用户名 name = models.CharField(max_length=32) #个人签名 signature = models.CharField(max_length=255,blank=True,null=True) #头像 head_img = models.ImageField(height_field=200,width_field=200) def __str__(self): return self.name
2.2.生成表
3.注册django admin
from django.contrib import admin from bbs import models # Register your models here. class ArticleAdmin(admin.ModelAdmin): list_display = ('title','category','author','pub_date','last_modify','status','priority') class CommentAdmin(admin.ModelAdmin): list_display = ('article','parent_comment','comment_type','comment','user','date') class CategoryAdmin(admin.ModelAdmin): list_display = ('name','set_as_top_menu','position_index') admin.site.register(models.Article,ArticleAdmin) admin.site.register(models.Comment,CommentAdmin) admin.site.register(models.UserProfile) admin.site.register(models.Category,CategoryAdmin)
4.添加超级用户
5.登录添加内容
5.1.添加文章
5.2.添加评论
5.3.为文章添加图片
5.3.1.最新表结构
from django.db import models from django.contrib.auth.models import User from django.core.exceptions import ValidationError import datetime # Create your models here. class Article(models.Model): #文章标题 title = models.CharField(max_length=255) #文章简介 brief = models.CharField(null=True,blank=True,max_length=255) #文章板坏 category = models.ForeignKey("Category") #文章内容 content = models.TextField("文章内容") #作者 author = models.ForeignKey("UserProfile") #文章发布时间,auto_now_add自动发布为发布时的时间 pub_date = models.DateTimeField(blank=True,null=True) #最后修改时间为修改的当前时间 last_modify = models.DateTimeField(auto_now=True) #文章置顶,默认是1000 priority = models.IntegerField("优先级",default=1000) # 文图 head_img = models.ImageField("文章配图") #文章状态可选项 status_choice = (('draft',"草稿"), ('published',"已发布"), ('hidden','隐藏'), ) #文章状态 status = models.CharField(choices=status_choice,default='published',max_length=64) def __str__(self): return self.title def clean(self): # Don't allow draft entries to have a pub_date. if self.status == 'draft' and self.pub_date is not None: raise ValidationError(('Draft entries may not have a publication date.')) # Set the pub_date for published items if it hasn't been set already. if self.status == 'published' and self.pub_date is None: self.pub_date = datetime.date.today() class Comment(models.Model): #关联所属文章 article = models.ForeignKey(Article,verbose_name="所属文章") #顶级评论 parent_comment = models.ForeignKey('self',related_name='my_children',null=True,blank=True) #评论及点赞可选项 comment_choice = ((1,'评论'), (2,'点赞')) #评论类型 comment_type = models.IntegerField(choices=comment_choice,default=1) #评论用户 user = models.ForeignKey("UserProfile") #评论内容 comment = models.TextField(blank=True,null=True) # 添加评论时间 date = models.DateField(auto_now_add=True) def __str__(self): return "%s,%s,%s" %(self.article,self.parent_comment,self.comment) def clean(self): if self.comment_type == 1 and len(self.comment) == 0: raise ValidationError("评论写点什么吧") class Category(models.Model): #板块名 name = models.CharField(max_length=64,unique=True) #板块简介 brief = models.CharField(null=True,blank=True,max_length=255) #顶级菜单 set_as_top_menu = models.BooleanField(default=False) #版主设置,可设置为空 admins = models.ManyToManyField("UserProfile",blank=True) #菜单位置 position_index = models.SmallIntegerField() def __str__(self): return self.name class UserProfile(models.Model): #关联到django的的User user = models.OneToOneField(User) #用户名 name = models.CharField(max_length=32) #个人签名 signature = models.CharField(max_length=255,blank=True,null=True) #头像 head_img = models.ImageField(height_field=200,width_field=200,blank=True) def __str__(self): return self.name
5.3.2.重新应用表
5.3.3.添加文章图片
5.3.4.图片默认被保存的路径
5.3.5.图片保存修改
django 自带图片保存,修改字段:
head_img = models.ImageField("文章配图",upload_to="uploads")
上传图片:
自动创建uploads并保存图片
5.4.配置动态板块
5.4.1.配置views
from django.shortcuts import render from bbs import models # Create your views here. def index(request):
#或许所有的的板块 category_list = models.Category.objects.filter(set_as_top_menu=True).order_by("position_index") return render(request,'bbs/index.html',{'category_list':category_list})
5.4.2.配置返回页面
返回页面直接配置base即可
https://v3.bootcss.com/examples/navbar-fixed-top/
.... <div id="navbar" class="navbar-collapse collapse"> <ul class="nav navbar-nav"> {% for category in category_list %} <li class="active"><a href="#">{{ category.name }}</a></li> {% endfor %} </ul> <ul class="nav navbar-nav navbar-right"> <li><a href="https://v3.bootcss.com/examples/navbar/">Default</a></li> <li><a href="https://v3.bootcss.com/examples/navbar-static-top/">Static top</a></li> <li class="active"><a href="https://v3.bootcss.com/examples/navbar-fixed-top/">Fixed top <span class="sr-only">(current)</span></a></li> </ul> </div><!--/.nav-collapse --> ...
5.4.3.查看返回板块
5.4.4.增加多的板块
5.4.5.配置urls
如果点击板块,只需要返回同一个页面,需要引入一个url,如:http://127.0.0.1:8000/bbs/category/1/
from django.conf.urls import url,include from bbs import views urlpatterns = [ url(r'^$', views.index), url(r'category/(\d+)/$', views.category), ]
5.4.6.配置views
from django.shortcuts import render from bbs import models #单独将category列表取出 category_list = models.Category.objects.filter(set_as_top_menu=True).order_by("position_index") # Create your views here. def index(request): return render(request,'bbs/index.html',{'category_list':category_list}) def category(request,id): category_obj = models.Category.objects.get(id=id) return render(request, 'bbs/index.html',{'category_list':category_list, 'category_obj':category_obj})
5.4.7.配置base页面
.... <ul class="nav navbar-nav"> {% for category in category_list %} <!-- 如果前端循环的category的id正好和后端返回的id相等,则将当前板块设为active --> {% if category.id == category_obj.id %} <li class="active"><a href="/bbs/category/{{ category.id }}">{{ category.name }}</a></li> {% else %} <li class=""><a href="/bbs/category/{{ category.id }}">{{ category.name }}</a></li> {% endif %} {% endfor %} </ul> ....
5.4.8.查看返回
6.配置用户登录及退出
6.1.配置登录及注销方法
from django.shortcuts import render from bbs import models from django.contrib.auth import login,logout,authenticate from django.contrib.auth.decorators import login_required from django.shortcuts import render,HttpResponse,HttpResponseRedirect category_list = models.Category.objects.filter(set_as_top_menu=True).order_by("position_index") # Create your views here. def index(request): return render(request,'bbs/index.html',{'category_list':category_list}) def category(request,id): category_obj = models.Category.objects.get(id=id) return render(request, 'bbs/index.html',{'category_list':category_list, 'category_obj':category_obj}) def acc_login(request): if request.method == 'POST': print(request.POST) user = authenticate(username=request.POST.get('username'), password=request.POST.get('password')) if user is not None: login(request,user) return HttpResponseRedirect('/bbs') else: login_err = "Worng user name or password" return render(request,'login.html',{'login_err':login_err}) return render(request,'login.html') def acc_logout(request): logout(request) return HttpResponseRedirect('/bbs')
6.2.配置url
from django.conf.urls import url,include from django.contrib import admin from bbs import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^bbs',include('bbs.urls')), url(r'login',views.acc_login,name='login'), url(r'logout',views.acc_logout,name='logout'), ]
6.3.配置返回html
.... <div id="navbar" class="navbar-collapse collapse"> <ul class="nav navbar-nav"> {% for category in category_list %} <!-- 如果前端循环的category的id正好和后端返回的id相等,则将当前板块设为active --> {% if category.id == category_obj.id %} <li class="active"><a href="/bbs/category/{{ category.id }}">{{ category.name }}</a></li> {% else %} <li class=""><a href="/bbs/category/{{ category.id }}">{{ category.name }}</a></li> {% endif %} {% endfor %} </ul> <ul class="nav navbar-nav navbar-right"> <!-- 如果用户登录,则显示用户名及注销,否则显示登录 --> {% if request.user.is_authenticated %} <li class=""><a href="#">{{ request.user.userprofile.name }}</a></li> <li class=""><a href="{% url 'logout' %}">注销</a></li> {% else %} <li class=""><a href="{% url 'login' %}">登录/注册</a></li> {% endif %} </ul> </div><!--/.nav-collapse --> ....
登录html 拷贝之前的登录页面
6.4.登录及退出
登录:
7.各个板块分开展示
7.1.每个板块增加文章
7.2.内容预览
7.2.1.各个专栏展示
7.2.2.全部展示所有的内容
.... def category(request,id): category_obj = models.Category.objects.get(id=id) if category_obj.position_index == 1:#全部 article_list = models.Article.objects.filter(status='published') else: article_list = models.Article.objects.filter(category_id = category_obj.id,status='published') return render(request, 'bbs/index.html',{'category_list':category_list, 'category_obj':category_obj, 'article_list':article_list }) .....
7.2.3.查看全部内容
7.3.默认路径访问所有内容
如:http://127.0.0.1:8000/bbs
def index(request): article_list = models.Article.objects.filter(status='published') return render(request,'bbs/index.html',{'category_list':category_list, 'article_list': article_list })
查看展示:
7.4.将全部增加选中样式
def index(request): category_obj = models.Category.objects.get(position_index=1) article_list = models.Article.objects.filter(status='published') return render(request,'bbs/index.html',{'category_list':category_list, 'article_list': article_list, 'category_obj':category_obj })
查看展示:
7.5.文章内容展示
7.5.1.展示文章标题及图片
修改index.html
{% extends 'base.html' %} {% block page-container %} <!-- 循环文章列表 --> {% for article in article_list %} <div class="article-box"> <!-- 展示文章图片 --> <div class="article-head-img"> <img src="/static/{{ article.head_img }}"> </div> <!-- 展示文章标题 --> <div class="article-brief"> {{ article.title }} </div> </div> {% endfor %} {% endblock %}
7.5.2.展示内容
7.5.3.图片无法展示
图片路径为http://127.0.0.1:8000/static/uploads/2.jpg
7.5.4.增加静态资源的路径
STATICFILES_DIRS = [ os.path.join(BASE_DIR, "statics"), os.path.join(BASE_DIR, "uploads") # '/var/www/static/', ]
图片路径多了一个uploads,去掉可以访问
需要做的,就是讲url的路径截取,没有这样的方法,需要自定义标签
7.6.新增自定义标签
7.6.1.定义标签方法
#!/usr/bin/env python # -*- coding: utf-8 -*- from django import template from django.utils.html import format_html register = template.Library() @register.filter def truncate_url(img_obj): print(img_obj.name) return img_obj.name.split("/",maxsplit=1)[-1]
7.6.2.静态页面load 自定义标签并使用方法
{% extends 'base.html' %} {% load custom %} {% block page-container %} <!-- 循环文章列表 --> {% for article in article_list %} <div class="article-box"> <!-- 展示文章图片 --> <div class="article-head-img"> <img src="/static/{{ article.head_img|truncate_url }}"> </div> <!-- 展示文章标题 --> <div class="article-brief"> {{ article.title }} </div> </div> {% endfor %} {% endblock %}
7.6.3.查看结果
7.7.配置静态展示
7.7.1.添加静态css
.page-container {
border: 1px dashed darkcyan;
padding-left: 150px;
}
.wrap-left {
width: 75%;
float: left;
background-color: darkgoldenrod;
}
.wrap-right {
width: 25%;
float: left;
background-color: darkolivegreen;
}
.clear-both {
clear: both;
}
.footer {
height: 300px;
background-color: #1e0eff;
}
7.7.2.配置index.html
{% extends 'base.html' %} {% load custom %} {% block page-container %} <div class="wrap-left"> <!-- 循环文章列表 --> {% for article in article_list %} <div class="article-box"> <!-- 展示文章图片 --> <div class="article-head-img"> <img src="/static/{{ article.head_img|truncate_url }}"> </div> <!-- 展示文章标题 --> <div class="article-brief"> {{ article.title }} </div> </div> {% endfor %} </div> <div class="wrap-right"> right </div> <div class="clear-both"></div> <div class="footer"> you can see </div> {% endblock %}
7.7.3.配置base.html
<!DOCTYPE html> <!-- saved from url=(0049)https://v3.bootcss.com/examples/navbar-fixed-top/ --> <html lang="zh-CN"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! --> <meta name="description" content=""> <meta name="author" content=""> <link rel="icon" href="https://v3.bootcss.com/favicon.ico"> <title>诗经源远</title> <!-- Bootstrap core CSS --> <link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <!-- Custom styles for this template --> <link href="/static/bootstrap/css/navbar-fixed-top.css" rel="stylesheet"> <link href="/static/bootstrap/css/custom.css" rel="stylesheet"> </head> <body> <!-- Fixed navbar --> <nav class="navbar navbar-default navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">Our Earth</a> </div> <div id="navbar" class="navbar-collapse collapse"> <ul class="nav navbar-nav"> {% for category in category_list %} <!-- 如果前端循环的category的id正好和后端返回的id相等,则将当前板块设为active --> {% if category.id == category_obj.id %} <li class="active"><a href="/bbs/category/{{ category.id }}">{{ category.name }}</a></li> {% else %} <li class=""><a href="/bbs/category/{{ category.id }}">{{ category.name }}</a></li> {% endif %} {% endfor %} </ul> <ul class="nav navbar-nav navbar-right"> <!-- 如果用户登录,则显示用户名及注销,否则显示登录 --> {% if request.user.is_authenticated %} <li class=""><a href="#">{{ request.user.userprofile.name }}</a></li> <li class=""><a href="{% url 'logout' %}">注销</a></li> {% else %} <li class=""><a href="{% url 'login' %}">登录/注册</a></li> {% endif %} </ul> </div><!--/.nav-collapse --> </div> </nav> <div class="page-container"> {% block page-container %} <!-- Main component for a primary marketing message or call to action --> <div class="jumbotron"> <h1>Being young is an attitude!</h1> </div> {% endblock %} </div> <!-- /container --> <footer class="footer"> szsz </footer> <!-- Bootstrap core JavaScript ================================================== --> <!-- Placed at the end of the document so the pages load faster --> <script src="/static/bootstrap/js/jquery-3.3.1.js"></script> <script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery.min.js"><\/script>')</script> <script src="/static/bootstrap/js/bootstrap.min.js"></script> </body></html>
7.7.4.页面展示
截图不完全,太大了
7.8.图片大小裁剪
7.8.1.index.html
<div class="article-box"> <!-- 展示文章图片 --> <div class="article-head-img"> <img src="/static/{{ article.head_img|truncate_url }}"> </div> <!-- 展示文章标题 --> <div class="article-brief"> {{ article.title }} </div> </div>
7.8.2.增加样式
custom.css
.article-head-img img {
width: 230px;
height: 130px;
}
7.8.3.查看结果
7.9.字体展示在右边
修改index.html
... <div class="article-box row"> <!-- 展示文章图片 --> <div class="article-head-img col-md-4"> <img src="/static/{{ article.head_img|truncate_url }}"> </div> <!-- 展示文章标题 --> <div class="article-brief col-md-8"> {{ article.title }} </div> </div> ...
7.10.增加间距
.article-box {
padding-bottom: 10px;
}
<div class="article-box row"> <!-- 展示文章图片 --> <div class="article-head-img col-md-4"> <img src="/static/{{ article.head_img|truncate_url }}"> </div> <!-- 展示文章标题 --> <div class="article-brief col-md-8"> {{ article.title }} </div> </div> <hr>
查看展示:
7.11.增加边距去掉背景
#去掉背景
.wrap-left {
width: 75%;
float: left;
}
#内边距
.article-box {
padding-bottom: 10px;
}
#字体与图片距离
.article-brief {
margin-left: -170px;
}
<div class="article-box row"> <!-- 展示文章图片 --> <div class="article-head-img col-md-4"> <img src="/static/{{ article.head_img|truncate_url }}"> </div> <!-- 展示文章标题 --> <div class="article-brief col-md-8"> <a href="#" class="article-title">{{ article.title }}</a> </div> </div> <hr>
查看展示:
7.12.修改文章title标签
index.html
<div class="article-brief col-md-8"> <a href="#" class="article-title">{{ article.title }}</a> </div>
/*
标题字体大小
*/
.article-title {
font-size: 20px;
}
a {
color: #333333;
}
a.active {
text-decoration: none;
}
/*
鼠标移动,颜色变化
*/
a:hover {
color: #337ab7;
text-decoration: none;
}
/*
整个页面字体,好像然并卵
*/
body{
background-color: rgb(255, 255, 255);
color: rgb(48, 48, 48);
font-family: Arial, 微软雅黑, "Microsoft yahei", "Hiragino Sans GB", "冬青黑体简体中文 w3", "Microsoft Yahei", "Hiragino Sans GB", "冬青黑体简体中文 w3", STXihei, 华文细黑, SimSun, 宋体, Heiti, 黑体, sans-serif;
}
7.13.添加点赞及作者信息
<!-- 展示文章标题 --> <div class="article-brief col-md-8"> <a href="#" class="article-title">{{ article.title }}</a> <div class="article-brief-info"> <span>{{ article.author.name }}</span> <span>{{ article.pub_date }}</span> <!-- 反查点赞与评论 --> <span>{{ article.comment_set.select_related.count }}</span> </div> </div>
查看展示:
8.点赞和评论分开
新增评论与点赞
8.1.新增自定义标签
#!/usr/bin/env python # -*- coding: utf-8 -*- from django import template from django.utils.html import format_html register = template.Library() @register.filter def truncate_url(img_obj): print(img_obj.name) return img_obj.name.split("/",maxsplit=1)[-1] @register.simple_tag def filter_comment(article_obj): #查询文章所有的评论与点赞 query_set = article_obj.comment_set.select_related() comments = { #评论 'comment_count':query_set.filter(comment_type=1).count(), #点赞 'thumb_count': query_set.filter(comment_type=2).count(), } return comments
8.2.修改index调用自定义方法获取内容
<div class="article-brief-info"> <span>{{ article.author.name }}</span> <span>{{ article.pub_date }}</span> <!-- 将方法值赋值给一个变量 --> <span>{% filter_comment article as comments %}</span> {{ comments.comment_count }} {{ comments.thumb_count }} </div>
8.3.查看返回
8.4.添加点赞与评论图标
组件图标地址http://v3.bootcss.com/components/
<div class="article-brief-info"> <span>{{ article.author.name }}</span> <span>{{ article.pub_date }}</span> <!-- 将方法值赋值给一个变量 --> <span>{% filter_comment article as comments %}</span> <span class="glyphicon glyphicon-comment" aria-hidden="true"></span>{{ comments.comment_count }} <span class="glyphicon glyphicon-thumbs-up" aria-hidden="true"></span>{{ comments.thumb_count }} </div>
查看返回
8.5.添加主题
<div class="article-brief col-md-8"> <a href="#" class="article-title">{{ article.title }}</a> <div class="article-brief-info"> <span>{{ article.author.name }}</span> <span>{{ article.pub_date }}</span> <!-- 将方法值赋值给一个变量 --> <span>{% filter_comment article as comments %}</span> <span class="glyphicon glyphicon-comment" aria-hidden="true"></span>{{ comments.comment_count }} <span class="glyphicon glyphicon-thumbs-up" aria-hidden="true"></span>{{ comments.thumb_count }} </div> <div class="article-brief-text"> <span>{{ article.brief }}</span> </div> </div>
8.5.1.配置主题样式
.article-brief-text{
margin-top: 10px;
color: #999;
}
8.5.2.查看返回
9.详细页面展示
9.1.增加返回url
from django.conf.urls import url,include from bbs import views urlpatterns = [ url(r'^$', views.index), url(r'category/(\d+)/$', views.category), url(r'detail/(\d+)/$', views.article_detail,name="article_detail"), ]
9.2.增加返回方法
def article_detail(request,article_id): article_obj = models.Article.objects.get(id=article_id) return render(request,'bbs/article_detail.html',{'article_obj':article_obj})
9.3.新增返回页面
{% extends 'base.html' %} {% load custom %} {% block page-container %} <div class="wrap-left"> {{ article_obj }} </div> <div class="wrap-right"> right </div> <div class="clear-both"></div> <div class="footer"> you can see </div> {% endblock %}
9.4.增加图片及文字
9.4.1.增加静态图标及文章展示
{% extends 'base.html' %} {% load custom %} {% block page-container %} <div class="wrap-left"> <!-- 文章标题 --> <div class="article-title-size"> {{ article_obj.title }} </div> <div class="article-title-brief"> <span>作者:{{ article_obj.author.name }}</span> <span>{{ article_obj.pub_date }}</span> <span> <!-- 获取评论数 --> <span class="glyphicon glyphicon-comment" aria-hidden="true"></span> {% filter_comment article_obj as comments %} {{ comments.comment_count }} </span> </div> <!-- 文章图片 --> <div class="article-content"> <img class="article-detail-head-img" src="/static/{{ article_obj.head_img|truncate_url }}"> {{ article_obj }} {{ article_obj.content }} </div> </div> <div class="wrap-right"> right </div> <div class="clear-both"></div> <div class="footer"> you can see </div> {% endblock %}
9.4.2.增加样式
.article-title-size {
font-size: 30px;
}
#标题位置及颜色
.article-title-brief {
margin-top: 10px;
color: #999;
}
#图片位置及占比
.article-detail-head-img {
margin-top: 25px;
margin-bottom: 25px;
width: 100%;
}
#文字间距
.article-content {
line-height: 30px
}
9.4.3.方法修改增加板块
def article_detail(request,article_id): article_obj = models.Article.objects.get(id=article_id) return render(request,'bbs/article_detail.html',{'article_obj':article_obj, 'category_list': category_list,})
9.4.4.查看页面
9.5.添加评论及CSRF
9.5.1.新增评论输入框
base.html 新增继承block
base.html新增csrf
article_detail.html
{% extends 'base.html' %} {% load custom %} {% block page-container %} <div class="wrap-left"> <!-- 文章标题 --> <div class="article-title-size"> {{ article_obj.title }} </div> <div class="article-title-brief"> <span>作者:{{ article_obj.author.name }}</span> <span>{{ article_obj.pub_date }}</span> <span> <!-- 获取评论数 --> <span class="glyphicon glyphicon-comment" aria-hidden="true"></span> {% filter_comment article_obj as comments %} {{ comments.comment_count }} </span> </div> <!-- 文章图片 --> <div class="article-content"> <img class="article-detail-head-img" src="/static/{{ article_obj.head_img|truncate_url }}"> <!-- 文章内容 --> {{ article_obj.content }} </div> <div class="comment-box"> {% if request.user.is_authenticated %} <!-- 判断用户已经登录,输入框输入评论 --> <textarea class="form-control" rows="3"></textarea> <!-- 按钮样式 --> <button type="button" style="margin-top: 10px;" class="btn btn-success pull-right">评论</button> {% endif %} </div> </div> <div class="wrap-right"> right </div> <div class="clear-both"></div> {% endblock %} {% block bottom-js %} <script> //定义方法,获取csrf内容 function getCsrf() { return $("input[name='csrfmiddlewaretoken']").val(); } $(document).ready(function () { $(".comment-box button").click(function () { var comment_text = $(".comment-box textarea").val() if (comment_text.trim().length < 5){ alert("评论字数不能少于5"); }else{ //post $.post("{% url 'post_comment' %}", { //1为评论 'comment_type':1, article_id:"{{ article_obj.id }}", parent_comment_id:null, //评论内容 'comment':comment_text.trim(), //获取csrf 'csrfmiddlewaretoken':getCsrf() }, function(callback){ console.log(callback); })//end post }//end if });//end button click }); </script> {% endblock %}
9.5.2.新增url
from django.conf.urls import url,include from bbs import views urlpatterns = [ url(r'^$', views.index), url(r'category/(\d+)/$', views.category), url(r'detail/(\d+)/$', views.article_detail,name="article_detail"), url(r'comment/$', views.comment,name="post_comment"), ]
9.5.3.新增返回方法
def comment(request): print(request.POST) return HttpResponse('xxx')
9.5.4.提交评论
输入正常评论:
9.6.增加登录评论
9.6.1.增减登录静态显示
<div class="comment-box"> {% if request.user.is_authenticated %} <!-- 判断用户已经登录,输入框输入评论 --> <textarea class="form-control" rows="3"></textarea> <!-- 按钮样式 --> <button type="button" style="margin-top: 10px;" class="btn btn-success pull-right">评论</button> {% else %} <div class="jumbotron"> <!-- 点击登录后,跳转到原先准备评论的页面,也就是给原路径加next?xxx --> <h4 class="text-center"><a class="btn-link" href="{% url 'login' %}?next={{ request.path }}">登录</a>后评论</h4> </div> {% endif %} </div>
9.6.2.修改方法
def acc_login(request): if request.method == 'POST': print(request.POST) user = authenticate(username=request.POST.get('username'), password=request.POST.get('password')) if user is not None: login(request,user) #return HttpResponseRedirect('/bbs') return HttpResponseRedirect(request.GET.get('next') or '/bbs') else: login_err = "Worng user name or password" return render(request,'login.html',{'login_err':login_err}) return render(request,'login.html')
9.6.3.评论测试
10.分级显示评论
10.1.增加展示方法
#!/usr/bin/env python # -*- coding: utf-8 -*- def add_node(tree_dic,comment): if comment.parent_comment is None: #我就是顶层,我放在这 tree_dic[comment] = {} else: #循环当前整个dict,直到找到为止 for k,v in tree_dic.items(): if k == comment.parent_comment:#找到爹了 tree_dic[comment.parent_comment][comment] = {} else:#进入下一层继续找 add_node(v,comment) def render_tree_node(tree_dic,margin_val): html = "" for k, v in tree_dic.items(): ele = "<div class='comment-node' style='margin-left:%spx'>" % margin_val + k.comment + "<span style='margin-left:20px'>%s</span>" %k.date \ + "<span>%s</span>" %k.user.name +"</div>" html += ele html += render_tree_node(v,margin_val+10) return html def render_comment_tree(tree_dic): html = "" for k,v in tree_dic.items(): ele = "<div class='root-comment'>" + k.comment + "<span style='margin-left:20px'>%s</span>" %k.date \ + "<span>%s</span>" %k.user.name + "</div>" html += ele html += render_tree_node(v,10) return html def build_tree(comment_set): #print(comment_set) tree_dic = {} for comment in comment_set: add_node(tree_dic,comment) print("-"*10) for k,v in tree_dic.items(): print(k,v) return tree_dic
10.2.增加views方法
from django.shortcuts import render from bbs import models from django.contrib.auth import login,logout,authenticate from django.contrib.auth.decorators import login_required from django.shortcuts import render,HttpResponse,HttpResponseRedirect from bbs import comment_hander import json category_list = models.Category.objects.filter(set_as_top_menu=True).order_by("position_index") # Create your views here. def index(request): category_obj = models.Category.objects.get(position_index=1) article_list = models.Article.objects.filter(status='published') return render(request,'bbs/index.html',{'category_list':category_list, 'article_list': article_list, 'category_obj':category_obj }) def category(request,id): category_obj = models.Category.objects.get(id=id) if category_obj.position_index == 1:#全部 article_list = models.Article.objects.filter(status='published') else: article_list = models.Article.objects.filter(category_id = category_obj.id,status='published') return render(request, 'bbs/index.html',{'category_list':category_list, 'category_obj':category_obj, 'article_list':article_list }) def acc_login(request): if request.method == 'POST': print(request.POST) user = authenticate(username=request.POST.get('username'), password=request.POST.get('password')) if user is not None: login(request,user) #return HttpResponseRedirect('/bbs') return HttpResponseRedirect(request.GET.get('next') or '/bbs') else: login_err = "Worng user name or password" return render(request,'login.html',{'login_err':login_err}) return render(request,'login.html') def acc_logout(request): logout(request) return HttpResponseRedirect('/bbs') def article_detail(request,article_id): article_obj = models.Article.objects.get(id=article_id) #多级评论树 comment_tree = comment_hander.build_tree(article_obj.comment_set.select_related()) return render(request,'bbs/article_detail.html',{'article_obj':article_obj, 'category_list': category_list,}) def comment(request): print(request.POST) if request.method == 'POST': new_comment_obj = models.Comment( article_id = request.POST.get('article_id'), parent_comment_id = request.POST.get('parent_comment_id') or None, comment_type = request.POST.get("comment_type"), user_id = request.user.userprofile.id, comment = request.POST.get("comment") ) new_comment_obj.save() return HttpResponse('success') def get_comments(request,article_id): article_obj = models.Article.objects.get(id=article_id) comment_tree = comment_hander.build_tree(article_obj.comment_set.select_related()) tree_html = comment_hander.render_comment_tree(comment_tree) return HttpResponse(tree_html)
10.3.增加url
from django.conf.urls import url,include from bbs import views urlpatterns = [ url(r'^$', views.index), url(r'category/(\d+)/$', views.category), url(r'detail/(\d+)/$', views.article_detail,name="article_detail"), url(r'comment/$', views.comment,name="post_comment"), url(r'^comment_list/(\d+)/$', views.get_comments,name="get_comments"), ]
10.4.增加样式
.comment-node {
border: 1px dashed blue;
padding: 5px;
}
.root-comment {
border: 1px dashed brown;
padding: 5px;
}
10.4.评论展示增加
{% extends 'base.html' %} {% load custom %} {% block page-container %} <div class="wrap-left"> <!-- 文章标题 --> <div class="article-title-size"> {{ article_obj.title }} </div> <div class="article-title-brief"> <span>作者:{{ article_obj.author.name }}</span> <span>{{ article_obj.pub_date }}</span> <span> <!-- 获取评论数 --> <span class="glyphicon glyphicon-comment" aria-hidden="true"></span> {% filter_comment article_obj as comments %} {{ comments.comment_count }} </span> </div> <!-- 文章图片 --> <div class="article-content"> <img class="article-detail-head-img" src="/static/{{ article_obj.head_img|truncate_url }}"> <!-- 文章内容 --> {{ article_obj.content }} </div> <div class="comment-box"> {% if request.user.is_authenticated %} <!-- 判断用户已经登录,输入框输入评论 --> <textarea class="form-control" rows="3"></textarea> <!-- 按钮样式 --> <button type="button" style="margin-top: 10px;" class="btn btn-success pull-right">评论</button> {% else %} <div class="jumbotron"> <!-- 点击登录后,跳转到原先准备评论的页面,也就是给原路径加next?xxx --> <h4 class="text-center"><a class="btn-link" href="{% url 'login' %}?next={{ request.path }}">登录</a>后评论</h4> </div> {% endif %} <div class="comment-list"> </div> </div> <button type="button" onclick="getComments()">测试评论</button> </div> <div class="wrap-right"> right </div> <div class="clear-both"></div> {% endblock %} {% block bottom-js %} <script> //评论展示 function getComments() { $.get("{% url 'get_comments' article_obj.id %}",function(callback){ console.log(callback); $(".comment-list").html(callback); }); } //定义方法,获取csrf内容 function getCsrf() { return $("input[name='csrfmiddlewaretoken']").val(); } $(document).ready(function () { $(".comment-box button").click(function () { var comment_text = $(".comment-box textarea").val() if (comment_text.trim().length < 5){ alert("评论字数不能少于5"); }else{ //post $.post("{% url 'post_comment' %}", { //1为评论 'comment_type':1, article_id:"{{ article_obj.id }}", parent_comment_id:null, //评论内容 'comment':comment_text.trim(), //获取csrf 'csrfmiddlewaretoken':getCsrf() }, function(callback){ console.log(callback); if (callback == "success"){ alert("hahhahahahha") } })//end post }//end if });//end button click }); </script> {% endblock %}
10.5.查看展示结果