Django笔记08-博客首页(2)

博客首页

博客首页除了显示数据之外,还有登陆跳转、分页显示、搜索等

登陆

 django 内置的后台管理应用就是处理注册登陆、管理文章等的应用。在fbckf/urls.py 中规定了匹配后台应用的 url规则 ,在浏览器中输入127.0.0.1:8000/admin可进入登陆界面。

 点击首页中的 Sign in 按钮将跳转到登陆界面

模板

<!-- templates/blog/index.html -->
...
<!-- nav -->
<div class="container">
    <nav class="navbar navbar-light border-bottom" style="background-color: white">
        <span class="navbar-brand font-italic">
            Fbckf
        </span>
        <div class="my-2 my-lg-0">
            <!-- 将 button 标签修改为以下内容 -->
          {% if username %}
          <a href="/admin" class="btn btn-light text-dark" role="button">{{ username }}</a>
          {% else %}
          <a href="/admin" class="btn btn-light" role="button">Sign in</a>
          {% endif %}
        </div>        
    </nav>
</div>
...

url 规则

# fbckf/urls.py
...
urlpatterns = [
    path('admin/', admin.site.urls),
    # 修改首页 url 规则
    path('', include('blog.urls')),
]
...

 修改完成之后刷新页面,点击 Sign in 按钮时成功跳转到登陆页面,在后台点击查看站点返回首页。成功登陆之,如果没有注销登陆,浏览器会有缓存,之后访问后台都不用再次输入账户密码。

视图函数

# blog/views.py

...
def index(request):
    ...
    # 判断用户是否登陆,若用户已登陆在线则将用户名存放到字典 context 中传递到模板
    if request.user.is_authenticated:
        context['username']= request.user.username
    return render(request, 'blog/index.html', context=context)
...

 刷新博客首页,Sign in 按钮已经改为 fbckf,在将用户注销登陆,刷新博客首页,按钮有恢复为 Sign in

文章排序

 首页的文章是按照发布时间来排序的,从最新到最旧的顺序。而之前文章从数据库中取出的顺序是相反的,文章排序需要修改有一下视图函数

# blog/views.py

...

def index(request):
    # 通过 order_by() 方法来给文章排序
    # order_by() 的参数是 指定排序的字段,字段前的'-'好表示逆序
    article_list = Article.objects.all().order_by('-create_time') 
...

 刷新是首页,文章已正确排序。

分页

 当文章增加到一定数量的时候,就必须要用到分页功能,将文章按一定数量分页显示。

测试数据

 现在的数据库中,仅有两篇文章,这是远远不够的。分页功能需较多的测试数据,而一个一个的手动输入文章及其麻烦,所以只能通过脚本来输入,而且使用脚本的话,也可以保证在团队中
测试数据的一致性

# populate.py
"""
数据脚本
"""
import os 
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'fbckf.settings')

import django
django.setup()

from blog.models import Category, Article
from django.contrib.auth.models import User
from django.utils import timezone


def run_script():
    author = User.objects.get(username='fbckf')
    category = Category.objects.get(name='古诗')
    article = add_article(
        title="望江南 超然台作",
        author=author,
        category=category,
        body="春未老,风细柳斜斜。试上超然台上看,半壕春水一城花。烟雨暗千家。寒食后,酒醒却咨嗟。休对故人思故国,且将新火试新茶。诗酒趁年华。",
    )
    article = add_article(
        title="寄黄几复",
        author=author,
        category=category,
        body="我居北海君南海,寄雁传书谢不能。桃李春风一杯酒,江湖夜雨十年灯。持家但有四立壁,治病不蕲三折肱。想见读书头已白,隔溪猿哭瘴溪藤。",
    )
    article = add_article(
        title="侠客行",
        author=author,
        category=category,
        body="赵客缦胡缨,吴钩霜雪明。银鞍照白马,飒沓如流星。十步杀一人,千里不留行。事了拂衣去,深藏身与名。闲过信陵饮,脱剑膝前横。将炙啖朱亥,持觞劝侯嬴。三杯吐然诺,五岳倒为轻。眼花耳热后,意气素霓生。救赵挥金槌,邯郸先震惊。千秋二壮士,烜赫大梁城。纵死侠骨香,不惭世上英。谁能书阁下,白首太玄经。",
    )
    article = add_article(
        title="画堂春 一生一代一双人",
        author=author,
        category=category,
        body="一生一代一双人,争教两处销魂。相思相望不相亲,天为谁春。浆向蓝桥易乞,药成碧海难奔。若容相访饮牛津,相对忘贫。",
    )
    article = add_article(
        title="丑奴儿 书博山道中壁",
        author=author,
        category=category,
        body="少年不识愁滋味,爱上层楼。爱上层楼。为赋新词强说愁。 而今识尽愁滋味,欲说还休。欲说还休。却道天凉好个秋。",
    )
    article = add_article(
        title="折桂令 春情",
        author=author,
        category=category,
        body="平生不会相思,才会相思,便害相思。身似浮云,心如飞絮,气若游丝。空一缕余香在此,盼千金游子何之。证候来时,正是何时?灯半昏时,月半明时。",
    )
    article = add_article(
        title="凤求凰",
        author=author,
        category=category,
        body="其一:【琴曲出自王实甫《西厢记》】有美一人兮,见之不忘。(有美人兮,见之不忘。)一日不见兮,思之如狂。凤飞翱翔兮,四海求凰。无奈佳人兮,不在东墙。将琴代语兮,聊写衷肠。何时见许兮,慰我彷徨。愿言配德兮,携手相将。不得於飞兮,使我沦亡。",
    )
    article = add_article(
        title="子衿",
        author=author,
        category=category,
        body="青青子衿,悠悠我心。纵我不往,子宁不嗣音?青青子佩,悠悠我思。纵我不往,子宁不来?挑兮达兮,在城阙兮。一日不见,如三月兮。",
    )
    article = add_article(
        title="贺新郎 甚矣吾衰矣",
        author=author,
        category=category,
        body="邑中园亭,仆皆为赋此词。一日,独坐停云,水声山色,竞来相娱。意溪山欲援例者,遂作数语,庶几仿佛渊明思亲友之意云。甚矣吾衰矣。怅平生、交游零落,只今馀几!白发空垂三千丈,一笑人间万事。问何物、能令公喜?我见青山多妩媚,料青山见我应如是。情与貌,略相似。一尊搔首东窗里。想渊明《停云》诗就,此时风味。江左沉酣求名者,岂识浊醪妙理。回首叫、云飞风起。不恨古人吾不见,恨古人不见吾狂耳。知我者,二三子。",
    )

