Django表单结合后台数据及渲染
初学Django,对框架还有很多不熟悉的地方,所以决定记录今后学习过程中遇到的问题。
在做个人Blog系统的时候,需要实现文章修改页面。如何把数据库中Article的数据与表单类结合,并使用Django的表单类直接在前端模板中渲染的问题困扰了我好多天,浏览了官方文档并尝试了网上一些解决方案之后终于成功了,来总结下整个过程。
首先,文章类Article模型设计如下:
models.py
#文章
class Article(models.Model):
title = models.CharField('标题',max_length=70)
#OneToOneRelationships
category = models.ForeignKey(Category,on_delete=models.DO_NOTHING,verbose_name='分类',blank=True,null=True)
#ManyToManyRelationships
tags = models.ManyToManyField(Tag,verbose_name='标签',blank=True)
body = UEditorField('内容',width=800,height=500,
toolbars="full",imagePath="upimg/",filePath="upfile/",
settings={},command=None,blank=True)
user = models.ForeignKey(User,on_delete=models.CASCADE,verbose_name='作者')
#文章标签
class Tag(models.Model):
name = models.CharField('标签',max_length=100)
class Meta:
verbose_name = '文章标签'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
#文章分类
class Category(models.Model):
name = models.CharField('博客分类',max_length=100)
index = models.IntegerField(default=999,verbose_name='分类排序')
class Meta:
verbose_name = '博客分类'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
Article有title、category、body、tags、user等属性,每一个属性都是字段类型(field),其中,Article与Category是多对一的关联关系(Many-to-one relationships),category作为ForeignKey类型的字段,Tags与Article是多对多的关联关系(Many-to-many relationships),tags作为ManyToManyField字段。
有了模型之后,就可以创建表单了。Django为我们提供了表单类,可以避免在前端写繁琐的代码。如果操作表单的同时也涉及到数据库的改动,请继承forms.ModelForm
;如果表单只是进行某些验证工作,不会修改数据库,继承forms.Form
即可。
forms.py
from django import forms
from .models import Article
class ArticlePostForm(forms.ModelForm):
class Meta:
model=Article
#选取我们希望在表单中显示的字段即可
fields=('title','category','body','tags')
Model中的字段与Form中的字段存在着对应的关系:例如在模型中,category作为Article的ForeignKey字段。对应到表单中,category就成为了ModelChoiceField字段。
Model field | Form field |
---|---|
ForeignKey | ModelChoiceField |
ManyToManyField | ManyToManyField |
更多对应关系可参考Django文档:Creating forms from models
ModelChoiceField
- default widget(默认组件):Select
- Empty value:None
- queryset:可以为该字段指定一个包含模型对象的QuerySet,从中可以派生字段的选项。
- empty_label:空标签的显示(默认为"---------")。
queryset与empty_label都可以在初始化时,自己指定
# A custom empty label
field1 = forms.ModelChoiceField(queryset=..., empty_label="(Nothing)")
# No empty label
field2 = forms.ModelChoiceField(queryset=..., empty_label=None)
views.py
@login_required
def article_update(request,id):
article = Article.objects.get(id = id)
if request.method == 'POST':
#使用POST提交表单,并把表单数据写入数据库
article_post_form = ArticlePostForm(data = request.POST)
if article_post_form.is_valid():
article.title = request.POST['title']
article.category = Category.objects.get(id=request.POST['category'])
article.body = request.POST['body']
#获取提交的tags
mytags =article_post_form.cleaned_data.get('tags')
for t in mytags:
#使用add()方法将QuerySet中标签依次添加入文章
article.tags.add(t)
article.save()
#提交成功后,跳转到想要的页面即可
return redirect(reverse('success',args=[User.objects.get(username=request.user).id]))
else:
article_post_form = ArticlePostForm(initial={
'title':article.title,
'category':article.category,
'body':article.body,
'tags':Tag.objects.filter(article=article.id),
})
return render(request,'update.html',locals())
文章的标签不能直接通过request.POST['tags']
来获取,这里使用 mytags =article_post_form.cleaned_data.get('tags')
,mytags是一个QuerySet对象。
Tips:当使用request.POST['name']
来获取表单的相应数据时,如果数据为空值,Django会报错;而使用request.POST.get('name')
时会返回None空值,所以,对于不是必填字段的选项,建议使用后者。
update.html
<form method="post">
{% csrf_token %}
{{ article_post_form.media }}
{{ article_post_form.as_p }}
<p><button class="btn btn-outline-dark" type="submit">Post</button></p>
</form>
前端页面的部分代码如上,我的文章内容字段使用了UEditor插件,所以需要{{ article_post_form.media }}
来渲染UEditor的样式。
Django 表单的渲染
article_post_form.as_p
自动把表单渲染为带有 < p >标签的组件
<p>
<label for="id_title">标题:</label>
<input type="text" name="title" maxlength="70" required id="id_title">
</p>
article_post_form.as_ul
将表单渲染为一系列带有< li >标签的组件
<li>
<label for="id_title">标题:</label>
<input type="text" name="title" maxlength="70" required id="id_title">
</li>
article_post_form.as_table
将表单渲染为table
Ps:搜索解决方案的时候听说Django提供了View视图类UpdateView可以实现文章修改页面的设计,但笔者还没有来得及亲身实践,有兴趣的朋友可以自己尝试一下呀。