The home page displays a list of all articles. When the user sees an article he is interested in, he should click the title of the article or the continue to read button and jump to the details page of the article to read the detailed content of the article. Now let’s develop the details page of the blog. With the basics in place, the development process is the same: first configure the URL, that is, bind the relevant URL to the view function, then implement the view function, write the template and let the view function render the template.

Design the URL for the article details page

Reviewing the URL of our home page view, in the blog\urls.py file we wrote:

blog/urls.py

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^$', views.index, name='index'),]Copy the code

The URL matched by the home page view is an empty string without the domain name. For the article details view, each article corresponds to a different URL. For example, we could design the corresponding view of the article details page to look like this: When the user visits < web domain >/ POST /1/, the content of the first article is displayed, while when the user visits < web domain >/ POST /2/, the content of the second article is displayed, where the number represents the number of articles, that is, the ID value of the POST record in the database. We bind urls and views according to this rule:

blog/urls.py

from django.conf.urls import url

from . import views

app_name = 'blog'
urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^post/(? P
      
       [0-9]+)/$'
      , views.detail, name='detail'),]Copy the code

Django uses regular expressions to match the url a user visits. Where r ‘^ post/(? P [0-9]+)/$’ The entire regular expression matches the URL rule we defined above. The regular expression starts with POST/and is followed by at least one digit, and ends with /. For example, POST /1/ and POST /255/ are all valid. [0-9]+ indicates one or more digits. Besides here (? P< PK >[0-9]+) represents the named capture group, whose function is to capture the string matched in parentheses from the URL accessed by the user and pass it as a keyword argument to its corresponding view function detail. For example, when a user accesses post/255/ (note that Django doesn’t care about the domain name, but only the relative URL after the domain name is removed), the enclosed part (? P [0-9]+) matches 255, so 255 is passed in when the view function detail is called, which is actually what the view function is called: detail(request, pk=255). Here we have to capture the id of the article from the URL, because only then can we know which article the user is visiting.

If the above regular expressions are a little confusing to you, the regular expressions section is not django-related, but Python. Django just uses Python’s RE module for regular expressions here. So for a better understanding of regular expressions in Python, check out the documentation for the RE module in Python’s official documentation.

In addition, we passedapp_name='blog'Tell Django that the urls.py module belongs to the blog application. This technique is called the view function namespace. We see that blog\urls.py currently has two view functions and gives them individual names, index and detail, via the name attribute. However, a complex Django project may have more than one of these view functions. Some third-party applications, for example, may also have view functions called index and detail. The method is to specify the namespace with app_name. How to use the namespace is described below. If you forget to add this line to your blog\urls.py, you might end up with a NoMatchReversed exception.

To easily generate the above URL, we define a get_absolute_URL method in the Post class. Note that Post itself is a Python class, where we can define any method.

blog/models.py

from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
from django.utils.six import python_2_unicode_compatible

@python_2_unicode_compatible
class Post(models.Model):.def __str__(self):
        return self.title

    # Custom get_absolute_URL method
    Remember to import the reverse function from django.urls
    def get_absolute_url(self):
        return reverse('blog:detail', kwargs={'pk': self.pk})Copy the code

Notice that the URL (r’^post/(? P [0-9]+)/$’, views.detail, name=’detail’) If you look at the reverse function, its first parameter is ‘blog:detail’, which means the name=detail function under the blog application, Since we told Django above that the URL module belongs to the blog application with app_name = ‘blog’, Django was able to find the view function named detail in the blog application without any trouble. So the reverse function is going to parse the URL that this view function corresponds to, and the rule that the detail corresponds to is post/(? P [0-9]+)/ the regular expression is replaced by pk, so if the ID of Post (or pk, where pk and ID are equivalent) is 255, So get_absolute_URL returns /post/255/, so the post generates its own URL.

Write the detail view function

Now it’s time to implement our detail view function:

blog/views.py

from django.shortcuts import render, get_object_or_404
from .models import Post

def index(request):
    #...

def detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    return render(request, 'blog/detail.html', context={'post': post})Copy the code

The view function is simple. It retrieves the database record with that value based on the article ID we captured from the URL (that is, PK, where PK and ID are equivalent) and passes it to the template. Note that we are using the get_object_or_404 method imported from the Django.shortcuts module. This will return the corresponding PK Post if it exists in the database and a 404 error if it does not. Indicates that the requested article does not exist.

Write the detail page template

The next step is to write the template file, copy single.html from the downloaded blog template (click here to download it if you haven’t already) into the templates\blog directory (in the same level as index.html) and rename it detail.html. Your directory structure should look something like this:

blogproject\
    manage.py
    blogproject\
        __init__.py
        settings.py
        ...
    blog/
        __init__.py
        models.py
        ,,,
    templates\
        blog\
            index.html
            detail.htmlCopy the code

In the title and continue button of the blog post list on the Index page, write the hyperlink to jump to, that is, the URL of the details page corresponding to the post, so that users can jump to the detail page after clicking:

templates/blog/index.html

<article class="post post-1">
  <header class="entry-header">
    <h1 class="entry-title">
      <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
    </h1>.</header>
  <div class="entry-content clearfix">.<div class="read-more cl-effect-14">
      <a href="{{ post.get_absolute_url }}" class="more-link">Continue reading<span class="meta-nav">-</span></a>
    </div>
  </div>
</article>
{% empty %}
  <div class="no-post">The article that has not been published for the time being!</div>
{% endfor %}Copy the code

Here we modify two places, the first is the title of the article:

