django博客项目-文章详情页功能

文章详情页左侧边栏数据复用

文章详情页和个人站点的左侧边栏的内容格式都是一样的,从个人站点路由和文章详情路由进入到网页,侧边栏显示的内容是一模一样,解决方案:
方案一:
写一个home_site.html和article_detail.html建立一个新的母版base.html,两者继承base.html,在将所需的数据从对应的视图函数中传入即可。这只是解决了html代码重复问题。视图函数代码的问题是,在文章详情页和个人站点的视图函数中都要获取相同的数据,存在了数据代码冗余问题。
方案二:
利用inclusion_tag来解决方案一中的代码冗余。将样式和数据整合为一个整体,在返回到html中。

inclusion_tag方案设计

  1. 在对应应用下创建templatetags文件夹,文件夹的名字必须为这个。
  2. 在templatetags文件夹下创建my_tags.py文件,这个文件的名字可以随意。
  3. 在settings.py文件中添加my_tags的配置
    TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
            'libraries':{
                    "my_tags": "blog.templatetags.my_tags",
            },
        },
    
    },
    

]
```
准备工作完毕,下面添加正式代码

代码

前端代码:
classification.html

<div class="panel panel-warning">
    <div class="panel-heading">我的标签</div>
    <div class="panel-body">
        {% for tag in tag_list %}
            <p><a href="/{{ username }}/tag/{{ tag.title }}/">{{ tag.title }}({{ tag.c }})</a></p>
        {% endfor %}
    </div>
</div>
<div class="panel panel-danger">
    <div class="panel-heading">我的分类</div>
    <div class="panel-body">
        {% for category in category_list %}
            <p><a href="/{{ username }}/category/{{ category.title }}/">{{ category.title }}({{ category.c }})</a></p>
        {% endfor %}
    </div>
</div>
<div class="panel panel-info">
    <div class="panel-heading">随笔归档</div>
    <div class="panel-body">
        {% for date in date_list %}
        <p><a href="/{{ username }}/archive/{{ date.month|date:'Y-m' }}/">{{ date.month|date:'Y-m' }}({{ date.c }})</a></p>
        {% endfor %}
    </div>
</div>

base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/css/bootstrap.min.css">
    <script src="/static/js/jquery.min.js"></script>
    <script src="/static/js/bootstrap.min.js"></script>
    <style>
        *{
            margin: 0;
            padding: 0
        }
        .header{
            width:100%;
            height:60px;
            background-color: #369;
        }
        .header .title{
            font-size: 18px;
            font-weight: 100;
            line-height: 60px;
            color: white;
            margin-left: 15px;
            margin-top: -10px;
        }
        .backend{
            float:right;
            color:white;
            text-decoration: none;
            margin-right:10px;
            margin-top:10px;
            font-size:14px;
        }
        .pub_info{
            margin-top:10px;
            color:dark-green;
        }
    </style>
</head>
<body>
<div class="header">
    <div class="content">
        <p class="title">
            <span class="">{{ blog.title }}</span>
            <a href="" class="backend">管理</a>
        </p>
    </div>
</div>
<div class="container">
    <div class="row">
        <div class="col-md-3">
            {% load my_tags %}
            {% get_classification_style username %}

        </div>
        <div class="col-md-9">
            {% block content %}
            {% endblock %}
        </div>
    </div>
</div>
</body>
</html>

home_site.html

{% extends 'base.html' %}

{% block content %}
    <div class="article_list">
        {% for article in article_list %}
        <div class="article_item clearfix">
            <h5><a href="">{{ article.title }}</a></h5>
            <div class="article-desc">
                {{ article.desc }}
            </div>
            <div class="small pub_info pull-right">
                <span><a href="">{{ article.user.username }}</a></span>&nbsp;&nbsp;
                <span>发布于&nbsp;&nbsp;{{ article.create_time|date:"Y-m-d H:i" }}</span>&nbsp;&nbsp;&nbsp;
                <span class="glyphicon glyphicon-comment"></span>评论({{ article.comment_count }})&nbsp;&nbsp;
                <span class="glyphicon glyphicon-thumbs-up"></span>点赞({{ article.up_count }})
            </div>

        </div>
        <hr>
        {% endfor %}
    </div>
{% endblock %}

article_detail.html

{% extends 'base.html' %}

后端代码:

# urls.py
"""cnblog URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.10/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
from django.contrib import admin
from django.views.static import serve
from cnblog import settings
from blog import views as blog_views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', blog_views.index),
    url(r'^login/', blog_views.login, name="login"),
    url(r'^logout/', blog_views.logout, name="logout"),
    url(r'^register', blog_views.register, name="register"),
    url(r'^index/', blog_views.index, name="index"),
    url(r'^get_validCode_img', blog_views.get_validCode_img),

    # media 配置
    url(r'media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}),

    # 个人站点
    url(r'^(?P<username>\w+)/$', blog_views.home_site),

    # 个人站点跳转
    url(r'^(?P<username>\w+)/(?P<condition>tag|category|archive)/(?P<param>.*)/$', blog_views.home_site),

    # 文章详情
    url(r'^(?P<username>\w+)/article/(?P<article_id>\d+)/$', blog_views.article_detail),
]

# views.py
def home_site(request, username, **kwargs):
    user = UserInfo.objects.filter(username=username).first()
    # 判断用户是否存在
    if not user:
        # 返回404
        return render(request, 'not_found.html')

    # 查询出当前用户对应的站站点信息
    blog = user.blog

    # 基于对象的所有文章查询
    # article_list = user.article_set.all()
    # 基于 __ 查询
    article_list = models.Article.objects.filter(user=user)
    if kwargs:
        condition = kwargs.get('condition')
        param = kwargs.get('param')

        if condition == 'category':
            # article_list = models.Article.objects.filter(user=user, category__title=param)
            article_list = article_list.filter(category__title=param)
        elif condition == 'tag':
            # article_list = models.Article.objects.filter(user=user, tags__title=param)
            article_list = article_list.filter(tags__title=param)
        elif condition == 'archive':
            year, month = param.split('-')
            print(year, month)
            # article_list = models.Article.objects.filter(
            #     user=user,
            #     create_time__year=year,
            #     create_time__month=month
            # )
            article_list = article_list.filter(create_time__year=year, create_time__month=month)
        else:
            pass

    return render(request, 'home_site.html', {'username': username, 'blog': blog, 'article_list': article_list})


def article_detail(request, username, article_id):
    return render(request, 'article_detail.html', locals())
# my_tags.py
from django import template
from django.db.models.functions import TruncMonth
from django.db.models import Count
from blog import models

register = template.Library()


@register.inclusion_tag("classification.html")
def get_classification_style(username):
    print(username)
    user = models.UserInfo.objects.filter(username=username).first()
    blog = user.blog

    category_list = models.Category.objects.filter(blog=blog).annotate(c=Count('article__title')).values('title', 'c')
    tag_list = models.Tag.objects.filter(blog=blog).annotate(c=Count('article__title')).values('title', 'c')
    date_list = models.Article.objects.filter(user=user).annotate(month=TruncMonth('create_time')).values(
        'month').annotate(c=Count('pk')). \
        values('month', 'c')

    return {'user': user, 'blog': blog, 'category_list': category_list, 'tag_list': tag_list, 'date_list': date_list, 'username': username}

知识点

模板继承
inclusion_tag

执行流程

  1. 访问http://127.0.0.1:8080/lxt/
  2. 进入路由层进行匹配,匹配到url(r’^(?P\w+)/$’, blog_views.home_site)
  3. 进入对应视图层blog_views.home_site,返回home_site.html网页
  4. 在home_site.html中继承了base.html,先加载base.html
  5. base.html中执行了inclusion_tag的语法{% load my_tags %}
    {% get_classification_style username %}
  6. 加载get_classification_style,执行get_classification_style中的代码,将执行完拿到的数据返回到@register.inclusion_tag(“classification.html”)对应的classification.html进行数据和样式整合。
  7. 将数据和样式整合之后的html代码返回到调用get_classfication_style的地方
  8. 在base.html中将样式和数据整合为一体的html代码展示到调用处
  9. home_site.html网页完成加载

每次加载base.html时,其他网页的视图函数就不需要重复传入左侧边栏的数据内容,解决了代码冗余。

style样式抽出

对base.html中的style样式提出到单独的css文件隔离出home_site.css和article_detail.css样式

代码

home_site.css

*{
    margin: 0;
    padding: 0
}
.header{
    width:100%;
    height:60px;
    background-color: #369;
}
.header .title{
    font-size: 18px;
    font-weight: 100;
    line-height: 60px;
    color: white;
    margin-left: 15px;
    margin-top: -10px;
}
.backend{
    float:right;
    color:white;
    text-decoration: none;
    margin-right:10px;
    margin-top:10px;
    font-size:14px;
}
.pub_info{
    margin-top:10px;
    color:dark-green;
}

article_detail.css

.article_info .title{
    margin-bottom: 20px;
}

base.html
引入两个css样式

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/css/home_site.css">
    <link rel="stylesheet" href="/static/css/article_detail.css">
    <script src="/static/js/jquery.min.js"></script>
    <script src="/static/js/bootstrap.min.js"></script>

</head>

点赞样式构建

代码

article_detail.css

.article_info .title{
    margin-bottom: 20px;
}
#div_digg {
    float: right;
    margin-bottom: 10px;
    margin-right: 30px;
    font-size: 12px;
    width: 125px;
    text-align: center;
    margin-top: 10px;
}
.diggit {
    float: left;
    width: 46px;
    height: 52px;
    background: url("/static/fonts/upup.gif") no-repeat;
    text-align: center;
    cursor: pointer;
    margin-top: 2px;
    padding-top: 5px;
}
.buryit {
    float: right;
    margin-left: 20px;
    width: 46px;
    height: 52px;
    background: url("/static/fonts/downdown.gif") no-repeat;
    text-align: center;
    cursor: pointer;
    margin-top: 2px;
    padding-top: 5px;
}
.clear {
    clear: both;
}
.diggword {
    margin-top: 5px;
    margin-left: 0;
    font-size: 12px;
    color: #808080;
}

article_detail.html

<div id="div_digg">
    <div class="diggit">
        <span class="diggnum" id="digg_count">0</span>
    </div>
    <div class="buryit">
        <span class="burynum" id="bury_count">0</span>
    </div>
    <div class="clear"></div>
    <div class="diggword" id="digg_tips" style="color: red;"></div>
</div>

效果预览

在这里插入图片描述

文章点赞

给推荐和反对绑定同一个点击事件,当点击推荐的时候传递到后端的是True,当点击反对的时候传递到后端的是False。
给div_digg下的两个div标签起同一个名字添加事件。
一个用户只能赞同或者反对一次,再次点击会提示已经操作过了,不能重复操作。

代码

article_detail.html

<div id="div_digg">
    {% csrf_token %}
    <div class="diggit action">
        <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
    </div>
    <div class="buryit action">
        <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
    </div>
    <div class="clear"></div>
    <div class="diggword" id="digg_tips" style="color: red;"></div>
</div>

<script>
    $('#div_digg .action').click(function (){
        var is_up = $(this).hasClass("diggit")
        $.ajax({
            url:"/digg/",
            type:"post",
            data:{
                "csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val(),
                "is_up":is_up,
                "article_id":{{ article_obj.pk }}
            },
            success: function (data){
                console.log(data.state, data.handle)
                if (data.state){
                    if (is_up){
                        var val =parseInt($('#digg_count').text())+1;
                        $('#digg_count').text(val)
                    }
                    else{
                        var val =parseInt($('#bury_count').text())+1;
                        $('#bury_count').text(val)
                    }
                }
                else{
                    if (data.handle){
                        $('#digg_tips').html("您已经推荐过")
                    }
                    else{
                        $('#digg_tips').html("您已经反对过")
                    }
                    setTimeout(function(){
                        $('#digg_tips').html("")
                    }, 1000)

                }
            }
        })
    })
</script>

views.py

def digg(request):
    article_id = request.POST.get("article_id")
    is_up = request.POST.get("is_up")  # 拿到的是一个字符串
    is_up = json.loads(is_up)

    response = {'state': True, 'handle': None}
    obj = models.ArticleUpDown.objects.filter(user=request.user, article_id=article_id).first()
    if not obj:
        # 用户没有进行赞同或者反对,生成新的数据
        models.ArticleUpDown.objects.create(user=request.user, article_id=article_id, is_up=is_up)
        queryset = models.Article.objects.filter(pk=article_id)
        if is_up:
            # 用户点击赞同,对article中的upcount进行加1
            queryset.update(up_count=F("up_count")+1)
        else:
            queryset.update(down_count=F("down_count")+1)
    else:
        response['state'] = False  # state 代表用户已经进行过赞同或者反对了
        response['handle'] = obj.is_up  # handle 代表用户进行的是赞同还是反对
    return JsonResponse(response)

urls.py

url(r'^digg/', blog_views.digg),

效果展示

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值