一个完整的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 %}
打开一个主题并刷新页面几次,看看它浏览量是不是增加
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【实战】提权某找小姐网站服务器
- 激发态计算中的溶剂效应
- 不会吧,有人用了两年Spring, 居然不知道包扫描是怎么实现的
- 不会吧!都 0202 年了,不会还有不知道 axios 的吧
- 全栈的自我修养: 002使用@vue/cli进行vue环境搭建 (使用Vue,SpringBoot,Flask完成前后端分离)
- JAVA | Java 解决跨域问题 花式解决跨域问题
- 深入理解Java泛型(二)通配符与嵌套
- 用高斯计算磷光发射能
- Android So动态加载 优雅实现与原理分析
- 堆与栈区别
- 在Gaussian16中同时扫描两个反应坐标
- python调试神器traceback
- Centos安装高版本GCC
- Django+Vue开发生鲜电商平台之5.使用DRF实现商品列表页和过滤
- Spring 注入集合的成员变量属性