Django一个BLOG实现(参考)(三)

面向大众

至今为止我们做了一个不错的后端系统,但普通用户却看不见网站的内容。终端用户目前只会得到404错误页或者管理员登录界面(如果用户聪明到能发现它)。现在,让我们来添加一些东西。

首先,我们需要编辑urls.py文件来让我们的urls与我们之后要创建的函数联系起来。为了区别特定应用中的url细节,我们可以在posts中创建一个新的urls.py。这样做的好处在于,当我们想快速地向另一个工程中插入一个应用时,我们能简单地在urls.py中添加一行来把这个应用链接起来。那么,我们需要修改在工程文件夹内的urls.py中的urlpattern一项:

urlpatterns = patterns(”,
   (r’^', include(‘posts.urls’)),
   (r’^admin/’, include(admin.site.urls)),
)
像管理员url系统一样,我们利用项目中的urls.py来做url映射。由于项目中的urls.py是我们blog的主要特征,我们将其设为总url。

为什么在每个应用中都要添加urls.py呢?

这样做是为了让开发者自己定义应用被分配的层次。项目可以通过使用其中应用提供的urls.py或者自己实现urls.py来进行url映射。另外,项目自身可能含有其他特定的应用,用来向某个应用提供可选的view,它们使用的是相同的model,这种情况你需要更多的手工调整。尽管如此,应用中的view应当更加灵活,尽量避免不必要的重复。urls.py文件只是提供了简单化的项目实现以及有着默认url映射的未来的项目。


在你的posts应用目录下创建一个新的urls.py文件。应用中的urls.py和在项目中的urls.py作用一样。现在,让我们来实现两个前端的view:

1. 一个用来取得最近的帖子并展示出来的view

2. 一个用来展示特定的帖子及其评论的view

这里我们会使用到Django中内置的评论系统来实现我们需要的评论功能。但首先我们需要展示blog中的帖子。

Generic views

回到我们新建的urls.py。在解释之前,先加入下列代码。

from django.conf.urls.defaults import *
from models import Post
queryset = {‘queryset’: Post.objects.all()}
urlpatterns = patterns(‘django.views.generic.list_detail’,
    url(‘^$’, ‘object_list’, queryset, name=”posts”),
    url(‘^(?P<object_id>\d+)/$’, ‘object_detail’, queryset, name=”post”),
)

这里我们用到了Django提供的generic views,它帮我们省去了自己写函数代码的麻烦:增删改查操作。所以我们在这里使用它。url的前缀用来清楚地映射每个url。

我们import了自己定义的Post 模型,这样做是为了从数据库取得数据并传递到我们的view中。Generic views 接受一个称做queryset的附加参数,在其中包含着view用来取得数据库结果的对象查询。因为model查询是通过对象允许多种方式地取得model实例的属性。queryset是懒惰的求值程序不会有什么问题,于是在第二个url模式的view中会自动的将queryset中的数据缩小范围到某个特定的id而不用从数据库中取出所有的帖子。

Querysets 是懒惰的

Queryset会尽量避免数据库查询。这意味着,如下:

myqs = Post.objects.all()

其实并没有操作数据库。只在当结果中确切的数据被请求时,如:

    myqs.count() # uses a COUNT query
    myqs.get(id=1) # uses WHERE id=1, but expects to only fetch 1 record

    print myqs # executes the current query

当这样时,才会执行数据库查询。访问一个特定下标的值时也会自动执行queryset,但只执行一次:

myqs[0] # fetches all the query values, returns the first result

myqs[1] # doesn’t query since above did, returns the second result

你还可以建立一些基于queryset中数据的新的queryset,这么做也不会进行数据库操作:

import datetime

newqs = myqs.filter(pub_date__lte=datetime.datetime.now()) # no database call

newqs = newqs.exclude(id=1) # no database call

for post in newqs: # hits the database once and cached for iterating

    print post.title

如果你想了解更多的信息,可以参考Django官方文档“Querysets are Lazy” 与"When QuerySets are evaluated"

Url 函数

另一个新东西是我们用普通元组实现的url 函数。它提供了与我们之前用到的添加了一些特征的3元组一样的功能。其中最值得注意的特征叫做名称关键字。它允许开发者用一个自定义的名字来引用一个特定的url。这在我们写自己的模板是很有帮助。

最后如果你想知道post应用的url模式,可以参考官方文档regular expression named capture。如果你对正则表达式不太熟悉的话,它是用来抓取括号内的值,并将其命名为object_id后传到view中。在我们的例子中,我们匹配至少一位数字(\d+),这个值在generic view中用作id,

Order By

如果仔细看我们上面的urls.py中的代码,你会注意到我们并没有对帖子做任何排序通过用Post.objects.order_by('-pub_date')(它对所有的帖子按发布日期降序排列)替换掉Post.objects.all可以做到对帖子的简单排序。当然,你也可以自己添加更多的参数进行排序避免某个参数相等的情况。

Templates
现在让我们来为我们的两个公共view页做html模板。Django有一套定制模板语言,它大致可分为以下两种东西:
1. 变量输出用{{ variable }} 加上可选过滤器用{{ variable|filter_name }}
2.模板标签或函数调用通过 {% if variable_exists_or_not %}{% endif %},等。
尽管if语句看起来不像是个函数调用,但它确实是的。这里一个函数被简化为用一个关键字来代替函数调用,并且之后的值都被传入函数。
当然这里我们要介绍的不是好的html设计,所以就让html看起来越简单越好。现在,在你的项目目录中创建一个称做templates的文件夹(名字取决于你在settings.py中的命名)。


Base.html

Django模板系统的中心思想是扩展的使用。一个父模板用来定义一系列可以被子模板重载的块。这一点在实际使用后会变得很有体会。那么,让我们创建一个所有其他模板的基础模板,它的作用是统一网站的外观,就让我们命名为base.html。下面是代码:

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” xml:lang=”en” lang=”en”>
    <head>
        <meta http-equiv=”Content-type” content=”text/html; charset=utf-8″ />
        <title>My Blog</title>
        <link rel=”stylesheet” href=”{{ MEDIA_URL}}css/style.css” type=”text/css” media=”screen,projection” />
    </head>
    <body>
        <h1><a href=”/”>My Blog</a></h1>
        {% block content %}{% endblock %}
    </body>
</html>

在样式表中,我们输出在settings.py中定义的MEDIA_URL用来指明样式表的路径(这有一点超出了我们访问MEDIA_URL的范围,可以看官方文档RequestContext)。我们还没有将这些资源映射到开发服务器上的url,所以这些东西暂时没有作用,但我们之后会使用的到。


之后我们定义了这个模板中的一个称做content的块。当然,我们能在这个块不被重载的情况下向其中输入一些默认的html,但作为主要的内容,这部分位置是用来做替换的。

为了使MEDIA_URL正确工作,我们需要对我们的媒体文件做url映射。可以这样修改urls.py。(将下面代码加在你的文件底部)

from django.conf import settings
if settings.DEBUG:
urlpatterns += patterns(‘’,
(r‘^rsrc/(? .*)$’, ‘django.views.static.serve’, {
‘document_root’: settings.MEDIA_ROOT})
) 

关于django.views.static.serve的注意

这个view不是用来做成型产品这一点值得注意。这在Django的官方文档中清楚的描述了,这也是为什么我将其置于settings中的原因。如果出问题了就DEBUG。

上面的代码import了settings配置并且如果它是在DEBUG模式下,就会添加static serve content到url模式。你可以将rsrc改成任意你个人的MEDIA_URL(注意前面没有'/')。其他的东西都应当是可解释的。

Listing page

现在我们要实现generic view使用的模板。在urls.py定义的generic view会默认的到某些地方查找模板(你可以通过template_name参数定制这个行为)。当默认情况下,它会去为object_list查找<app_name>/<model_name>_list.html 以及为object_detail查找<app_name>/<model_name>_detail.html 。所以对于我们特定的应用,我们可以用include标签来包含另一个位于posts/_post.html的额外html文件。

首先是post_list.html:

{% extends ‘base.html’ %}
{% block content %}
{% for object in object_list %}
    <hr/>
    {% include ‘posts/_post.html’ %}
{% endfor %}
{% endblock %}

在这里显得相当简单。我们告诉Django我们继承所有base.html的内容并扩展它。我们重载content块,循环输出每一条帖子,并在帖子顶端加一条水平线做分隔,这就做到通过包含_post.html文件来展示帖子内容的目的。不知你是否注意到,所有模板路径是相对于基本模板目录而言的(所以我们要在前面加上'posts')。

你也许想知道我们如何获得object_list变量。object_list由object_list generic view 提供(在Django官方文档中有描述),在object_list中会包含我们帖子的列表。所以用python的方式,我们重复所有的帖子(post),并且为每个html块生成各自的对象。_post.html会继承所有在模板中可访问的变量,它就像被插入模板一样被包含其中。在这里我们保留帖子的命名object来区别于post,因为object_detail generic view 使用object变量来代表一个被取到的post。使用同样的变量使使用者能反复使用这个包含方式。


Detail page

我们的posts/post_detail.html 会看起来更简单:

{% extends ‘base.html’ %}
{% block content %}
{% include ‘posts/_post.html’ %}
{% endblock %}

没什么新东西需要解释。但我们同时需要实现_post.html模板:

<div id=”object_{{ object.id }}”>
<h2>
    <a href=”{% url post object.id %}”>{{ object.title }}</a>
    <small>{{ object.pub_date|timesince }} ago</small>
    </h2>
    {{ object.body }}
</div>

object变量代表了我们与之交互的post模型。所以 object.id, object.title, object.body都是我们之前定义的post模型中的字段,同样,他们还可以是字段,函数调用(无参数)。

在这里我们用到了url 模板标签。这个标签返回一个基于url模式名(在posts应用中urls.py定义的)和一个以逗号分隔的参数集合。参数值可以指定关键字名,例如 object_id=object.id,但那会使得代码看起来冗长尤其是一个参数时。

另外我们能添加一个’返回‘链接。在 post_detail.html 中加上:

<a href=”{% url posts %}”>Back to posts</a>

由于posts的url模式不需要任何参数,我们可以空着那个位置

在这个模板中,我们还使用了一个模板过滤器,timesince,来将datetime字段转成距当前的时间。模板过滤器只是用在变量输出中(即在{{ }}中),作用是改变输出的方式。这些过滤器是从左边接受参数的函数,并且是栈式的。所以我们可以这么做:{{ object.pub_date|timesince|upper }} 来使输出均为大写字母。

 

PS:

感觉这次翻译得比较糟,有些东西我能理解,但翻译过来发现看不大懂了。这里面比较值得注意的应该是url pattern 与 generic view,具体可参阅官方文档



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值