基于 Django 的个人网站(4)

2020-05-27 11:40:11 浏览数 (1)

上回说到,虽然已经基本上把显示的格式改的差不多了,但是界面还是不够美观,同时也没有实现分类页面和检索文章页面,今天我们先来实现分类页面和检索文章的页面。

分类页面的实现

考虑到分类页面是一个用来展示属于当前类别的所有文章,和首页一样的格式,就是显示的列表项十有八九会比首页少,因此分类页面的视图直接继承首页的视图,然后重写 get_queryset 方法就完事了,代码如下:

代码语言:javascript复制
class CategoryView(IndexView):
    def get_queryset(self):
        return super().queryset.filter(categories__in=Category.objects.filter(id=self.kwargs['category_id'])).order_by(
            '-id')

然后去配置一下 URL,向 urlpatterns 列表中添加一项,代码如下:

代码语言:javascript复制
path('categories/<category_id>', CategoryView.as_view())

最后去对应的模板文件中增加带有超链接的导航栏,代码如下:

代码语言:javascript复制
<nav>
    <a href="/">首页</a>
    {% for category in categories %}
        <a href="/categories/{{ category.id }}">{{ category }}</a>
    {% endfor %}
</nav>

运行之后点击分类的超链接就跳到分类页面,分类页面从逻辑上来说就已经实现了,可能会有排版和导包的问题,最后我会给出 views.py 的源码和 html 文件的源码,我们先不管了,接着去实现搜索页面,最后再一起测试。

搜索页面的实现

要想搜索首先必须有一个输入框,一个按钮,这两个组件直接使用 html 的表单就可以完成了,表单代码如下:

代码语言:javascript复制
<form action="/search" method="get">
    <label>
        <input name="keyword" type="text">
    </label>
    <input type="submit" value="搜索">
</form>

接下来我们就去想一下搜索页面是个什么样的格式——其实还是和主页面差不多,同样继承主页面对应的视图,然后重写 get_queryset 方法就完事了,代码如下:

代码语言:javascript复制
class SearchView(IndexView):
    def get_queryset(self):
        keyword = self.request.GET.get('keyword', '')
        return super().queryset if not keyword else super().queryset.filter(Q(title__icontains=keyword) | Q(
            abstract__icontains=keyword))

我为了性能考虑,就不从文章内容中匹配关键字了,只从标题和摘要中匹配一下,分类也不需要考虑,一篇文章对应多个分类,模糊检索本来就很耗费时间,多个分类就对应了多个模糊检索。

表单有了,视图有了,最后我们直接配置 URL,在 urlpatterns 列表中添加一项,代码如下:

代码语言:javascript复制
path('search/', SearchView.as_view())

现在该实现的也基本全都实现了,接下来我就给出视图、模板和 URL 的完整代码,首先是视图 personal_websiteviews.py,代码如下:

代码语言:javascript复制
from django.db.models import Q
from django.views.generic import DetailView, ListView
from.models import Article, Category


# Create your views here.
class IndexView(ListView):
    context_object_name = 'articles'
    paginate_by = 10
    queryset = Article.objects.filter(state=1).order_by('-id')
    template_name = 'index.html'

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super().get_context_data()
        context['categories'] = Category.objects.order_by('-id')
        return context


class ArticleDetailView(DetailView):
    context_object_name = 'article'
    model = Article
    template_name = 'article_detail.html'

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super().get_context_data()
        context['categories'] = Category.objects.order_by('-id')
        return context


class CategoryView(IndexView):
    def get_queryset(self):
        return super().queryset.filter(categories__in=Category.objects.filter(id=self.kwargs['category_id'])).order_by(
            '-id')


class SearchView(IndexView):
    def get_queryset(self):
        keyword = self.request.GET.get('keyword', '')
        return super().queryset if not keyword else super().queryset.filter(Q(title__icontains=keyword) | Q(
            abstract__icontains=keyword))

接下来是 URL 配置的代码,对应文件为 PersonalWebsiteurls.py,代码如下:

代码语言:javascript复制
from django.contrib import admin
from django.urls import path, include
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.conf import settings
from django.conf.urls.static import static
from personal_website.views import IndexView, ArticleDetailView, CategoryView, SearchView
urlpatterns = [
    path('admin/', admin.site.urls),
    path("ckeditor5/", include('django_ckeditor_5.urls')),
    path('', IndexView.as_view()),
    path('articles/<pk>', ArticleDetailView.as_view()),
    path('categories/<category_id>', CategoryView.as_view()),
    path('search/', SearchView.as_view())
] static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) staticfiles_urlpatterns()

