用python搭建一个校园维基网站(二)—— 可编辑内容的首页的创建

项目总体简介请看用python搭建一个校园维基网站(一)
本文可独立使用,创建了一个可编辑内容的首页,展示了wagtail的一些基础用法。文末为本文所创项目文件github地址。
比较详细,新手可尝试,不过最好有一定Django基础。

项目结构概观

  • 首先使用wagtail start geniuspip install wagtail安装依赖)创建名为genius的工程文件夹,cd genius进入目录。
  • 在windows命令行输入tree /a /f > 1.txt在当前目录下生成1.txt看到如下的项目结构。
E:.
|   manage.py
|   requirements.txt
|   
+---genius
|   |   urls.py
|   |   wsgi.py
|   |   __init__.py
|   |   
|   +---settings
|   |       base.py
|   |       dev.py
|   |       production.py
|   |       __init__.py
|   |       
|   +---static
|   |   +---css
|   |   |       genius.css
|   |   |       
|   |   \---js
|   |           genius.js
|   |           
|   \---templates
|           404.html
|           500.html
|           base.html
|           
+---home
|   |   models.py
|   |   __init__.py
|   |   
|   +---migrations
|   |       0001_initial.py
|   |       0002_create_homepage.py
|   |       __init__.py
|   |       
|   \---templates
|       \---home
|               home_page.html
|               
\---search
    |   views.py
    |   __init__.py
    |   
    \---templates
        \---search
                search.html
  1. manage.pyDjango项目通用的管理脚本(通过python manage.py 某命令参数使用)。
  2. requirements.txt用于存储当前项目的依赖列表(自动生成的为Djangowagtail,虚拟环境(virtualenv)下可用pip freeze >> requirements.txt追加)。
  3. genius包含项目主要信息,有主路由(urls.py)、wsgi接口(wsgi.py)、配置文件夹(分基础配置base.py、开发环境配置dev.py与生产环境配置production.py,后二者依赖基础配置)、全局静态资源文件夹(static)与模板资源文件夹(templates)。
  4. home是自动生成的app文件夹,包含了models.py页面数据模型和templates模板文件夹。默认生成的models.py中定义了一个简单的HomePage类(继承自wagtailPage类)来代表一个页面(即默认的欢迎页)的模型(该简单模型的可编辑内容部分只有title字段)。在wagtail的概念中,页面模型和模板文件是默认关联的,如HomePage默认对应的模板为templates/home/home_page.html(注意命名的转换关系),而欢迎页http://127.0.0.1:8000中的大部分内容就在该模板中(该模板使用extends语句继承genius\templates\base.html,并使用block语句填充相应内容)。如下:
# 在命令行中先迁移数据库再启动服务,即可在本地查看欢迎页面
python manage.py migrate
python manage.py runserver
欢迎页

欢迎页模板 - templates/home/home_page.html
  1. search则是自动生成的提供搜索功能的app文件夹,由于基于wagtail.wagtailsearch所以只包含了views.py视图文件和templates模板文件夹。暂时不管。

创建wiki主页

  • 我们先清空数据库,python manage.py flush或者直接删除db.sqlite3数据库文件。
  • 在项目根目录下删除home文件夹,新建一个名为wiki的文件夹代表wikiapp,并将genius\settings\base.py配置文件中第28行左右的INSTALLED_APPS列表中的home改为wiki,以此来向配置文件注册我们的app。并在wiki文件夹里添加目录和空文件:
\---wiki
    |   models.py
    |   __init__.py
    |   
    +---migrations
    |       __init__.py
    |       
    \---templates
        \---wiki
  • 现在创建我们的主页模型,主要元素如下:

    我们的WikiHome页面模型中需要图中红色高亮的一系列字段,其中title字段继承自Page类,不用额外添加,image字段为连接到wagtailimages.Image模型的外键。content_panels列表提供了该页面模型在后台管理编辑页面的呈现内容。
    此外,对于TopLinkLittleIntros我们需要另外新建两个继承wagtail提供的Orderable(使有序)的非页面模型。

    WikiHomeLittleIntros的字段有fontawesome图标类名,小标题和简述,如下图。还包含了一个wagtail提供的对ForeignKey进行了一层封装的ParentalKey外键连接到它所属的WikiHome页面。类似的,panels表明出现在可编辑区。


    WikiHomeTopLink类似,为了层次上更清晰,采用了多重继承,在models.py中只定义ParentalKey外键,而在另一个文件中定义了RelatedLink模型,包含的字段有链接文本和具体链接,只是具体链接可能为外链、某个页面或某个文档,占用了三个字段,此外还利用@property装饰器为该模型添加了link属性,来返回它的具体链接,这样在模板中就可以使用.link调用。
    综上,models.py的内容为:
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals

from django.db import models
from modelcluster.fields import ParentalKey

from wagtail.wagtailcore.models import Page, Orderable
from wagtail.wagtailadmin.edit_handlers import (
    FieldPanel, InlinePanel)
from wagtail.wagtailimages.edit_handlers import ImageChooserPanel

from .umodels import RelatedLink


# ------------------------主页-------------------------
class WikiHome(Page):
    logoname = models.CharField(
        max_length=255,
        help_text=u"显示在左上角的网页名称"
    )
    image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text=u"大幅背景图"
    )
    intro = models.TextField(
        blank=True,
        help_text=u"下方简单口号"
    )

    content_panels = Page.content_panels + [
        FieldPanel('logoname'),
        InlinePanel('toplinks', label="顶部右侧链接"),
        ImageChooserPanel('image'),
        FieldPanel('intro', classname="full"),
        InlinePanel('little_intros', label="下方一排推广简述"),
    ]


class WikiHomeTopLink(Orderable, RelatedLink):
    page = ParentalKey('wiki.WikiHome', related_name='toplinks')


class WikiHomeLittleIntros(Orderable):
    page = ParentalKey(WikiHome, related_name='little_intros')
    fa_name = models.CharField(blank=True, max_length=250,
                               help_text=u'''FontAwesome图标类名
                               - 参考fontawesome.io/icons/''')
    title = models.CharField(blank=True, max_length=250,
                             help_text=u"小标题")
    caption = models.CharField(blank=True, max_length=1000,
                               help_text=u"简述")

    panels = [
        FieldPanel('fa_name'),
        FieldPanel('title'),
        FieldPanel('caption'),
    ]

models.py旁新建umodels.py文件供models.py引用:

#--------------------------umodels.py----------------------------#
from django.db import models
from wagtail.wagtailadmin.edit_handlers import (FieldPanel,
                                                PageChooserPanel,
                                                MultiFieldPanel)
from wagtail.wagtaildocs.edit_handlers import DocumentChooserPanel


class LinkFields(models.Model):
    link_external = models.URLField("External link", blank=True)
    link_page = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        related_name='+'
    )
    link_document = models.ForeignKey(
        'wagtaildocs.Document',
        null=True,
        blank=True,
        related_name='+'
    )

    @property
    def link(self):
        if self.link_page:
            return self.link_page.url
        elif self.link_document:
            return self.link_document.url
        else:
            return self.link_external

    panels = [
        FieldPanel('link_external'),
        PageChooserPanel('link_page'),
        DocumentChooserPanel('link_document'),
    ]

    class Meta:
        abstract = True


# Related links
class RelatedLink(LinkFields):
    title = models.CharField(max_length=255, help_text="链接显示文本")

    panels = [
        FieldPanel('title'),
        MultiFieldPanel(LinkFields.panels, "Link"),
    ]

    class Meta:
        abstract = True
  • 这样,我们的wiki首页模型就定义好了。
  • 该创建页面模型对应的模板了,在wiki文件夹下新建templates\wiki\wiki_home.html模板文件。
  • 首先要考虑的是模板的大概样式该怎么做,感谢开源世界,我们找到了轻量美观的purecss框架。
    它有一系列现成的layouts供我们使用,选择最适合本次主页的样式,查看源码可以得到详细的信息,在这里,为了简便,我们直接使用了该layout的额外样式表的链接(最好处理为本地的css样式文件,使用Django的static标签引用)。
    对于模板来说,它对应的页面模型处于它的上下文环境,在模板中可以调用到该页面模型中的所有元素(使用Django的模板语言)。我们要按照页面排版将元素填充进去。
    修改wiki_home.html中内容如下:
{% extends "base.html" %}
{% load wagtailcore_tags wagtailimages_tags %}

{% block extra_css %}
    <link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/pure-min.css">

    <link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/grids-responsive-min.css">

    <link rel="stylesheet" href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css">

    <link rel="stylesheet" href="https://purecss.io/combo/1.18.13?/css/layouts/marketing.css">
{% endblock %}

{% block body_class %}wiki-homepage{% endblock %}

{% block content %}

    <div class="header">
        <div class="home-menu pure-menu pure-menu-horizontal pure-menu-fixed">
            <a class="pure-menu-heading" href="">{{ page.logoname }}</a>

            <ul class="pure-menu-list">
                <li class="pure-menu-item pure-menu-selected"><a href="{{ page.url }}" class="pure-menu-link" style="color:white">首页</a></li>
                {% for item in page.toplinks.all %}
                    <li class="pure-menu-item"><a href="{{ item.link }}" class="pure-menu-link">{{ item.title }}</a></li>
                {% endfor %}
            </ul>
        </div>
    </div>

    {% image page.image original as image %}
    <div class="splash-container" style="background-image: url({{ image.url }}); background-size:cover">
        <div class="splash">
            <h1 class="splash-head" style="color: black">{{ page.title }}</h1>
            <p class="splash-subhead">
            </p>
            <p>
                <form class="pure-form" action="/search">
                    <input type="text" name="query" placeholder="请在此输入搜索内容...">
                    <button type="submit" class="pure-button pure-button-primary">Get Started</button>
                </form>
            </p>
        </div>
    </div>
    
    <div class="content-wrapper">
        <div class="content">
            <h2 class="content-head is-center">{{ page.intro }}</h2>
            <div class="pure-g">
            
            {% for item in page.little_intros.all %}
                <div class="l-box pure-u-1 pure-u-md-1-2 pure-u-lg-1-4">
                
                    <h3 class="content-subhead">
                        <i class="fa {{ item.fa_name }}"></i>
                        {{ item.title }}
                    </h3>
                    <p> {{ item.caption }} </p>
                </div>
            {% endfor %}
            
            </div>
        </div>
        
        <div class="l-box-lrg pure-g" style="height:20px">
        </div>
        
        {% load wiki_tags %}
        {% wikihome_footer %}
        
    </div>
{% endblock %}

{% block extra_js %}

{% endblock %}
  • 这样,大致就成功了,但是模板中倒数几行里的{% load wiki_tags %} {% wikihome_footer %}还没有实现,它就是之前图中黄色框圈住的页脚了。考虑到页脚的内容一般比较固定,我们使用snippets和模板标签tag的形式来实现。它使得我们既可以在管理控制页面修改该页脚的内容,也使得页脚具有自己的一小段html模板,可以简便地被其它模板所调用。
  • wiki文件夹下的models.py文件旁新建一个snippets.py文件,添加如下内容:
from wagtail.wagtailsnippets.models import register_snippet
from django.db import models
from wagtail.wagtailcore.fields import RichTextField
from wagtail.wagtailadmin.edit_handlers import FieldPanel


@register_snippet
class FooterText(models.Model):

    body = RichTextField()

    panels = [
        FieldPanel('body'),
    ]

    def __str__(self):
        return u"页面最底部文本 - 限单条"

    class Meta:
        verbose_name_plural = u'页面最底部文本'

实际上,它还是创建了一个Django模型,只包含了一个富文本字段,但是利用Wagtail提供的register_snippet装饰器我们可以简便地将其注册到管理界面,以便在管理界面修改。但是,还不能在模板中调用它,我们需要将它注册到Django的tag标签系统中,在wiki目录下新建templatetags文件夹,在该文件夹下新建wiki_tags.py文件,添加如下内容。同样,借助简单的装饰器注册了该模板标签,且与wiki/tags/footer.html片段模板绑定,并提供footer_text作为上下文。

from django import template
from wiki.snippets import FooterText


register = template.Library()


@register.inclusion_tag('wiki/tags/footer.html', takes_context=True)
def wikihome_footer(context):
    footer_text = ""
    if FooterText.objects.first() is not None:
        footer_text = FooterText.objects.first().body

    return {
        'footer_text': footer_text,
    }

然后就该创建对应的片段模板文件了。与上面代码中绑定的html文件路径对应,在wikiapp目录下新建templates\wiki\tags\footer.html文件,添加如下内容:

{% load wagtailcore_tags %}

<div class="footer l-box is-center">
    {{ footer_text|richtext }}
</div>
  • 好了,主页的所有代码部分都结束了。让我们尝试运行。在项目根目录下执行:
python manage.py makemigrations # 创建数据库迁移文件
python manage.py migrate # 执行数据库迁移
python manage.py createsuperuser # 创建超级管理员,邮箱随意
python manage.py runserver # 启动服务
  • 登录管理界面:http://127.0.0.1:8000/admin/

    点击红圈部分来到如下图页面,删除默认页面。

    确认删除后,选择在根目录下新建页面

    这时便来到我们的创建的WikiHome模型的页面元素填写界面,依次填写后按红圈处Publish提交。

    页面创建好后,我们需要将其挂载到站点上来正常显示,点击下图红框创建站点

    如下图创建并保存,Root Page选择新创建的页面。

    最终,访问http://127.0.0.1:8000/ 便可以看到页面效果。
  • 不过细心的朋友可能会发现页脚还是空的,我们还需要在管理界面设置下页脚,点击snippets栏,并点击红圈



创建并保存


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

推荐阅读更多精彩内容