未加星标

Build a blog with Django: Display published posts

字体大小 | |
[开发(python) 所属分类 开发(python) | 发布者 店小二03 | 时间 2017 | 作者 红领巾 ] 0人收藏点击收藏

In this post, I'd show you how to display your published posts to your readers. There are many ways to go about this task but I'd show you the best practice way.

Our journey would take us through many parts of the framework. In particular, we'd touch on managers , get_absolute_url , URL patterns , views and templates .

I hope you enjoy the ride.

Let's get started.

Quick recap

This task is part of a larger feature we're adding to our blog which startedhere when we first created the posts app. Since then we've done the following:

Added a post model, seehere.

Added post administration capabilities, seehere.

And, added a custom management command to seed posts. Seehere,here andhere.

If you haven't been following along, then be sure to check out those posts I've linked to above.

If you have been following along, then congratulations on reaching this far. We're almost done with the feature.

Here's the commit that marks our starting point for today.

Goals

What do we want to accomplish here?

We want a page that lists all our published posts in reverse chronological order. If there are no posts then we should show an empty state .


Build a blog with Django: Display published posts

But, if there are posts then for each post we should display its title and excerpt.


Build a blog with Django: Display published posts

The title should be a link which takes you to a detail page for that post. And, on the detail page we should show its title and body content.


Build a blog with Django: Display published posts

The reader should NEVER be allowed to view a draft post. And, attempting to do so MUST return a 404 page.


Build a blog with Django: Display published posts

N.B. Since DEBUG = True we don't actually see the 404 page but we do see that a 404 response was returned.

Retrieving published posts in reverse chronological order

A Django model maps to a single database table. In order to perform database query operations on that table you have to go through a manager. By default, at least one manager, named objects , exists for every model in a Django application.

Hence, Post.objects returns the default manager.

Once you have the manager you use it to construct a QuerySet . A QuerySet represents a collection of objects from your database.

The QuerySet we need is

Post.objects.filter(is_published=True).order_by('-published_at')

i.e. all published posts in reverse chronological order.

The filter method returns a new QuerySet containing objects that match the given lookup parameters. And, the order_by method orders the results.

The above works but since we'll be needing this exact QuerySet in many places in the application we can do something to keep the code DRY .

One thing we can do is to create a custom manager and modify its initial QuerySet . Open posts/models.py and edit it to contain:

class PublishedPostManager(models.Manager): def get_queryset(self): return super().get_queryset() \ .filter(is_published=True) \ .order_by('-published_at') class Post(models.Model): objects = models.Manager() published_objects = PublishedPostManager() # ...

By setting a custom manager, published_objects , the default one isn't automatically created for us. Since we do want the default objects manager as well, we have to create it for ourselves.

Now, when we do Post.published_objects.all() we get all the published posts in reverse chronological order.

We only scratched the surface of what's available.

Learn more about the concepts we covered by checking out the following links:

Managers Making queries QuerySets The posts index page

Next let's work on displaying the list of the published posts in reverse chronological order.

Within the src/posts create a URLconf called urls.py and edit it to contain:

from django.conf.urls import url from . import views app_name = 'posts' urlpatterns = [ url(r'^$', views.index, name='index'), ]

N.B. The app_name and name values allow us to be independent of the URL structure. You'd see what I mean when we make use of it later on.

Two things are missing for this to work:

A connection to the root URLconf, and The index view function.

To connect it to the root URLconf go to yaba/urls.py and update it as follows:

from django.conf.urls import include urlpatterns = [ # ... url(r'^blog/', include('posts.urls')), # ... ]

Then, open posts/views.py and replace its contents with:

from django.shortcuts import get_object_or_404, render from .models import Post def index(request): posts = Post.published_objects.all() return render(request, 'posts/index.html', { 'posts': posts })

We're almost here.

If you navigate to http://127.0.0.1:8000/blog/ you'd get a TemplateDoesNotExist exception. Try it and see for yourself. To fix this we need to create our template and tell Django where to find it.

In the src directory create a templates folder with the following structure:

templates/ ├── posts │ ├── detail.html │ └── index.html └── yaba ├── index.html └── layouts └── base.html

Open templates/yaba/layouts/base.html and edit it to contain:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>{% block title %}Yet Another Blog{% endblock %}</title> </head> <body> {% block main_content %}{% endblock %} </body> </html>

Open templates/posts/index.html and edit that to contain:

{% extends "yaba/layouts/base.html" %} {% block main_content %} {% for post in posts %} <div> <h2><a href="#">{{ post.title }}</a></h2> <p>{{ post.excerpt }}</p> </div> {% empty %} <p>No posts.</p> {% endfor %} {% endblock %}

Lastly, edit the TEMPLATES setting in the settings file yaba/settings.py and change the DIRS option to be as follows:

TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ os.path.join(BASE_DIR, 'templates'), ], # ... }, ]

