您好,登录后才能下订单哦!
本篇文章为大家展示了表单Form对象的使用及如何构建复杂的QuerySet,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。
Django包含两个基类可构建表单,如下所示:
Form 可构建标准的表单
ModelForm 可构建与模型实例相关联的表单
首先 在应用程序目录中创建 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)
CharField
该字段类型显示为 <input type="text">
widget
为字段所使用的插件,在 comments
字段中使用了 Textarea
插件
EmailField
需要使用有效的电子邮件地址;否则,字段验证将抛出 forms.ValidationError
异常
required
表示该字段是否必填项
有效的表单字段列表请参照:点击此处
接下来 在 views.py
中使用 Form 表单对象
from .forms import EmailPostForm def post_share(request, post_id): post = get_object_or_404(Post, id=post_id, status='published') if request.method == 'POST': form = EmailPostForm(request.POST) if form.is_valid(): cd = form.cleaned_data # ... 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.method == 'POST'
方法区分 POST
请求还是 GET
请求
表单处理流程如下:
当视图为 GET 请求时,创建一个新的 form
实例,并用于显示模板中的空表单: form = EmailPostForm()
当为 POST 请求时,通过包含于 request.POST
中的提交数据生成一个表单实例:form = EmailPostForm(request.POST)
利用表单 is_valid()
方法验证所提交的数据。如果作一字段包含了无效数据,将返回 False
。通过访问 form.errors
可查看验证错误列表
若表单正确,通过访问 form.cleaned_data
将对验证后的数据进行检索。该属性表示为表单字段及其对应值的字典。
最后 在HTML模板中使用 Form
对象:
{% extends "blog/base.html" %} {% block title %}Share a post{% endblock %} {% block content %} {% if sent %} <h2>E-mail successfully sent</h2> <p> "{{ post.title }}" was successfully sent to {{ form.cleaned_data.to }}. </p> {% else %} <h2>Share "{{ post.title }}" by e-mail</h2> <form action="." method="post"> {{ form.as_p }} {% csrf_token %} <input type="submit" value="Send e-mail"> </form> {% endif %} {% endblock %}
此处通知Django利用 as_p
方法将字段显示为 <p>
中的字段。除此之外,还可利用 as_ul
作为无序列表显示表单;或者利用 as_table
作为表予以显示。如果需要显示每个字段,可遍历相关字段,如下所示:
{% for field in form%} <div> {{ field.errors }} {{ field.label_tag }} {{ field }} </div> {% endfor %}
首先 创建用于评论帖子的模型:
class Comment(models.Model): post = models.ForeignKey(Post, on_delete=models.CASCADE, 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)
ForeignKey
以外键关联帖子
related_name
可对对应关系表进行跨表查询。定义完毕后,可通过 comment.post
检索评论对象的帖子,也可采用 post.comments.all()
检索某个帖子的全部评论;如果没有定义该值,Django将使用 模型名称_set
(如 comment_set )这一形式命名相关的对象管理器
关于多对一关系,可点击此处了解更多内容
接着 创建模型中的表单,编辑 应用程序 目录下的 forms.py
文件,添加如下代码:
from .models import Comment class CommentForm(forms.ModelForm): class Meta: model = Comment fields = ('name', 'email', 'body')
model
指定使用哪一个模型创建表单
fields
显式地通知当前框架希望在表单中包含哪些字段;或者采用 exclude
定义希望排除的字段。
然后 在视图View中使用ModelForms,修改 views.py
文件的 post_detail()
方法如下:
from .models import Post, Comment from .forms import EmailPostForm, CommentForm def post_detail(request, year, month, day, post): post = get_object_or_404(Post, slug=post, status='published', publish__year=year, publish__month=month, publish__day=day) # 通过post对象查找关联的comment对象 comments = post.comments.filter(active=True) new_comment = None if request.method == 'POST': comment_form = CommentForm(data=request.POST) if comment_form.is_valid(): new_comment = comment_form.save(commit=False) new_comment.post = post new_comment.save() else: comment_form = CommentForm() return render(request, 'blog/post/detail.html', {'post': post, 'comments': comments, 'new_comment': new_comment, 'comment_form': comment_form})
comments = post.comments.filter(active=True)
使用了 post_detail 视图显示帖子及其评论内容,并针对该帖子加入了QuerySet以过滤检索评论
new_comment = comment_form.save(commit=False)
创建表单所链接的模型实例。如果采用 commit=False
则会创建模型实例,但不会将其保存至数据库中
new_comment.post = post
修改ModelForms的属性值
new_comment.save()
保存至数据库中
最后 在HTML模板中显示:
<!-- 显示评论数量 --> {% with comments.count as total_comments %} <h3> {{ total_comments }} comment{{ total_comments|pluralize }} </h3> {% endwith %} <!-- 显示所有评论内容 --> {% for comment in comments %} <div class="comment"> <p class="info"> Comment {{ forloop.counter }} by {{ comment.name }} {{ comment.created }} </p> {{ comment.body|linebreaks }} </div> {% empty %} <p>There are no comments yet.</p> {% endfor %} <!-- 显示添加评论的表单 --> {% if new_comment %} <h3>Your comment has been added.</h3> {% else %} <h3>Add a new comment</h3> <form action="." method="post"> {{ comment_form.as_p }} {% csrf_token %} <p><input type="submit" value="Add comment"></p> </form> {% endif %}
{% with %}
标签可将某个值赋予可用的新变量中,直到遇到 {% endwith %}
标签。
pluralize
过滤器将返回包含字母 s 的复数字符串形式。
Django-taggit 源码地址
pip/pipenv install django_taggit
INSTALLED_APPS = [ ... 'taggit', ]
from taggit.managers import TaggableManager class Post(models.Model): ... tags = TaggableManager()
python manage.py makemigrations blog(应用名称) python manage.py migrate
迁移完成后,运行服务器,可以 Django管理 页面中管理所有 标签(tags)
>>> from blog.models import Post >>> post = Post.objects.get(id=1) >>> post.tags.add('music', 'jazz', 'django') >>> post.tags.all() <QuerySet [<Tag: music>, <Tag: django>, <Tag: jazz>]> >>> post.tags.remove('django') >>> post.tags.all() <QuerySet [<Tag: music>, <Tag: jazz>]>
<p class="tags">Tags: {{ post.tags.all|join:", " }}</p>
join
模板过滤器类似于字符串的 join()
方法,并添加包含指定字符串到相关元素中。
修改 应用程序 目录的 views.py
文件,代码如下:
from taggit.models import Tag def post_list(request, tag_slug=None): object_list = Post.published.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, 3) # 每页3条记录 page = request.GET.get('page') try: posts = paginator.page(page) except PageNotAnInteger: # 如为无效页码则跳转到第1页 posts = paginator.page(1) except EmptyPage: # 如果页数超出范围则跳转到最后一页 posts = paginator.page(paginator.num_pages) return render(request, 'blog/post/list.html', {'posts': posts, 'page': page, 'tag': tag})
post_list
接收可选的 tag_slug
参数,默认值为 None
并包含于URL中
在构建初始的QuerySet、检索发布的全部帖子,如果存在标签slug,将通过 get_object_or_404()
获得 Tag 对象
随后,通过slug过滤帖子列表。
最后调整视图下方的 render()
函数,将 tag
变量会至HTML模板中。
添加额外的URL路径,通过标签列出帖子:
path('tag/<slug:tag_slug>/', views.post_list, name='post_list_by_tag'),
前端面HTML模板代码修改如下:
{% extends "blog/base.html" %} {% block title %}My Blog{% endblock %} {% block content %} <h2>My Blog</h2> {% if tag %} <h3>Posts tagged with "{{ tag.name }}"</h3> {% endif %} {% for post in posts %} <h3> <a href="{{ post.get_absolute_url }}"> {{ post.title }} </a> </h3> <p class="tags"> Tags: {% for tag in post.tags.all %} <a href="{% url "blog:post_list_by_tag" tag.slug %}"> {{ tag.name }} </a> {% if not forloop.last %}, {% endif %} {% endfor %} </p> <p class="date"> Published {{ post.publish }} by {{ post.author }} </p> {{ post.body|truncatewords:30|linebreaks }} {% include 'pagination.html' with page=posts %} {% endfor %} {% endblock %}
{% url "blog:post_list_by_tag" tag.slug %}
使URL名称以及slug标签作为其参数
我们将在 post_detail
视图中构建复杂的QuerySet,修改 应用程序 目录下的 views.py
文件,添加如下代码:
from django.db.models import Count
Count
为Django ORM中的Count聚合函数,其它聚合函数可访问如下地址
在 render()
之前,在 post_detail
视图中添加下列代码:
post_tag_ids = post.tags.values_list('id', flat=True) similar_posts = Post.published.filter(tags__in=post_tag_ids).exclude(id=post.id) similar_posts = similar_posts.annotate(same_tags=Count('tags')).order_by('-same_tags', '-publish')[:4] return render(request, 'blog/post/detail.html', {'post': post, 'comments': comments, 'new_comment': new_comment, 'comment_form': comment_form, 'similar_posts': similar_posts})
上述代码执行下列操作:
针对当前帖子的标签,标签Python ID列表。values_list()
返回包含指定字段的元组。将 flat=True
传入,可获得形如 [1,2,3,...]
的列表。
获取包含此类标签的全部帖子,并排除当前帖子本身。
使用 Count
函数生成一个计算后的字段,即 same_tags
。该字段包含了与所有查询标签所共有的标签号
通过标签号和发布日期对结果进行排序。此处仅检索前4个帖子。
将 similar_posts
放入上下文字典中。
最后,在HTML模板中显示:
<h3>Similar posts</h3> {% for post in similar_posts %} <p> <a href="{{ post.get_absolute_url }}">{{ post.title }}</a> </p> {% empty %} There are no similar posts yet. {% endfor %}
至此,可向用户推荐标签相似的帖子的功能。
上述内容就是表单Form对象的使用及如何构建复杂的QuerySet,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。