html - 在 Django/Wagtail 中创建博客文章的喜欢/不喜欢按钮
问题描述
我是 Django 和 Wagtail 的新手,并且一直在寻找一种方法来使用 Wagtail 在博客条目页面上实现“简单”的喜欢/不喜欢按钮。
我在我的页面模型中包含了一个 total_likes IntegerField,当用户单击 html 模板上的按钮时,我希望在数据库中增加或减少这个 int。
用户不应该登录。我发现的大多数教程只针对注册用户处理这个问题,这不是我想要的。
如果有人能指出我正确的方向,我会很高兴。models.py 代码如下。
我不明白如何从模板中调用函数。
class BlogEntry(Page):
date = models.DateField("Post date")
intro = models.CharField(max_length=250, blank=False)
body = RichTextField(blank=True)
tags = ClusterTaggableManager(through=BlogEntryTag, blank=True)
categories = ParentalManyToManyField('blog.BlogCategory', blank=False)
total_likes = models.IntegerField(blank=False, default=0)
image = models.ForeignKey(
"wagtailimages.Image",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="+"
)
streamBody = StreamField([
("text", blocks.StaticContentBlock()),
("quotes", blocks.QuoteBlock()),
("image_and_text", blocks.ImageTextBlock()),
("related_articles", blocks.RelatedArticlesBlock()),
], null=True, blank=True)
sidebarBody = StreamField([
("text", blocks.StaticContentBlock()),
("quotes", blocks.QuoteBlock()),
("image_and_text", blocks.ImageTextBlock()),
("related_articles", blocks.RelatedArticlesBlock()),
], null=True, blank=True)
search_fields = Page.search_fields + [
index.SearchField('intro'),
index.SearchField('body'),
]
content_panels = Page.content_panels + [
MultiFieldPanel([
ImageChooserPanel("image"),
FieldPanel('date'),
FieldPanel('tags'),
FieldPanel('categories', widget=forms.CheckboxSelectMultiple),
], heading="Blog information"),
FieldPanel('intro'),
StreamFieldPanel("streamBody"),
]
sidebar_panels = [
MultiFieldPanel([
FieldPanel("sidebarBody"),
], heading="Sidebar Content")
]
edit_handler = TabbedInterface(
[
ObjectList(content_panels, heading="Custom"),
ObjectList(Page.promote_panels, heading="Promote"),
ObjectList(Page.settings_panels, heading="Settings"),
ObjectList(sidebar_panels, heading="Sidebar"),
]
)
def __str__(self):
return self.total_likes
def likePost(self):
self.total_likes += 1
def dislikePost(self):
self.total_likes -= 1
解决方案
Overview
Welcome to Django and Wagtail, there is a lot to learn but hopefully you are finding it fun. The first thing to wrap your head around is how a website (browser / client) can talk to the server (Python code running Django/Wagtail). Even though your Page
model has a likePost
method, you will need to provide a way for your Page
to handle this kind of request.
The web uses a system of HTTP requests, the most common being GET
and POST
, where GET
is used to pull down data to show to the user, POST
is used for when the website wants to send something back to the server.
Django's docs on working with forms may be a good place to start to understand this process a bit more. Once your website has a form
(in the HTML template), you can provide a way to 'listen' to this form when submitted. Wagtail has a method that exists on all Page
models that is called serve
and it allows you to override the normal behaviour.
Solution
In the solution below you will need to do the following:
1. Add two form
s to your template (e.g. blog_page.html
)
- Remember to load the tags
wagtailcore_tags
so that you can access the page's URL in the form. - For simplicity, two forms have been created, one with a button for like and another with a button for dislike.
- Both forms will use the
method="POST"
and the action (this is the URL to POST to) being the current URL. - Each form contains a
csrf_token
, you can read more about this in the Django docs but this helps avoid some security issues. - Each form contains a html
input
that ishidden
with a name and value, we will use thename
only in the server code to determine what button has been clicked.
{% extends "base.html" %}
{% load wagtailcore_tags %}
... BLOG CONTENT
<form action="{% pageurl page %}" method="POST" role="form">
{% csrf_token %}
<input type="hidden" name="like" value="true">
<input type="submit" value="LIKE">
</form>
<form action="{% pageurl page %}" method="POST" role="form">
{% csrf_token %}
<input type="hidden" name="dislike" value="true">
<input type="submit" value="DISLIKE">
</form>
2. Override the serve
method in your Page
model
The serve
method on the Page
model takes the argument request
, which is the Django request object and should return a response. Thankfully we do not have to think about how this response is built, just know that we must call the super
(original) version of this after any logic.
- Check if the current request is a POST request and if so, then check what kind of value has been submitted, depending on the value call the
Page
method that matches - Update the
likePost
anddislikePost
methods to ensure that the model data gets Saved viaself.save()
class BlogEntry(Page):
# ...
def likePost(self):
self.total_likes += 1
self.save() ## note: you may need to ensure you call save to update data
def dislikePost(self):
self.total_likes -= 1
self.save() ## note: you may need to ensure you call save to update data
def serve(self, request):
if request.method == 'POST':
if request.POST.get('like'):
self.likePost()
# a form POST has been submitted with a value for 'like'
if request.POST.get('dislike'):
# a form POST has been submitted with a value for 'dislike'
self.dislikePost()
# ensure we call the super's serve method so that the page loads correctly
return super(BlogPage, self).serve(request)
Good to know
- As you have noted, this is a basic request that does not require authentication (sign in) to submit a like, however it is very possible you will get a lot of spam this way and you may want to consider other approaches (even third party systems) to work around this.
- This way of storing likes (as an integer) will also not give you much data, just a number at the current point in time, it might be worth tracking individual submissions and then providing a tally to the UI.
- Here is a great overview of HTTP from MDN that I have found to be a good reference for understanding how a server handles requests & responses.
推荐阅读
- javascript - 为什么引导模式不显示图像?
- javascript - 如何在 InputText 中更改特定文本范围内的颜色
- python - 将数百个 csv 文件转换为 hdf5 文件
- c++ - 如何修复由我尝试调用的求和函数引起的分段错误?C++
- javascript - JS:函数与类的区别
- python - imageio 单个帧速率
- html - div的边框不可见
- javascript - 在 React --Edit 中创建“修改日期”值:更新为工作代码
- python - 有没有办法从用户使用while循环输入的数字中找到偶数和奇数?
- java - 未在 parallelStream 上填充 Spring PreAuthorize SecurityContext