溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

表單Form對(duì)象的使用及如何構(gòu)建復(fù)雜的QuerySet

發(fā)布時(shí)間:2021-10-29 09:12:32 來(lái)源:億速云 閱讀:154 作者:柒染 欄目:大數(shù)據(jù)

本篇文章為大家展示了表單Form對(duì)象的使用及如何構(gòu)建復(fù)雜的QuerySet,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過(guò)這篇文章的詳細(xì)介紹希望你能有所收獲。

一、利用Django創(chuàng)建表單

Django包含兩個(gè)基類(lèi)可構(gòu)建表單,如下所示:

  1. Form 可構(gòu)建標(biāo)準(zhǔn)的表單

  2. ModelForm 可構(gòu)建與模型實(shí)例相關(guān)聯(lián)的表單

1. 標(biāo)準(zhǔn)表單Form

首先 在應(yīng)用程序目錄中創(chuàng)建 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 該字段類(lèi)型顯示為 <input type="text">

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

  • EmailField 需要使用有效的電子郵件地址;否則,字段驗(yàn)證將拋出 forms.ValidationError 異常

  • required 表示該字段是否必填項(xiàng)

有效的表單字段列表請(qǐng)參照:點(diǎn)擊此處

接下來(lái)views.py 中使用 Form 表單對(duì)象

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 對(duì)象和 post_id 變量作為參數(shù)

  • 采用 get_object_or_404 快捷方式,并通過(guò) ID 檢索帖子,以確保檢索的狀態(tài)為 published

  • 根據(jù) request.method == 'POST' 方法區(qū)分 POST 請(qǐng)求還是 GET 請(qǐng)求

表單處理流程如下:

  1. 當(dāng)視圖為 GET 請(qǐng)求時(shí),創(chuàng)建一個(gè)新的 form 實(shí)例,并用于顯示模板中的空表單: form = EmailPostForm()

  2. 當(dāng)為 POST 請(qǐng)求時(shí),通過(guò)包含于 request.POST 中的提交數(shù)據(jù)生成一個(gè)表單實(shí)例:form = EmailPostForm(request.POST)

  3. 利用表單 is_valid() 方法驗(yàn)證所提交的數(shù)據(jù)。如果作一字段包含了無(wú)效數(shù)據(jù),將返回 False。通過(guò)訪(fǎng)問(wèn) form.errors 可查看驗(yàn)證錯(cuò)誤列表

  4. 若表單正確,通過(guò)訪(fǎng)問(wèn) form.cleaned_data 將對(duì)驗(yàn)證后的數(shù)據(jù)進(jìn)行檢索。該屬性表示為表單字段及其對(duì)應(yīng)值的字典。

最后 在HTML模板中使用 Form 對(duì)象:

{% 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 作為無(wú)序列表顯示表單;或者利用 as_table 作為表予以顯示。如果需要顯示每個(gè)字段,可遍歷相關(guān)字段,如下所示:

{% for field in form%}
<div>
 {{ field.errors }}
 {{ field.label_tag }} {{ field }}
</div>
{% endfor %}

2. 模型表單ModelForm

首先 創(chuàng)建用于評(píng)論帖子的模型:

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 以外鍵關(guān)聯(lián)帖子

  • related_name 可對(duì)對(duì)應(yīng)關(guān)系表進(jìn)行跨表查詢(xún)。定義完畢后,可通過(guò) comment.post 檢索評(píng)論對(duì)象的帖子,也可采用 post.comments.all() 檢索某個(gè)帖子的全部評(píng)論;如果沒(méi)有定義該值,Django將使用 模型名稱(chēng)_set (如 comment_set )這一形式命名相關(guān)的對(duì)象管理器

關(guān)于多對(duì)一關(guān)系,可點(diǎn)擊此處了解更多內(nèi)容

接著 創(chuàng)建模型中的表單,編輯 應(yīng)用程序 目錄下的 forms.py 文件,添加如下代碼:

from .models import Comment

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ('name', 'email', 'body')
  • model 指定使用哪一個(gè)模型創(chuàng)建表單

  • fields 顯式地通知當(dāng)前框架希望在表單中包含哪些字段;或者采用 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)

    # 通過(guò)post對(duì)象查找關(guān)聯(lián)的comment對(duì)象
    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 視圖顯示帖子及其評(píng)論內(nèi)容,并針對(duì)該帖子加入了QuerySet以過(guò)濾檢索評(píng)論

  • new_comment = comment_form.save(commit=False) 創(chuàng)建表單所鏈接的模型實(shí)例。如果采用 commit=False 則會(huì)創(chuàng)建模型實(shí)例,但不會(huì)將其保存至數(shù)據(jù)庫(kù)中

  • new_comment.post = post 修改ModelForms的屬性值

  • new_comment.save() 保存至數(shù)據(jù)庫(kù)中

