一个完整的Django入门指南(三)

第五部分

 Introduction

Welcome to the 5th part of the tutorial series! In this tutorial, we are going to learn more about protecting views against unauthorized users and how to access the authenticated user in the views and forms. We are also going to implement the topic posts listing view and the reply view. Finally, we are going to explore some features of Django ORM and have a brief introduction to migrations.

5.1. 权限

我们必须开始保护我们对非授权用户的视图。到目前为止,用户没有登录,他们也可以看到页面和表单。

Django有一个内置的视图装饰器来避免这个问题:

from django.contrib.auth.decorators import login_required


@login_required
def new_topic(request,pk):
'
'
'
@login_required
def new_topic(request,pk):
    board = get_object_or_404(Board,pk=pk)
    #获取当前登录的用户
    user = User.objects.first()
    if request.method == 'POST':
        #实例一个表单实例
        form = NewTopicForm(request.POST)
        if form.is_valid():
            topic = form.save(commit=False)
            topic.board = board
            topic.starter = request.user
            topic.save()
            post = Post.objects.create(
                message = form.cleaned_data.get('message'),
                topic = topic,
                created_by = request.user
            )
            return redirect('board_topics',pk=board.pk)     #重定向到已创建的主题页

    else:
        form = NewTopicForm()
    return render(request,'new_topic.html',{'board':board,'form':form})

5.2.帖子视图

先在让我们花点时间来实现帖子列表页面,相应的线框如下:

(1) 首先创建一个url

url(r'^boards/(?P<pk>\d+)/topics/(?P<topic_pk>\d+)/$', views.topic_posts, name='topic_posts'),

 pk:用于标识boards

topic_pk:用来检测是哪个主题

(2)匹配视图如下:

def topic_posts(request, pk, topic_pk):
    topic = get_object_or_404(Topic, board__pk=pk, pk=topic_pk)
    return render(request, 'topic_posts.html', {'topic': topic})

(3)templates/topic_posts.html

{% extends 'base.html' %}

{% block title %}{{ topic.subject }}{% endblock %}

{% block breadcrumb %}
  <li class="breadcrumb-item"><a href="{% url 'home' %}">Boards</a></li>
  <li class="breadcrumb-item"><a href="{% url 'board_topics' topic.board.pk %}">{{ topic.board.name }}</a></li>
  <li class="breadcrumb-item active">{{ topic.subject }}</li>
{% endblock %}

{% block content %}

{% endblock %}

 (4)显示主题里面所有的帖子

topic_posts.html内部,我们可以创建一个迭代主题帖子的for循环:

模板/ topic_posts.html

{% extends 'base.html' %}

{% load static %}

{% block title %}{{ topic.subject }}{% endblock %}

{% block breadcrumb %}
  <li class="breadcrumb-item"><a href="{% url 'home' %}">Boards</a></li>
  <li class="breadcrumb-item"><a href="{% url 'board_topics' topic.board.pk %}">{{ topic.board.name }}</a></li>
  <li class="breadcrumb-item active">{{ topic.subject }}</li>
{% endblock %}

{% block content %}

  <div class="mb-4">
    <a href="#" class="btn btn-primary" role="button">Reply</a>
  </div>

  {% for post in topic.posts.all %}
    <div class="card mb-2">
      <div class="card-body p-3">
        <div class="row">
          <div class="col-2">
            <img src="{% static 'image/people.png' %}" alt="{{ post.created_by.username }}" class="w-100">
            <small>Posts: {{ post.created_by.posts.count }}</small>
          </div>
          <div class="col-10">
            <div class="row mb-3">
              <div class="col-6">
                <strong class="text-muted">{{ post.created_by.username }}</strong>
              </div>
              <div class="col-6 text-right">
                <small class="text-muted">{{ post.created_at }}</small>
              </div>
            </div>
            {{ post.message }}
            {% if post.created_by == user %}
              <div class="mt-3">
                <a href="#" class="btn btn-primary btn-sm" role="button">Edit</a>
              </div>
            {% endif %}
          </div>
        </div>
      </div>
    </div>
  {% endfor %}

{% endblock %}

 由于现在我们没有办法上传用户图片,因此我们只需要有一张空白图片图像下载地址

 把图像图片放到静态文件image下

(5)更新topics.html模板

 {% for topic in board.topics.all %}
            <tr>
                <td><a href="{% url 'topic_posts' board.pk topic.pk %}">{{ topic.subject }}</a></td>
                <td>{{ topic.starter.username }}</td>
                <td>0</td>
                <td>0</td>
                <td>{{ topic.last_updated }}</td>
            </tr>
        {% endfor %}

点主题,跳到对应的帖子

5.3.帖子回复

我们现在实现回复帖子视图