def add_category(name, instructions):
    """
    get_or_create() 获取或创建一个对象 并返回一个元组
    """
    c = Category.objects.get_or_create(name=name)[0]
    c.instructions = instructions
    c.save()
    return c
    
def add_article(title, author, category, body):
    a = Article.objects.get_or_create(title=title)[0]
    a.author = author
    a.category = category
    a.body = body
    a.abstract = body[:17]
    a.create_time = timezone.now()
    a.likes = 0
    a.save()
    return a

if __name__ == "__main__":
    print("Blog populate script running ...")
    run_script()
# 运行脚本插入数据
$ python populate.py

 查看后台文章列表

image

视图函数

 django 提供了管理分页的类 Paginator,可以快速的完成分页功能呢改,在官方文档中也有简单的例子,在视图函数中导入Paginator

# blog/views.py
from django.shortcuts import render
# 导入 Paginator 类
from django.core.paginator import Paginator
from django.contrib.auth.models import User
from .models import Category, Article

# 修改 index 函数
def index(request):
    # 创建字典 context
    context = {}

    article_list = Article.objects.all().order_by('-create_time')
    # 判断文章数量是否需要分页
    if article_list.count > 10:
        # 实例化一个 Paginator 类
        # 第一个参数是要进行分页的文章列表,第二个参数表示每页文章数量
        paginator = Paginator(article_list, 3)
        # 从 request 中获取目标页面数
        page = request.GET.get('page')
        # 首次访问页面 page 为 None
        if page == None:
            page=1
        # 调用 page 方法获取目标页面的文章列表
        page_list = paginator.page(page)
        context['article_list'] = page_list
        # 通过该变量判断是否分页
        context['is_page'] = True
    else:   
        context['article_list'] = article_list
        
    category_list = Category.objects.all()
    context['category_list'] = category_list

    for category in category_list:
        count = Article.objects.all().filter(category=category).count()
        category.article_count = count
        category.save()

    if request.user.is_authenticated:
        context['username']= request.user.username

    return render(request, 'blog/index.html', context=context)

 每次用户点击页面导航栏中的页码后,通过 request.GET.get() 获取页码 page,再获取页码对应的文章列表并传递给模板。

模板文件

<!-- templates/blog/index.html -->
...
{% if is_page %}
<!-- Pagination -->
<div class="justify-content-center">
    <nav aria-label="Page navigation">
        <ul class="pagination justify-content-center">
          <!-- 跳转到第一页 -->
          <li class="page-item">
            <a href="?page=1" class="page-link bg-light text-dark" aria-label="Previous">
              <span aria-hidden="true">&laquo;</span>
              <span class="sr-only">Previous</span>
            </a>
          </li>
        {% ifequal article_list.number article_list.paginator.num_pages %}
          <li class="page-item"><a href="?page={{ article_list.number|add:-2 }}" class="page-link bg-light text-dark">{{ article_list.number|add:-2 }}</a></li>
        {% endifequal %}

        {% ifnotequal article_list.number|add:-1 0 %}
          <li class="page-item"><a href="?page={{ article_list.number|add:-1 }}" class="page-link bg-light text-dark">{{ article_list.number|add:-1 }}</a></li>
        {% endifnotequal %}
          <!-- 当前页 -->
          <li class="page-item disabled"><a href="?page={{ article_list.number }}" class="page-link bg-dark text-light">{{ article_list.number}}</a></li>

        {% ifnotequal article_list.number article_list.paginator.num_pages %}
          <li class="page-item"><a href="?page={{ article_list.number|add:1 }}" class="page-link bg-light text-dark">{{ article_list.number|add:1 }}</a></li>
        {% endifnotequal %}

        {% ifequal article_list.number|add:-1 0 %}
          <li class="page-item"><a href="?page={{ article_list.number|add:2 }}" class="page-link bg-light text-dark">{{ article_list.number|add:2 }}</a></li>
        {% endifequal %}
          <!-- 跳转到最后一页 -->
          <li class="page-item">
            <a href="?page={{ article_list.paginator.num_pages}}" class="page-link bg-light text-dark" aria-label="Next">
              <span aria-hidden="true">&raquo;</span>
              <span class="sr-only">Next</span>
            </a>
          </li>
        </ul>
    </nav>
