第二十 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.查看展示结果

 

posted @ 2018-02-07 21:15  ckl893  阅读(189)  评论(0编辑  收藏  举报