(1)添加url

url(r'^boards/(?P<pk>\d+)/topics/(?P<topic_pk>\d+)/reply/$', views.reply_topic, name='reply_topic'),

(2)为帖子回复添加一个新表单

boards/forms.py

from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['message', ]

(3)帖子回复的视图函数

boards/views.py

def reply_topic(request, pk, topic_pk):
    topic = get_object_or_404(Topic, board__pk=pk, pk=topic_pk)
    if request.method == 'POST':
        form = PostForm(request.POST)
        if form.is_valid():
            post = form.save(commit=False)
            post.topic = topic
            post.created_by = request.user
            post.save()
            return redirect('topic_posts', pk=pk, topic_pk=topic_pk)
    else:
        form = PostForm()
    return render(request, 'reply_topic.html', {'topic': topic, 'form': form})

更新new_topic的视图函数

return redirect('topic_posts', pk=pk, topic_pk=topic.pk)  
def new_topic(request,pk):
    board = get_object_or_404(Board,pk=pk)
    #获取当前登录的用户
    user = User.objects.first()
    if request.method == 'POST':
        #实例一个表单实例
        form = NewTopicForm(request.POST)
        if form.is_valid():
            topic = form.save(commit=False)
            topic.board = board
            topic.starter = request.user
            topic.save()
            post = Post.objects.create(
                message = form.cleaned_data.get('message'),
                topic = topic,
                created_by = request.user
            )
            return redirect('topic_posts', pk=pk, topic_pk=topic.pk)

    else:
        form = NewTopicForm()
    return render(request,'new_topic.html',{'board':board,'form':form})

(4)reply_topic.html

{% extends 'base.html' %}

{% load static %}

{% block title %}Post a reply{% endblock %}

{% block breadcrumb %}
  <li class="breadcrumb-item"><a href="{% url 'home' %}">Boards</a></li>
  <li class="breadcrumb-item"><a href="{% url 'board_topics' topic.board.pk %}">{{ topic.board.name }}</a></li>
  <li class="breadcrumb-item"><a href="{% url 'topic_posts' topic.board.pk topic.pk %}">{{ topic.subject }}</a></li>
  <li class="breadcrumb-item active">Post a reply</li>
{% endblock %}

{% block content %}

  <form method="post" class="mb-4">
    {% csrf_token %}
    {% include 'includes/form.html' %}
    <button type="submit" class="btn btn-success">Post a reply</button>
  </form>

  {% for post in topic.posts.all %}
    <div class="card mb-2">
      <div class="card-body p-3">
        <div class="row mb-3">
          <div class="col-6">
            <strong class="text-muted">{{ post.created_by.username }}</strong>
          </div>
          <div class="col-6 text-right">
            <small class="text-muted">{{ post.created_at }}</small>
          </div>
        </div>
        {{ post.message }}
      </div>
    </div>
  {% endfor %}

{% endblock %}

访问帖子回复的url

然后,在发布回复之后,用户将重定向回主题帖子

我们现在可以更改首发帖子,以便在页面中更加强调它:

 templates / topic_posts.html

{% for post in topic.posts.all %}
        <div class="card mb-2 {% if forloop.first %}border-dark{% endif %}">
            {% if forloop.first %}
                <div class="card-header text-white bg-dark py-2 px-3">{{ topic.subject }}</div>
            {% endif %}
    
            <div class="card-body p-3">
{% extends 'base.html' %}

{% load static %}

{% block title %}{{ topic.subject }}{% endblock %}

{% block breadcrumb %}
    <li class="breadcrumb-item"><a href="{% url 'home' %}">Boards</a></li>
    <li class="breadcrumb-item"><a href="{% url 'board_topics' topic.board.pk %}">{{ topic.board.name }}</a></li>
    <li class="breadcrumb-item active">{{ topic.subject }}</li>
{% endblock %}

{% block content %}

    <div class="mb-4">
        <a href="#" class="btn btn-primary" role="button">Reply</a>
    </div>

    {% for post in topic.posts.all %}
        <div class="card mb-2 {% if forloop.first %}border-dark{% endif %}">
            {% if forloop.first %}
                <div class="card-header text-white bg-dark py-2 px-3">{{ topic.subject }}</div>
            {% endif %}

            <div class="card-body p-3">
                <div class="row">
                    <div class="col-2">
                        <img src="{% static 'image/people.png' %}" alt="{{ post.created_by.username }}" class="w-100">
                        <small>Posts: {{ post.created_by.posts.count }}</small>
                    </div>
                    <div class="col-10">
                        <div class="row mb-3">
                            <div class="col-6">
                                <strong class="text-muted">{{ post.created_by.username }}</strong>
                            </div>
                            <div class="col-6 text-right">
                                <small class="text-muted">{{ post.created_at }}</small>
                            </div>
                        </div>
                        {{ post.message }}
                        {% if post.created_by == user %}
                            <div class="mt-3">
                                <a href="#" class="btn btn-primary btn-sm" role="button">Edit</a>
                            </div>
                        {% endif %}
                    </div>
                </div>
            </div>
        </div>
    {% endfor %}