</div>
{% endif %}
...

 找到模板中的分页导航部分,即 `` 后面部分。

  • 首先修改 href 属性,?page=1 表示通过 GET 方法向后台提交数据 page=1,通过这个方式向后台传递目标页码

  • {% ifnotequal %}{% ifequal %} 标签分别判断两个值是否相等,当变量 a 不等于 变量 b{% ifnotequal a b %} 会执行包含在标签内的代码,而 {% ifequal a b %} 标签则相反

  • {{ article_list.number|add:1 }} 返回的结果是变量 article_list.number 加上 1 的值

 再次刷新页面,分页功能已经完成。

搜索

 用户可以根据关键字所搜文章,关键字在文章内容和标题中匹配

模板文件

 需要修首页中的搜索表单以及错误信息显示

<!-- templates/blog/index.html -->
...
<div class="sidebar-module">
    <div class="input-group mb-3 content">
        <div class="input-group-prepend">
            <span class="input-group-text" id="basic-addon1">
                <i class="fa fa-search" aria-hidden="true"></i>
            </span>
        </div>
        <form action="{% url 'blog:search' %}" method="post">
        {% csrf_token %}
            <input name="query" type="text" class="form-control" placeholder="Search" aria-label="Search" aria-describedby="basic-addon1">
        </form>
    </div>
</div>
...

 在模板文件中找到搜索表单,并按照以上代码修改。

  • {% url 'blog:search' %} 就是 127.0.0.1:8000/search的的意思,调用命名为 'blog' 的 urls.py 模块下名称为 'search'的 url 规则。

  • {% csrf_token %} 标签会被替换成一个隐藏的 <input> 标签 ,该标签会在上传搜索关键字的时候,上传一个名字为 csrfmiddlewaretoken 的变量,这个是 django 用以保护网站不受黑客 csrf 攻击的一种机制。

<!-- templates/blog/index.html -->
...
{% for article in article_list %}
<div class="jumbotron bg-light">
    <h1 class="display-4">{{ article.title }}</h1>
    <p class="lead">{{ article.abstract }}</p>
    <hr class="my-4">
    <p class="text-muted">{{ article.create_time }} , by  {{ article.author }}</p>
    <a href="#" class="btn btn-dark btn-lg" role="button">Read more</a> 
</div>
{% empty %}
    <!-- 判断是否有错误信息 -->
    {% if error_msg %}
    <div class="jumbotron bg-light">
        <p class="lead">{{ error_msg }}</p>
    </div>
    {% else %}      
    <div class="jumbotron bg-light">
        <p class="lead">暂无文章,敬请期待!</p>
    </div>
    {% endif %}
{% endfor %}
...

添加视图函数

# blog/views.p
from django.db.models import Q
...
def search(request):
    context = {}
    # 从 request 请求中获取关键字
    q = request.POST.get('query')
    # 判断关键字是否存空 
    # 是则返回错误信息
    if q is None or q=="":
        error_msg = "请输入关键词!"
        return render(request, 'blog/index.html', {'error_msg': error_msg})
    # Q 用于包装查询表达式,提供复杂的查询逻辑
    # 用 filter 过滤出含有关键字的文章
    # | 表示或
    article_list = Article.objects.all().filter(Q(title__icontains=q) | Q(body__icontains=q))
    context['article_list'] = article_list

    category_list = Category.objects.all()
    context['category_list'] = category_list

    if request.user.is_authenticated:
        context['username']= request.user.username
    return render(request, 'blog/index.html', context=context)
...

添加 url 规则

from django.urls import path
from . import views

app_name = 'blog'
urlpatterns = [
    path('', views.index, name='index'),
    # 将规则命名为 search 之后就可以通过 {% url 'blog:search' %} 使用
    path('search/', views.search , name='search'),
]

  搜索功能完成后,就可以在搜索表单中输入关键字来查询想要的文章。当然,这个搜索功能还很简单,没有关键字高亮,而且当文章一多的时候,效率就会大大下降,假如要解决这个问题,就需要建立数据库索引,这个这里不多说。

 而想要通过其他字段来查询文章的话,也差不多如此,比如查询作者,先使用关键字查询并实例化相应的作者,在通过该对象查询文章并返回即可。

总结

 首页的功能到这里就大致完成了,接下来就是完成文章详情页。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容