千家信息网

表单Form对象的使用及如何构建复杂的QuerySet

发表于:2024-11-22 作者:千家信息网编辑
千家信息网最后更新 2024年11月22日,本篇文章为大家展示了表单Form对象的使用及如何构建复杂的QuerySet,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。一、利用Django创建表单Djang
千家信息网最后更新 2024年11月22日表单Form对象的使用及如何构建复杂的QuerySet

本篇文章为大家展示了表单Form对象的使用及如何构建复杂的QuerySet,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

一、利用Django创建表单

Django包含两个基类可构建表单,如下所示:

  1. Form 可构建标准的表单

  2. ModelForm 可构建与模型实例相关联的表单

1. 标准表单Form

首先 在应用程序目录中创建 forms.py 文件,代码如下:

from django import formsclass EmailPostForm(forms.Form):    name = forms.CharField(max_length=25)    email = forms.EmailField()    to = forms.EmailField()    comments = forms.CharField(required=False, widget=forms.Textarea)
  • CharField 该字段类型显示为

  • widget 为字段所使用的插件,在 comments 字段中使用了 Textarea 插件

  • EmailField 需要使用有效的电子邮件地址;否则,字段验证将抛出 forms.ValidationError 异常

  • required 表示该字段是否必填项

有效的表单字段列表请参照:点击此处

接下来views.py 中使用 Form 表单对象

from .forms import EmailPostFormdef 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 请求

表单处理流程如下:

  1. 当视图为 GET 请求时,创建一个新的 form 实例,并用于显示模板中的空表单: form = EmailPostForm()

  2. 当为 POST 请求时,通过包含于 request.POST 中的提交数据生成一个表单实例:form = EmailPostForm(request.POST)

  3. 利用表单 is_valid() 方法验证所提交的数据。如果作一字段包含了无效数据,将返回 False。通过访问 form.errors 可查看验证错误列表

  4. 若表单正确,通过访问 form.cleaned_data 将对验证后的数据进行检索。该属性表示为表单字段及其对应值的字典。

最后 在HTML模板中使用 Form 对象:

{% extends "blog/base.html" %}{% block title %}Share a post{% endblock %}{% block content %}    {% if sent %}        

E-mail successfully sent

"{{ post.title }}" was successfully sent to {{ form.cleaned_data.to }}.

{% else %}

Share "{{ post.title }}" by e-mail

{{ form.as_p }} {% csrf_token %}
{% endif %}{% endblock %}

此处通知Django利用 as_p 方法将字段显示为

中的字段。除此之外,还可利用 as_ul 作为无序列表显示表单;或者利用 as_table 作为表予以显示。如果需要显示每个字段,可遍历相关字段,如下所示:

{% for field in form%}
{{ field.errors }} {{ field.label_tag }} {{ field }}
{% endfor %}

2. 模型表单ModelForm

首先 创建用于评论帖子的模型:

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 Commentclass CommentForm(forms.ModelForm):    class Meta:        model = Comment        fields = ('name', 'email', 'body')
  • model 指定使用哪一个模型创建表单

  • fields 显式地通知当前框架希望在表单中包含哪些字段;或者采用 exclude 定义希望排除的字段。

然后 在视图View中使用ModelForms,修改 views.py 文件的 post_detail() 方法如下:

from .models import Post, Commentfrom .forms import EmailPostForm, CommentFormdef 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 %}        

{{ total_comments }} comment{{ total_comments|pluralize }}

{% endwith %} {% for comment in comments %}

Comment {{ forloop.counter }} by {{ comment.name }} {{ comment.created }}

{{ comment.body|linebreaks }}
{% empty %}

There are no comments yet.

{% endfor %} {% if new_comment %}

Your comment has been added.

{% else %}

Add a new comment

{{ comment_form.as_p }} {% csrf_token %}

{% endif %}
  • {% with %} 标签可将某个值赋予可用的新变量中,直到遇到 {% endwith %} 标签。

  • pluralize 过滤器将返回包含字母 s 的复数字符串形式。

二、使用 django-taggit 添加标签功能

Django-taggit 源码地址

1. 安装django-taggit

pip/pipenv install django_taggit

2. 在 settings.py 文件中注册 taggia

INSTALLED_APPS = [    ...    'taggit',]

3. 将 djnago-taggit 提供的 TaggableManager 管理器添加到需要使用 tag 的模型中

from taggit.managers import TaggableManagerclass Post(models.Model):    ...    tags = TaggableManager()

4. 生成数据库迁移

python manage.py makemigrations blog(应用名称)python manage.py migrate

迁移完成后,运行服务器,可以 Django管理 页面中管理所有 标签(tags)

5. 在shell中考察如何使用tags管理器

>>> from blog.models import Post>>> post = Post.objects.get(id=1)>>> post.tags.add('music', 'jazz', 'django')>>> post.tags.all(), , ]>>>> post.tags.remove('django')>>> post.tags.all(), ]>

6. 在HTML模板中显示 Tags

Tags: {{ post.tags.all|join:", " }}

  • join 模板过滤器类似于字符串的 join() 方法,并添加包含指定字符串到相关元素中。

7. 在视图Views中使用 Tags

修改 应用程序 目录的 views.py 文件,代码如下:

from taggit.models import Tagdef 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//', views.post_list, name='post_list_by_tag'),

前端面HTML模板代码修改如下:

{% extends "blog/base.html" %}{% block title %}My Blog{% endblock %}{% block content %}    

My Blog

{% if tag %}

Posts tagged with "{{ tag.name }}"

{% endif %} {% for post in posts %}

{{ post.title }}

Tags: {% for tag in post.tags.all %} {{ tag.name }} {% if not forloop.last %}, {% endif %} {% endfor %}

Published {{ post.publish }} by {{ post.author }}

{{ post.body|truncatewords:30|linebreaks }} {% include 'pagination.html' with page=posts %} {% endfor %}{% endblock %}
  • {% url "blog:post_list_by_tag" tag.slug %} 使URL名称以及slug标签作为其参数

三、构建复杂的QuerySet

我们将在 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})

上述代码执行下列操作:

  1. 针对当前帖子的标签,标签Python ID列表。values_list() 返回包含指定字段的元组。将 flat=True 传入,可获得形如 [1,2,3,...] 的列表。

  2. 获取包含此类标签的全部帖子,并排除当前帖子本身。

  3. 使用 Count 函数生成一个计算后的字段,即 same_tags。该字段包含了与所有查询标签所共有的标签号

  4. 通过标签号和发布日期对结果进行排序。此处仅检索前4个帖子。

  5. similar_posts 放入上下文字典中。

最后,在HTML模板中显示:

    

Similar posts

{% for post in similar_posts %}

{{ post.title }}

{% empty %} There are no similar posts yet. {% endfor %}

至此,可向用户推荐标签相似的帖子的功能。

上述内容就是表单Form对象的使用及如何构建复杂的QuerySet,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注行业资讯频道。

0