{% endblock %}

 5.5.修改主页视图

 首先,让我们改进主视图:

这里有三项任务:

  • 显示board的帖子数;
  • 显示board的主题数量;
  • 显示发布内容的最后一位用户以及日期和时间。

 (1)更改models.py

添加__str__方法

# boards/models.py

from django.db import models
from django.contrib.auth.models import User
from django.utils.text import Truncator

class Board(models.Model):
    '''板块'''
    name = models.CharField(max_length=30,unique=True)
    # 用于说明这个板块做什么用的
    description = models.CharField(max_length=100)

    def __str__(self):
        return self.name

class Topic(models.Model):
    '''话题'''
    # 主题内容
    subject = models.CharField(max_length=255)
    # 定义排序
    last_updated = models.DateTimeField(auto_now_add=True)
    # 指定这个话题属于哪个板块
    board = models.ForeignKey(Board,related_name='topics',on_delete=models.CASCADE)
    # 用来识别谁发起的话题
    starter = models.ForeignKey(User,related_name='topics')

    def __str__(self):
        return self.subject


class Post(models.Model):
    '''帖子'''

    # 存储回复的内容
    message = models.TextField(max_length=4000)
    topic = models.ForeignKey(Topic,related_name='posts',on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(null=True)
    # 谁创建的
    created_by = models.ForeignKey(User,related_name='posts',on_delete=models.CASCADE)
    # 谁更新的
    updated_by = models.ForeignKey(User,null=True,related_name='+',on_delete=models.CASCADE)

    def __str__(self):
        truncated_message = Truncator(self.message)
        return truncated_message.chars(30)   

(2) 修改Board

添加两个方法

class Board(models.Model):
    '''板块'''
    name = models.CharField(max_length=30,unique=True)
    # 用于说明这个板块做什么用的
    description = models.CharField(max_length=100)

    def __str__(self):
        return self.name

    def get_posts_count(self):
        return Post.objects.filter(topic__board=self).count()

    def get_last_post(self):
        return Post.objects.filter(topic__board=self).order_by('-created_at').first()

(3)修改home.html

{#templates/home.html#}

{% extends 'base.html' %}

{% block breadcrumb %}
    <li class="breadcrumb-item active">Boards</li>
{% endblock %}

{% block content %}
    <table class="table">
        <thead class="thead-inverse">
        <tr>
            <th>Board</th>
            <th>Posts</th>
            <th>Topics</th>
            <th>Last Post</th>
        </tr>
        </thead>
        <tbody>
        {% for board in boards %}
            <tr>
                <td>
                    <a href="{% url 'board_topics' board.pk %}">{{ board.name }}</a>
                    <small class="text-muted d-block">{{ board.description }}</small>
                </td>
                <td class="align-middle">
                    {{ board.get_posts_count }}
                </td>
                <td class="align-middle">
                    {{ board.topics.count }}
                </td>
                <td class="align-middle">
                    {% with post=board.get_last_post %}
                        {% if post %}
                            <small>
                                <a href="{% url 'topic_posts' board.pk post.topic.pk %}">
                                    By {{ post.created_by.username }} at {{ post.created_at }}
                                </a>
                            </small>
                        {% else %}
                            <small class="text-muted">
                                <em>No posts yet.</em>
                            </small>
                        {% endif %}
                    {% endwith %}
                </td>
            </tr>
        {% endfor %}
        </tbody>
    </table>
{% endblock %}

现在可以显示了

 5.6.修改topic列表视图

(1)修改boards/views.py

from django.db.models import Count

def board_topics(request,pk):
    board = get_object_or_404(Board, pk=pk)
    topics = board.topics.order_by('-last_updated').annotate(replies=Count('posts') - 1)
    return render(request, 'topics.html', {'board': board, 'topics': topics})

(2)修改templates/topics.html

{% for topic in topics %}
  <tr>
    <td><a href="{% url 'topic_posts' board.pk topic.pk %}">{{ topic.subject }}</a></td>
    <td>{{ topic.starter.username }}</td>
    <td>{{ topic.replies }}</td>
    <td>0</td>
    <td>{{ topic.last_updated }}</td>
  </tr>
{% endfor %}
{#templates/topics.html#}

{% extends 'base.html' %}

{% block title %}
    {{ board.name }} - {{ block.super }}
{% endblock %}

{% block breadcrumb %}
    <li class="breadcrumb-item"><a href="{% url 'home' %}">Boards</a></li>
    <li class="breadcrumb-item active">{{ board.name }}</li>
{% endblock %}

{% block content %}
    <div class="mb-4">
        <a href="{% url 'new_topic' board.pk %}" class="btn btn-primary">New topic</a>
    </div>
    <table class="table">
        <thead class="thead-inverse">
        <tr>
            <th>Topic</th>
            <th>Starter</th>
            <th>Replies</th>
            <th>Views</th>
            <th>Last Update</th>
        </tr>
        </thead>
        <tbody>
        {% for topic in topics %}
            <tr>
                <td><a href="{% url 'topic_posts' board.pk topic.pk %}">{{ topic.subject }}</a></td>
                <td>{{ topic.starter.username }}</td>
                <td>{{ topic.replies }}</td>
                <td>0</td>
                <td>{{ topic.last_updated }}</td>
            </tr>
        {% endfor %}
        </tbody>
    </table>
{% endblock %}

 (3)给Topic添加一个views字段

class Topic(models.Model):
    '''话题'''
    # 主题内容
    subject = models.CharField(max_length=255)
    # 定义排序
    last_updated = models.DateTimeField(auto_now_add=True)
    # 指定这个话题属于哪个板块
    board = models.ForeignKey(Board,related_name='topics',on_delete=models.CASCADE)
    # 用来识别谁发起的话题
    starter = models.ForeignKey(User,related_name='topics')
    views = models.PositiveIntegerField(default=0)

    def __str__(self):
        return self.subject

(4)修改topics_posts函数

def topic_posts(request, pk, topic_pk):
    topic = get_object_or_404(Topic, board__pk=pk, pk=topic_pk)
    topic.views += 1
    topic.save()
    return render(request, 'topic_posts.html', {'topic': topic})

(5)修改templates / topics.html

{% for topic in topics %}
            <tr>
                <td><a href="{% url 'topic_posts' board.pk topic.pk %}">{{ topic.subject }}</a></td>
                <td>{{ topic.starter.username }}</td>
                <td>{{ topic.replies }}</td>
                <td>{{ topic.views }}</td>
                <td>{{ topic.last_updated }}</td>
            </tr>
        {% endfor %}

打开一个主题并刷新页面几次,看看它浏览量是不是增加

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏美团技术团队

Android Hook技术防范漫谈

背景 当下,数据就像水、电、空气一样无处不在,说它是“21世纪的生产资料”一点都不夸张,由此带来的是,各行业对于数据的争夺热火朝天。随着互联网和数据的思维深入人...

62470
来自专栏算法+

谷歌开源项目Google Preview Image Extractor(PIEX) (附上完整demo代码)

前天偶然看到谷歌开源项目中有一个近乎无人问津的项目Google Preview Image Extractor(PIEX) 。 项目地址: https://gi...

1.4K60
来自专栏老码农专栏

原 荐 一场版本升级引发的性能血案 - 之数

23930
来自专栏张戈的专栏

移动搜索SEO:网站移动适配之Meta标注、移动跳转终结篇

这些天,在给博客的标签页(tag)添加跳转和 META 动态申明时,居然让我醍醐灌顶,发现之前的动态适配的做法是多么的苦逼和小白! 总结前,先来回顾下小白张戈在...

52060
来自专栏IMWeb前端团队

React函数式进阶

React让很多人让追捧的一个特性是它的所有的组件都是完全由JavaScript组成的。组件的定义是JavaScript,组件的模板也可以是JavaScript...

24360
来自专栏GIS讲堂

web中的树形结构【小结】

最近在做一个项目,是一个b/s架构的,在项目中,用到了树形结构,即如图1所示的结构。

32920
来自专栏以南小隐-数通那些事儿

锐捷RSOS_10.4之后版本Ctrl层升级系统版本

20240
来自专栏老司机的简书

老司机带你走进Core Animation 之CADisplayLink

今天说点啥呢?上次老司机说过,带你走进CoreAnimation,那今天就趁热打铁,继续讲讲核心动画相关的东西吧。那今天要讲的就是CADisplayLink。

13420
来自专栏小樱的经验随笔

自己手动复现一个熊猫烧香病毒

最近逛了一下 bilibili ,偶然的一次机会,我在 bilibili 上看到了某个 up 主分享了一个他自己仿照熊猫病毒的原型制作的一个病毒的演示视频,虽然...

80420
来自专栏GopherCoder

『No19: Gorm 上手指南』

如果你是做后端开发的,日常工作中,除了熟悉编程语言之外,数据库怕是最常用的技术了吧。

76010

扫码关注云+社区

领取腾讯云代金券