您好,登錄后才能下訂單哦!
本篇文章為大家展示了表單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進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。