That's it.

Create some posts, start up the development server,

(venv) $ cd src (venv) $ python manage.py createposts --unpublished 2 --published 5 (venv) $ python manage.py runserver

and navigate to http://127.0.0.1:8000/blog/ , to see them.

N.B. Checkhere to learn how the custom createposts management command was implemented.

We had to know a little about a lot of different parts of the Django framework but that's how it usually unfolds as you develop a feature.

For more in-depth knowledge about any particular area we covered I'd suggest you read the following:

URL dispatcher Writing views Templates Template inheritance The extends tag The block tag The post detail page

Let's get those links working.

Starting with the URLconf, add the following pattern to posts/urls.py :

urlpatterns = [ # ... url(r'^(?P<slug>[\w-]+)/$', views.detail, name='detail'), ] The regular expression, [\w-]+ , matches one or more Unicode word characters or dashes in any combination. Hence, it allows us to give our posts URLs like http://127.0.0.1:8000/blog/hello-world or http://127.0.0.1:8000/blog/learn-python-and-django .

The ?P<slug> part tells the URL dispatcher to capture the match, i.e. hello-world or learn-python-and-django , and pass it to the view function as a keyword argument named slug .

Open posts/views.py and add the detail view function:

from django.shortcuts import get_object_or_404 def detail(request, slug): post = get_object_or_404(Post.published_objects, slug=slug) return render(request, 'posts/detail.html', { 'post': post })

The get_object_or_404 function calls get on the Post.published_objects model manager and raises Http404 if the post couldn't be found. Read here to learn more.

Now, add the following to the templates/posts/detail.html template file:

{% extends "yaba/layouts/base.html" %} {% block title %}{{post.title}} - {{ block.super }}{% endblock %} {% block main_content %} <h1>{{ post.title }}</h1> <div> {{ post.body }} </div> {% endblock %}

N.B. For SEO purposes we update the title of the page to include the title of the post.

Finally, link each post to their detail page by updating the links in templates/posts/index.html .

<a href="{% url 'posts:detail' post.slug %}">{{ post.title }}</a>

'posts' is the namespace we set when we configured app_name in posts/urls.py . And, 'detail' is the name we gave to the URL pattern that points to the detail view. Hence, posts:detail refers to said URL pattern. It requires the slug to generate the correct URL and so we give it the slug from the current post.

get_absolute_url

The get_absolute_url is an instance method you can define on a model to tell Django how to calculate the canonical URL for an instance of the model.

Django uses get_absolute_url in the admin and the syndication feed framework .

Edit posts/models.py and add the following to the existing Post model:

from django.urls import reverse class Post(models.Model): def get_absolute_url(self): return reverse('posts:detail', kwargs={'slug': self.slug})

We use the reverse function so that we don't have to depend on the actual URL.

This allows us to refactor the link on the detail page to:

<a href="{{ post.get_absolute_url }}">{{ post.title }}</a> Update the home page

Finally, I took the liberty to update the home page. At this point in the app we were still using a hard-coded home page.

But, with the introduction of templates we can refactor the home page to make use of one.

Open templates/yaba/index.html and edit it to contain:

{% extends "./layouts/base.html" %} {% block main_content %} <p>Hello, world! Check out my <a href="{% url 'posts:index' %}">blog</a>.</p> {% endblock %}

Open yaba/urls.py and replace url(r'^$', views.home) with url(r'^$', views.index) .

Finally, replace the contents of yaba/views.py with:

from django.shortcuts import render def index(request): return render(request, 'yaba/index.html') Wrap up

Woo hoo! The feature is complete.


Build a blog with Django: Display published posts

All the changes we've made today can be found here .

The pull request tracking this feature can be found here .

Next up, Markdown support.

See you then.

P.S. Let me know in the comments if you got into any problems. I'd be happy to help you resolve them.

P.S.S. Subscribe to my newsletter if you're interested in getting exclusive Django content.

本文开发(python)相关术语:python基础教程 python多线程 web开发工程师 软件开发工程师 软件开发流程

主题: DjangoSEORY
分页:12
转载请注明
本文标题:Build a blog with Django: Display published posts
本站链接:http://www.codesec.net/view/532350.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 开发(python) | 评论(0) | 阅读(68)