最后是两个模板文件,首先是列表页的模板文件——templatesindex.html,代码如下:

代码语言:javascript复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>陈志豪的个人网站</title>
    <style>
        p{
            text-indent: 2em;
        }
</style>
</head>
<body>
<!--suppress HtmlDeprecatedAttribute -->
<h1 align="center">陈志豪的个人网站</h1>
<nav>
    <a href="/">首页</a>
    {% for category in categories %}
        <a href="/categories/{{ category.id }}">{{ category }}</a>
    {% endfor %}
</nav>
<!--suppress HtmlUnknownTarget -->
<form action="/search" method="get">
    <label>
        <input name="keyword" type="text">
    </label>
    <input type="submit" value="搜索">
</form>
{% for article in articles %}
    <h2><a href="/articles/{{ article.id }}">{{ article.title }}</a></h2>
    <p>{{ article.abstract }}</p>
{% endfor %}
<p>
    {% if is_paginated %}
        {% if page_obj.has_previous %}
            <a href="?page={{ page_obj.previous_page_number }}">上一页</a>
        {% endif %}
        &nbsp;第&nbsp;{{ page_obj.number }}&nbsp;页/共&nbsp;{{ page_obj.paginator.num_pages }}&nbsp;页&nbsp;
        {% if page_obj.has_next %}
            <a href="?page={{ page_obj.next_page_number }}">下一页</a>
        {% endif %}
    {% endif %}
</p>
</body>
</html>

其次是文章详情页面的模板文件——templatesarticle_detail.html,代码如下:

代码语言:javascript复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ article.title }}</title>
    <link href="/static/css/prism.css" rel="stylesheet" />
    <style>
        p{
            text-indent: 2em;
        }
        figure{
            text-align: center;
            margin: 0 auto;
        }
</style>
</head>
<body>
<!--suppress HtmlDeprecatedAttribute -->
<h1 align="center">陈志豪的个人网站</h1>
<nav>
    <a href="/">首页</a>
    {% for category in categories %}
        <a href="/categories/{{ category.id }}">{{ category }}</a>
    {% endfor %}
</nav>
<!--suppress HtmlUnknownTarget -->
<form action="/search" method="get">
    <label>
        <input name="keyword" type="text">
    </label>
    <input type="submit" value="搜索">
</form>
<!--suppress HtmlDeprecatedAttribute -->
<h1 align="center">{{ article.title }}</h1>
<p>{{ article.abstract }}</p>
{{ article.content|safe }}
<script src="/static/js/prism.js"></script>
</body>
</html>

最后我们来看一下运行的效果,运行项目进入首页,如图所示。

这个时候虽然有点怪怪的,但是导航栏和搜索框都有了,我们虽点在导航栏中点击一个类别,我在这里点击类别5,如图所示。

看到 URL 最后是 5 就说明没有问题,同时也可以去管理后台看显示的这两篇文章是不是有类别5 这个分类。接下来我们就尝试搜索,在搜索框中输入一个东西,看看是否可以被检索到,我在这里直接输入 2,然后点击搜索,最后搜索的结果如图所示。

说明这可以实现对标题或者摘要的模糊搜索,其实可以多测试几个,我就不进行测试了。

今天的内容基本上结束了,最后讲一下怎么安装配置 bootstrap。

bootstrap 的安装

在安装 bootstrap 之前,我们首先需要了解一下什么是 bootstrap,Bootstrap,来自 Twitter,是目前最受欢迎的前端框架。Bootstrap 是基于 HTML、CSS、JAVASCRIPT 的,它简洁灵活,使得 Web 开发更加快捷。

首先进入 bootstrap 官网(https://www.bootcss.com/)把东西下载下来,下载好之后应该会得到一个压缩文件,我们进行解压,解压之后如图所示。

接下来不用去看都应该知道怎么弄了吧,把这里 css 文件夹下面所有文件复制到项目中的 css 静态文件目录,同时把 js 文件夹中的所有文件复制到项目中的 js 静态文件目录,完成之后项目中静态文件目录如图所示。

好,今天就讲到这里,明天我就直接给出修改好的模板文件的源码,同时给出运行的效果。

0 人点赞