第三章Django by example

创建自定义的模版标签

自定义的模板标签 主要用于在每一个页面上都需要来显示,这样就方便了使用,而不需要在每一个views函数中render。
django提供了以下函数以简单的方式创建自己的模板标签:

  • simple_tag(简单标签):处理数据并返回一个字符串(string)
  • inclusion_tag(包含标签):处理数据并返回一个渲染过的模板(template)
  • assignment_tag(分配标签):处理数据并在上下文(context)中设置一个变量(variable)
    进入blog 应用文件夹:
blog/
    __init__.py
    models.py
    ...
    templatetags/        #新创建文件夹,设置成python packages
        __init__.py
        blog_tags.py

simple_tag的使用在blog_tags中键入代码

from django import template  # 导入django中的模板文件

from  blog.models import Post

register = template.Library()  # 注册


@register.simple_tag #使用装饰器函数定义此函数为一个简单标签(tag)并注册它
def total_posts():
    return Post.objects.all().count() #返回字符串

Django将会使用这个函数名作为标签(tag)名。如果你想使用别的名字来注册这个标签(tag),你可以指定装饰器的name属性,比如@register.simple_tag(name='my_tag')。

在模板中使用方法

{%load blog_tags %} #加载 blog_tags文件
  <p>This is my blog. I've written {% total_posts %} posts so far.</p>
#使用{% total_posts %}模板变量,返回一个字符串

inclusion_tag的使用:在blog_tags文件中键入

@register.inclusion_tag('blog/post/latest_post.html')  # 必须指定渲染的模板是哪个
def show_latest_tag(count=5):
  latest_posts = Post.objects.all().order_by('-publish')[:count]
  return {'latest_posts': latest_posts}  # 必须返回一个字典
在指定渲染的html中
<ul>
{% for post in latest_posts %}
  <li>
    <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
  </li>
{% endfor %}
</ul>

在base.html中增加,注:可以通过传入可选的评论数量值来使用显示,类似{% show_latest_posts 3 %}。

<div id="sidebar">
  <h2>My blog</h2>
  <p>This is my blog. I've written {% total_posts %} posts so far.</p>
  <h3>Latest posts</h3>
  {% show_latest_posts 3 %} # 此处会加载latest_post.html,且count=3传入
</div>


assigment_tag的使用
分配标签(assignment tag)类似简单标签(simple tags)但是他们将结果存储在给予的变量中。

from django.db.models import Count

@register.assignment_tag
def get_most_commented_posts(count=5):
    return Post.published.annotate(
                total_comments=Count('comments')
            ).order_by('-total_comments')[:count]
为Post模型中的每一个实例添加total_comments字段,此字段计算Comment模型中关联别名comments中的数量,再进行order_by排序

这个查询集(QuerySet)使用annotate()函数,为了进行聚合查询,使用了Count聚合函数。我们构建了一个查询集(QuerySet),聚合了每一个帖子的评论总数并保存在total_comments字段中,接着我们通过这个字段对查询集(QuerySet)进行排序。我们还提供了一个可选的count变量,通过给定的值来限制返回的帖子数量。
除了Count以外,Django还提供了不少聚合函数,例如Avg,Max,Min,Sum.你可以在 https://docs.djangoproject.com/en/1.8/topics/db/aggregation/ 页面读到更多关于聚合方法的信息。
在base.html中侧边栏添加

<h3>Most commented posts</h3>
{% get_most_commented_posts as most_commented_posts %}
<ul>
{% for post in most_commented_posts %}
  <li>
    <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
  </li>
{% endfor %}
</ul>

使用分配模板标签(assignment template tags)的方法是{% template_tag as variable %}。对于我们的模板标签(template tag)来说,我们使用{% get_most_commented_posts as most_commented_posts %}。 这样,我们可以存储这个模板标签(template tag)返回的结果到一个新的名为most_commented_posts变量中。之后,我们就可以用一个无序列表(unordered list)显示返回的帖子。


创建自定义的模板过滤器(template filters)

创建一个自定义的过滤器(filter),可以在我们的blog帖子中使用markdown语法,然后在模板(template)中将帖子内容转变为HTML。Markdown是一种非常容易使用的文本格式化语法并且它可以转变为HTML。
1.先安装markdown

pip3 install markdown

编辑blog_tags文件

from django.utils.safestring import mark_safe
import markdown

@register.filter(name='markdown')
def markdown_format(text):
    return mark_safe(markdown.markdown(text))
设定了过滤器,并名字为'markdown',为了避免与模板名起冲突,过滤函数取名为markdown_format,使用mark_safe来标记结果,正常情况下django不会依赖任何html代码并转义,除非mark_safe。

在templates中使用

{% load blog_tags %}
{{ post.body|markdown}}
#{{ post.body|truncateword:300|linebreaks }}
{{ post.body|truncateword_html:30 }}#truncateword_html过滤器(filter)会在一定数量的单词后截断字符串,避免没有关闭的HTML标签(tags)。

为你的站点添加一个站点地图(sitemap)

Django自带一个站点地图(sitemap)框架,允许你为你的站点动态生成站点地图(sitemap)。一个站点地图(sitemap)是一个xml文件,它会告诉搜索引擎你的网站中存在的页面,它们的关联和它们更新的频率。使用站点地图(sitemap),你可以帮助网络爬虫(crawlers)来对你的网站内容进行索引和标记。

Django站点地图(sitemap)框架依赖django.contrib.sites模块,这个模块允许你将对象和正在你项目运行的特殊网址关联起来。当你想用一个单独Django项目运行多个网站时,这是非常方便的。

SITE_ID = 1 #添加
# Application definition
INSTALLED_APPS = (
# ...
'django.contrib.sites',#添加
'django.contrib.sitemaps',#添加
)

创建表python3 manage.py makemigrations
python3 manage.py migrate

sites应用现在已经在数据库中进行了同步。现在,在你的blog应用目录下创建一个新的文件命名为sitemaps.py

from django.contrib.sitemaps import Sitemap

from .models import Post


class PostSitmap(Sitemap):  # 通过继承Sitemap类可以创建一个自定义的站点地图
    changefrep = 'weekly'  # 页面修改频率
    priority = '0,9'  # 网站中的关联性(优先级)

    def items(self):
        return Post.objects.all()  # items()方法返回了在这个站点地图(sitemap)中所包含对象的查询集(QuerySet)

    def lastmod(self,obj):
        return obj.publish  # lastmode方法接收items()返回的每一个对象并且返回对象的最后修改时间

可以在Django的官方文档 https://docs.djangoproject.com/en/1.8/ref/contrib/sitemaps/ 页面中获取更多的站点地图(sitemap)参考

在主urls文件中新增

from django.contrib.sitemaps.views import sitemap#导入sitemap视图
from blog.sitemaps import PostSitmap #导入自定义的sitemap类

sitemaps = {
    'posts':PostSitmap
}

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'^blog/', include('blog.urls', namespace='blog', app_name='blog')),
    url(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'),#新增
]

打开http://127.0.0.1:8000/sitemap.xml显示xml

为你的blog帖子创建feeds

Django有一个内置的syndication feed框架,就像用sites框架创建站点地图(sitemap)一样,使用类似的方式(manner),你可以动态(dynamically)生成RSS或者Atom feeds。

在blog应用的目录下创建一个新文件命名为feeds.py。添加如下代码:

from django.contrib.syndication.views import Feed  # 从django.普通.联合.视图 导入Feed类
from django.template.defaultfilters import truncatewords  # 导入 truncatechars

from .models import Post


class LatestPostsFeed(Feed):
    title = 'My blog'  # 标题
    link = '/blog/'  # 连接
    description = 'New posts of my Blog'  # 描述

    def items(self):
        return Post.objects.all()[:5]  # 返回前5个

    def item_title(self, item):
        return item.title  # 返回对象的title

    def item_description(self, item):  # 返回描述
        return truncatewords(item.body, 30)  # 文章的前30个单词

首先,我们继承了syndication框架的Feed类创建了一个子类。其中的title,link,description属性各自对应RSS中的<title>,<link>,<description>元素。

items()方法返回包含在feed中的对象。我们只给这个feed取回最新五个已发布的帖子。item_title()和item_description()方法接受items()返回的每个对象然后返回每个item各自的标题和描述信息。我们使用内置的truncatewords模板过滤器(template filter)构建帖子的描述信息并只保留最前面的30个单词。

现在,编辑blog应用下的urls.py文件,导入你刚创建的LatestPostsFeed,在新的URL模式(pattern)中实例化feed:

from .feeds import LatestPostsFeed

urlpatterns = [
    # ...
    url(r'^feed/$', LatestPostsFeed(), name='post_feed'),
]

在浏览器中转到 http://127.0.0.1:8000/blog/feed/ 。你会看到最新的5个blog帖子的RSS feedincluding:

最后一步是在blog的侧边栏(sitebar)添加一个feed订阅(subscription)链接。打开blog/base.html模板(template),在侧边栏(sitebar)的div中的帖子总数下添加如下代码:

<p><a href="{% url "blog:post_feed" %}">Subscribe to my RSS feed</a></p>

学习来源于夜夜月翻译的django by example

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

推荐阅读更多精彩内容