最后 在HTML模板中顯示:

    <!-- 顯示評(píng)論數(shù)量 -->
    {% with comments.count as total_comments %}
        <h3>
            {{ total_comments }} comment{{ total_comments|pluralize }}
        </h3>
    {% endwith %}

    <!-- 顯示所有評(píng)論內(nèi)容 -->
    {% 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 %}

    <!-- 顯示添加評(píng)論的表單 -->
    {% 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 %} 標(biāo)簽可將某個(gè)值賦予可用的新變量中,直到遇到 {% endwith %} 標(biāo)簽。

  • pluralize 過(guò)濾器將返回包含字母 s 的復(fù)數(shù)字符串形式。

二、使用 django-taggit 添加標(biāo)簽功能

Django-taggit 源碼地址

1. 安裝django-taggit

pip/pipenv install django_taggit

2. 在 settings.py 文件中注冊(cè) taggia

INSTALLED_APPS = [
    ...
    'taggit',
]

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

from taggit.managers import TaggableManager

class Post(models.Model):
    ...
    tags = TaggableManager()

4. 生成數(shù)據(jù)庫(kù)遷移

python manage.py makemigrations blog(應(yīng)用名稱(chēng))
python manage.py migrate

遷移完成后,運(yùn)行服務(wù)器,可以 Django管理 頁(yè)面中管理所有 標(biāo)簽(tags)

5. 在shell中考察如何使用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>]>

6. 在HTML模板中顯示 Tags

<p class="tags">Tags: {{ post.tags.all|join:", " }}</p>
  • join 模板過(guò)濾器類(lèi)似于字符串的 join() 方法,并添加包含指定字符串到相關(guān)元素中。

7. 在視圖Views中使用 Tags

修改 應(yīng)用程序 目錄的 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)  # 每頁(yè)3條記錄
    page = request.GET.get('page')
    try:
        posts = paginator.page(page)
    except PageNotAnInteger:
        # 如為無(wú)效頁(yè)碼則跳轉(zhuǎn)到第1頁(yè)
        posts = paginator.page(1)
    except EmptyPage:
        # 如果頁(yè)數(shù)超出范圍則跳轉(zhuǎn)到最后一頁(yè)
        posts = paginator.page(paginator.num_pages)
    return render(request, 'blog/post/list.html', {'posts': posts, 'page': page, 'tag': tag})
  • post_list 接收可選的 tag_slug 參數(shù),默認(rèn)值為 None 并包含于URL中

  • 在構(gòu)建初始的QuerySet、檢索發(fā)布的全部帖子,如果存在標(biāo)簽slug,將通過(guò) get_object_or_404() 獲得 Tag 對(duì)象

  • 隨后,通過(guò)slug過(guò)濾帖子列表。

  • 最后調(diào)整視圖下方的 render() 函數(shù),將 tag 變量會(huì)至HTML模板中。

添加額外的URL路徑,通過(guò)標(biāo)簽列出帖子:

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名稱(chēng)以及slug標(biāo)簽作為其參數(shù)

三、構(gòu)建復(fù)雜的QuerySet

我們將在 post_detail 視圖中構(gòu)建復(fù)雜的QuerySet,修改 應(yīng)用程序 目錄下的 views.py 文件,添加如下代碼:

from django.db.models import Count
  • Count 為Django ORM中的Count聚合函數(shù),其它聚合函數(shù)可訪(fǎng)問(wèn)如下地址

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})

上述代碼執(zhí)行下列操作:

  1. 針對(duì)當(dāng)前帖子的標(biāo)簽,標(biāo)簽Python ID列表。values_list() 返回包含指定字段的元組。將 flat=True 傳入,可獲得形如 [1,2,3,...] 的列表。

  2. 獲取包含此類(lèi)標(biāo)簽的全部帖子,并排除當(dāng)前帖子本身。

  3. 使用 Count 函數(shù)生成一個(gè)計(jì)算后的字段,即 same_tags。該字段包含了與所有查詢(xún)標(biāo)簽所共有的標(biāo)簽號(hào)

  4. 通過(guò)標(biāo)簽號(hào)和發(fā)布日期對(duì)結(jié)果進(jìn)行排序。此處僅檢索前4個(gè)帖子。

  5. 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 %}

至此,可向用戶(hù)推薦標(biāo)簽相似的帖子的功能。

上述內(nèi)容就是表單Form對(duì)象的使用及如何構(gòu)建復(fù)雜的QuerySet,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI