我们从创建分享文章的表单开始。Django 内置的 form 框架帮助我们非常方便的创建表单。form 框架允许我们定义 form 的字段、指定字段展示方式、指定数据验证方法。Django form 框架还提供渲染表单和处理数据的方法。
Django提供两个类来创建表单:
Form:帮助我们创建标准表单;
ModelForm:帮助我们创建增加或者修改模型实例的表单。
首先,在 blog 应用的根目录新建一个名为 forms.py 的文件,并添加以下代码:
from django import forms
class EmailPostForm(forms.Form):
name = forms.CharField(max_length=25)
email = forms.EmailField()
to = forms.EmailField()
comments = forms.CharField(required=False, widget=forms.Textarea)
这是你的第一个 Django 表单。我们来看一下代码:创建继承 Form 的表单类,使用不同的字段对输入进行验证。
注意:
Forms 可以放在 Django 项目的任何位置,为了方便起见,我们将其放在每个应用的 forms.py 文件中。
name 字段是一个 CharField 。这种类型的字段渲染一个的 HTML元素。每一个字段都有默认的组件,这个组件决定 HTML 如何展示该字段。可以设置字段的 widget 属性覆盖默认的组件。在 comments 字段中,我们使用 Textarea 组件表示使用 HTML元素代替默认的 元素。
字段验证也依赖字段类型。例如,email 和 to 字段为 EmailField ,两个字段都需要有效地 e-mail 地址,否则字段验证将引发 forms.ValidationError 异常并且表单无法通过验证。表单验证还会考虑其他参数:我们定义了一个最大长度为 25 的 name 字段并将 comment 字段设置为 required=False 。表单验证时考虑这些参数。这个表单使用的字段类型只是 Django 表单字段的一小部分,所有的表单字段可以参考:https://docs.djangoproject.com/en/1.11/ref/forms/fields/。
在视图中处理表单
我们需要创建了一个新的视图来处理表单,并在表单成功提交时发送 e-mail 。编辑 blog 应用的 views.py 添加以下代码:
from .forms import EmailPostForm
def post_share(request, post_id):
# Retrieve post by id
post = get_object_or_404(Post, id=post_id, status='published')
if request.method == 'POST':
# Form was submitted
form = EmailPostForm(request.POST)
if form.is_valid():
# Form fields passed validation
cd = form.cleaned_data
# ... send email
else:
form = EmailPostForm()
return render(request, 'blog/post/share.html', {'post': post, 'form': form})
这个表单将实现以下功能:
定义 post_share 视图,该视图输入参数为:request 对象和 post_id 。
使用 get_object_or_404() 通过 id 获取文章并要求文章状态为 published 。
使用相同的视图展示初始表单和处理提交数据。使用 request 方法区分表单是否提交,如果 request 方法为 GET ,我们将展示一个空表单;如果 request 方法为 POST ,表单将被提交并且需要处理。因此我们使用request.method="POST" 来区分这两种情况。
下面是展示和处理表单的过程:
当使用 GET 方法请求视图时,我们创建一个新的表单实例(在模板中展示空的表单):
form=EmailPostForm()
用户填写表单并通过 POST 提交,我们在 POST 部分使用提交的数据创建了一个表单实例:
if request.method == 'POST':
# Form was submitted
form = EmailPostForm(request.POST)
from django.core.mail import send_mail
>>> send_mail('Django mail', 'This e-mail was sent with Django.', 'your_account@gmail.com', ['your_account@gmail.com'], fail_silently=False)
from django.core.mail import send_mail
>>> send_mail('Django mail', 'This e-mail was sent with Django.', 'your_account@hotmail.com', ['your_account@163.com'], fail_silently=False)
from django.core.mail import send_mail
>>> send_mail('Django mail', 'This e-mail was sent with Django.', 'your_account@hotmail.com', ['your_account@163.com'], fail_silently=False)
如果 send_mail 第三个参数为 hotmail 邮箱,邮件无法发送,引 发SMTPSenderRefused: (553, 'Mail from must equal authorized user') 异常。即发送信息邮箱必须与授权邮箱一致。
发送邮件测试总结
测试环境:python2.7+Django1.11。
settings.py 尽量配置 hotmail 邮箱;
send_mail 中的发送邮箱最好与settings.py 中的授权邮箱一致。
现在,在视图中将添加发送邮件功能,将 blog 应用 views.py 中的 post_share 视图更改为:
from .forms import EmailPostForm
from django.core.mail import send_mail
def post_share(request, post_id):
# Retrieve post by id
post = get_object_or_404(Post, id=post_id, status='published')
sent = False
if request.method == 'POST':
# Form was submitted
form = EmailPostForm(request.POST)
if form.is_valid():
# Form fields passed validation
cd = form.cleaned_data
# send email
post_url = request.build_absolute_uri(post.get_absolute_url())
subject = '{}({})recommends you read "{}"'.format(cd['name'],
cd['email'],
post.title)
message = 'read"{}" at {} \n\n\'s comments:{}'.format(post.title,
post_url,
cd['name'],
cd[
'comments'])
send_mail(subject, message, cd['email'], [cd['to']])
sent = True
else:
form = EmailPostForm()
return render(request, 'blog/post/share.html',
{'post': post, 'form': form, 'sent': sent})
这里定义了 sent 变量,邮件发送成功时 sent 设为 True 。我们将在模板中使用这个变量,当表单正确提交且邮件发送成功时显示成功信息。这里使用 get_absolute_url() 方法获取在文章的链接并使用 request.build_absolute_uri() 来封装这个函数从而构建包含 http 的完整 URL 。使用验证成功的表单的cleaned_data 作为邮件的主题和内容,然后将邮件发送到表单中 to 一栏中输入的地址。
现在视图完成了,我们为其添加 URL 模式,打开 blog 应用下的 urls.py 文件,添加以下内容:
class Comment(models.Model):
post = models.ForeignKey(Post, related_name='comments')
name = models.CharField(max_length=80)
email = models.EmailField()
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
, class Meta:
ordering = ('created',)
def __str__(self):
return 'Comment by {} on {}'.format(self.name, self.post)
我们需要创建一个表单来帮助用户对文章进行评论。Django 提供两个创建表单的类:Form 和 ModelForm 。上一节我们使用 Form 实现通过邮件分享文章。由于需要创建的表单与 Comment 模型息息相关,因此本节需要使用 ModelForm 。编辑 blog 应用的 forms.py 文件并添加以下内容:
from .models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('name', 'email', 'body')
创建模型表单只需在表单的 Meta 类中申明使用哪个模型即可。每个模型字段类型都有对应的表单字段类型,表单验证使用我们定义的模型字段类型。默认情况下,Django 为模型中的所有字段创建一个表单字段。然而,也可以在表单的 Meta 类中使用 fields 设置希望包含的字段。CommentForm 只使用 name、email 和 body 字段。
在视图中处理ModelForms
我们使用文章详细视图对表单进行实例化并进行处理。编辑 blog 应用的 views.py 文件并将 post_detail 修改为:
from .forms import EmailPostForm, CommentForm
from .models import Post, Comment
def post_detail(request, year, month, day, slug):
post = get_object_or_404(Post, slug=slug, status='published',
publish__year=year, publish__month=month,
publish__day=day)
# list of active comments for this post
comments = post.comments.filter(active=True)
if request.method == "POST":
# a comment was posted
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
# create comment object but don't save to database yet
new_comment = comment_form.save(commit=False)
# assign the current post to the comment
new_comment.post = post
# save the comment to the database
new_comment.save()
else:
comment_form = CommentForm()
return render(request, 'blog/post/detail.html',
{'post': post, 'comments': comments,
'comment_form': comment_form})
打开 blog 应用的 models.py 文件,在 Post 模型中添加 django-taggit 提供的 TaggableManager 管理器:
from taggit.managers import TaggableManager
class Post(models.Model):
# ...
tags = TaggableManager()
tags 管理器帮助我们为文章添加、获取、删除标签。
打开 Terminal ,在项目的根目录下执行以下命令将模型变更同步到数据库:
python manage.py makemigrations
我们会看到以下输出:
Migrations for 'blog':
blog/migrations/0003_post_tags.py
- Add field tags to post
然后执行以下命令:
python manage.py migrate
我们会看到以下输出:
Operations to perform:
Apply all migrations: admin, auth, blog, contenttypes, sessions, taggit
Running migrations:
Applying taggit.0001_initial... OK
Applying taggit.0002_auto_20150616_2121... OK
Applying blog.0003_post_tags... OK
现在我们将编辑 post_list 视图实现查找特定标签的所有文章的功能。打开 blog 应用的 views.py 文件,将post_list 函数更改为
from taggit.models import Tag
def post_list(request, tag_slug=None):
object_list = Post.objects.all()
tag = None
if tag_slug:
tag = get_object_or_404(Tag, slug=tag_slug)
object_list = object_list.filter(tags__in=[tag])
在视图中,创建初始 queryset 获得所有发布的文章,如果 URL 传入了有效的 tag_slug ,则使用get_object_or_404 获得满足 slug 要求的 Tag 对象( Tag 模型中的 slug 应该是唯一的);
根据步骤 2 得到的 tag 对文章列表进行过滤。由于这是一个多对多关系,我们需要使用 tags 进行过滤得到给定列表,本项目暂时只有一篇文章满足标签要求。
Queryset 是惰性的。只有循环时 Queryset 才会执行文章查询。
最后更改 post_list 视图底部的 render() ,将 tag 传到模板中。
from taggit.models import Tag
def post_list(request, tag_slug=None):
object_list = Post.objects.all()
tag = None
if tag_slug:
tag = get_object_or_404(Tag, slug=tag_slug)
object_list = object_list.filter(tags__in=[tag])
paginator = Paginator(object_list, 1) # 1 posts in each page
page = request.GET.get('page')
try:
posts = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer deliver the first page
posts = paginator.page(1)
except EmptyPage:
# If page is out of range deliver last page of results
posts = paginator.page(paginator.num_pages)
return render(request, 'blog/post/list.html',
{'page': page, 'posts': posts, 'tag': tag})
SELECT "blog_post"."id", "blog_post"."title", "blog_post"."slug", "blog_post"."author_id", "blog_post"."body", "blog_post"."publish", "blog_post"."created", "blog_post"."updated", "blog_post"."status", COUNT("taggit_taggeditem"."tag_id") AS "same_tags"
FROM "blog_post" INNER JOIN "taggit_taggeditem" ON ("blog_post"."id" = "taggit_taggeditem"."object_id" AND ("taggit_taggeditem"."content_type_id" = 7))
WHERE ("taggit_taggeditem"."tag_id" IN (SELECT DISTINCT U0."id" AS Col1 FROM "taggit_tag" U0 INNER JOIN "taggit_taggeditem" U1 ON (U0."id" = U1."tag_id") WHERE (U1."object_id" = 1 AND U1."content_type_id" = 7)) AND NOT ("blog_post"."id" = 1)) GROUP BY "blog_post"."id", "blog_post"."title", "blog_post"."slug", "blog_post"."author_id", "blog_post"."body", "blog_post"."publish", "blog_post"."created", "blog_post"."updated", "blog_post"."status"
ORDER BY "same_tags" DESC, "blog_post"."publish" DESC LIMIT 4
这些代码的功能为:
获取当前文章所有 tag 的id ,queryset 的 values_list() 返回指定字段的元组列表,我们为其传入 flat=True 来直接获得字段值列表(而不是元组列表)[1,2,3…];
获得除当前文章以外的所有具备这些标签的文章;
通过聚合函数 Count 生成一个计算字段 same_tags ,该字段为包含相同标签的数量;
按照共有标签数量降序及相同标签时发布时间降序对文章进行排序,然后截取前 4 篇文章。
在 render() 函数中添加 similar_posts 对象:
render(request, 'blog/post/detail.html',
{'post': post, 'comments': comments,
'comment_form': comment_form,
'similar_posts':similar_posts})
一、Vue的两种使用方式扩展核心包开发直接通过引入Vue.js,适用于简单页面或局部功能增强。优点:轻量,无需构建工具。缺点:难以管理复杂项目,缺少工程化支持。工程化开发使用VueCLI、Vite等工具创建项目,结合Webpack/Vite构建。支持单文件组件(.vue文件),结构清晰(`,,)。插件生态丰富(如VueRouter、Vuex、Pinia)。二、Vue实例的深入理解核心配置项 new
原文地址:http://www.open-open.com/lib/view/open1346857871615.html
使用Java Mail API来发送邮件也很容易实现,但是最近公司一个同事封装的邮件API实在让我无法接受,于是便打算改用Spring Mail API来发送邮件,顺便记录下这篇文章。 【Spring Mail API】
Spring Mail API都在org.spri
Comment {{ forloop.counter }} by {{ comment.name }}{{ comment.created }}
{{ comment.body|linebreaks }}