<h1 class="entry-title">
  <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
</h1>Copy the code

We changed the href value of the A tag to {{post. get_absolute_URL}}. To review the use of template variables, since the get_absolute_url method (defined in the Post class) returns the URL corresponding to the Post, So {{post.get_absolute_url}} will eventually be replaced with the URL of the post itself.

Also, the second change is the link to the continue button:

<a href="{{ post.get_absolute_url }}" class="more-link">Continue reading<span class="meta-nav">-</span>
</a>Copy the code

In this way, when we click the title of the article on the first page or continue to read the button, we will jump to the corresponding details page of the article. However, if you try to jump to the details page, you will find that the style is messy. As mentioned on the front page of the real Django blog, since we are copying templates directly, we haven’t handled static files properly. We can change the import path of static files as described above, but you will soon find that these static files need to be imported on any page, and it would be cumbersome to change them on every page, and the code would be repetitive. Here’s how Django template inheritance helps eliminate this duplication.

Template inheritance

We see that the index. HTML file and the detail. HTML file are identical except for the part wrapped in the main tag. We can extract the same part and put it in base.html. Start by creating a new base.html file in the templates\ directory. Your project directory should look something like this:

blogproject\
    manage.py
    blogproject\
        __init__.py
        settings.py
        ...
    blog\
        __init__.py
        models.py
        ,,,
    templates\
        base.html
        blog\
            index.html
            detail.htmlCopy the code

Copy the contents of index. HTML into the base. HTML file, then delete the contents wrapped in the main tag and replace them with the following contents.

templates/base.html

...
<main class="col-md-8">
    {% block main %}
    {% endblock main %}
</main>
<aside class="col-md-4">
  {% block toc %}
  {% endblock toc %}
  ...
</aside>.Copy the code

Here the block is also a template tag that serves as a placeholder. For example, {% block main %}{% endblock main %} is a placeholder, and main is the name we give the block. We’ll see the block tag in action next. We also added a placeholder {% block TOC %}{% endblock TOC %} under the aside tag, because in detail. HTML there would be a table of contents under the aside tag. When there is nothing in {% block toc %}{% endblock TOC %}, {% block TOC %}{% endblock TOC %} will not be displayed in the template. But when there is something in it, the template will display the contents of the block.

In index.html, we inherit base.html with {% extends ‘base.html’ %} at the top of the file. In addition to {% block main %}{% endblock main %} wrap with the index page should be displayed:

templates/blog/index.html

{% extends 'base.html' %}

{% block main %}
    {% for post in post_list %}
        <article class="post post-1">.</article>
    {% empty %}
        <div class="no-post">The article that has not been published for the time being!</div>
    {% endfor %}
    <! <div class="pagination-simple"> <a href="#">上 页</a> <span class="current">第 6 页 Href ="#"> Next </a> </div> -->
    <div class="pagination">.</div>
{% endblock main %}Copy the code

So the code in base.html plus {% block main %}{% endblock main %} is the same as the code in the original index.html. This is where template inheritance comes in. The common parts of the page are in base.html, and the rest of the page is different by replacing the {% block main %}{% endBlock main %} placeholder tags.

If you’re still a little confused about template inheritance, think of it as a Python class inheritance analogy. Base. HTML is the parent class and index.html is the subclass. Index.html inherits all of base.html, but it also has some content of its own, which is added by overwriting {% block main %}{% endBlock main %} (think of blocks as attributes of the parent class).

{% block main %}{% endBlock main %} fills the contents of the detail.html page with the same base. And fill in {% block toc %}{% endblock TOC %} with the contents of the directory section that is not in base.html. However, the current table of contents is just placeholder data, we will implement how to automatically extract the table of contents from the article in the future.

templates/blog/detail.html

{% extends 'base.html' %}

{% block main %}
    <article class="post post-1">.</article>
    <section class="comment-area">.</section>
{% endblock main %}
{% block toc %}
    <div class="widget widget-content">
        <h3 class="widget-title">The article directories</h3>
        <ul>
            <li>
                <a href="#">The tutorial features</a>
            </li>
            <li>
                <a href="#">Who is suitable for this tutorial</a>
            </li>
            <li>
                <a href="#">The online preview</a>
            </li>
            <li>
                <a href="#">The resource list</a>
            </li>
            <li>
                <a href="#">Get help</a>
            </li>
        </ul>
    </div>
{% endblock toc %}Copy the code

Modify something under the article TAB to show the actual data for the article:

<article class="post post-{{ post.pk }}">
  <header class="entry-header">
    <h1 class="entry-title">{{ post.title }}</h1>
    <div class="entry-meta">
      <span class="post-category"><a href="#">{{ post.category.name }}</a></span>
      <span class="post-date"><a href="#"><time class="entry-date"
                                                datetime="{{ post.created_time }}">{{ post.created_time }}</time></a></span>
      <span class="post-author"><a href="#">{{ post.author }}</a></span>
      <span class="comments-link"><a href="#">4 comments</a></span>
      <span class="views-count"><a href="#">588 to read</a></span>
    </div>
  </header>
  <div class="entry-content clearfix">
    {{ post.body }}
  </div>
</article>Copy the code

Click on the title of an article again from the home page or the Continue to read button to go to the details page, you can see the desired effect!

Blog post details page

conclusion

The code for this section is in Step8: blog detail view.

If you have problems, please ask for help in the following ways.

  • Leave a comment in the comments section of the blog Post details page – Dreamers’ blog.
  • Email a detailed description of the problem to [email protected] and a response will generally be received within 24 hours.