首先需要定义一个使用场景:
音乐网站的前端部分可以添加上传歌手的单曲,
这个添加页面就使用django form表单来实现。
目录
数据表内容
歌手表及表模型
单曲表及表模型
演示表单使用
设置路由
创建form.py
视图实例化表单类
模板使用表单对象
表单使用进阶
优化表单类
视图表单使用
总结
下方实现功能时,涉及到一些表的数据操作,所以我把表模型内容粘贴出来。
这样有助于整体理解form使用。
在子应用的models.py中的设定歌手表结构,这里由于在后台有一些功能实现,所以内容较多。
不必过多关注歌手表,因为是单曲添加只是涉及一部分歌手表内容,主要集中在使用单曲表使用。
内容如下:
class BaseModel(models.Model):
""" 设置基础模型类 """
addtime = models.DateTimeField(auto_now_add=True)
updatetime = models.DateTimeField(auto_now=True)
class Singler(BaseModel):
""" 歌手表模型 """
class Meta:
verbose_name = '歌手'
verbose_name_plural = '歌手'
# 正序
ordering = ['first_letter']
name = models.CharField(
'姓名',
max_length=50,
help_text='请输入歌手名称'
)
first_letter = models.CharField(max_length=15, editable=False)
# 设置上传位置
portrait = models.ImageField(
'照片',
upload_to=upload_save_path,
help_text='请上传歌手照片'
)
birthday = models.DateField(
'生日',
default=date.today,
help_text='请选择歌手生日',
blank=True
)
height = models.IntegerField(
'身高(cm)',
help_text='请输入歌手身高(cm)',
default=0,
blank=True
)
weight = models.IntegerField(
'体重(kg)',
help_text='请输入歌手体重(kg)',
default=0,
blank=True
)
constellation = models.CharField(
'星座',
max_length=50,
help_text='请输入歌手星座'
)
english_name = models.CharField(
'英文名',
max_length=50,
help_text='请输入歌手英文名',
default='-'
)
gender = models.IntegerField(
'性别',
help_text='请选择歌手性别',
choices=((0, '女'), (1, '男')),
default=1
)
country_name = models.CharField(
'国籍',
max_length=50,
help_text='请输入歌手国籍',
default='-'
)
# editable=False 后台将不再显示
singe_num = models.IntegerField(
default=0,
editable=False
)
album_num = models.IntegerField(
default=0,
editable=False
)
desc = models.TextField(
'简介',
help_text='请输入歌手简介'
)
def __str__(self):
""" 修改返回格式 """
return self.name
执行过表迁移后,添加部分数据。
表字段和记录内容如下:
在子应用的models.py中的设定单曲表结构,在这里定义了单曲表所有字段,
一会在添加功能时候,需要实现单曲中这些字段资料的添加。
class Singe(BaseModel):
""" 单曲表 """
class Meta:
verbose_name = '单曲'
verbose_name_plural = '单曲'
name = models.CharField(
'单曲名称',
max_length=50,
help_text='请输入单曲名称',
)
duration = models.IntegerField(editable=False, default=0)
playnum = models.IntegerField(default=0, editable=False)
path = models.FileField(
'歌曲文件',
upload_to=upload_save_path,
help_text='请上传歌曲',
)
lyric = models.FileField(
'歌曲歌词',
upload_to=upload_save_path,
help_text='请上传歌曲歌词',
)
# 设置与歌手表关联外键 一对多外键设置在多的模型中
singler = models.ForeignKey(
"Singler",
on_delete=models.CASCADE,
verbose_name='歌手',
help_text='请选择歌手'
)
执行过表迁移后,添加部分数据。
表字段和记录内容如下:
传统的表单生成方式是在模板文件中编写HTML代码实现。一个完整的表单主要由4部分组成:提交地址、请求方式、元素控件和提交按钮。提交地址和请求方式由form标签设置,提交按钮具有一定特殊性,不通过django form表单来实现。其他文本框、下拉框、复选框等可由django form表单的元素控件来实现。可以简化表单的实现过程和提高表单的灵活性。
在urls.py中设置路由地址。
path('test', views.test, name='test'),
在子项目目录下创建form.py,在其中定义表单类testForm。
名称可以使用charField类型来实现,文件用fileField类型实现。
但是下拉菜单必须使用choiceField类型实现;与表模型类设置有所不同。
内容如下:
from django import forms
from .models import *
class SingeForm(forms.Form):
name = forms.CharField(max_length=20, label='单曲名称')
path = forms.FileField(label='单曲文件')
lyric = forms.FileField(label='歌词文件')
# 设置下拉菜单
names = Singler.objects.all()
# 将数据以列表的形式标识,列表元素为元组的格式
singers = [(v.id, v.name) for v in names]
singer = forms.ChoiceField(label='所属歌手', choices=singers)
视图调用表单并传参给模板。
内容如下:
from .form import *
def test(request):
""" 演示form使用 """
v = SingeForm()
return render(request, 'test/index.html', locals())
在这里需要判断是否有错误返回,没有错误正常显示添加表单。
添加歌手单曲
{% if v.errors %}
操作失败,问题是:{{ v.errors }}
{% else %}
{# 表单 #}
{# 表单 #}
{% endif %}
注意:没有填写提交地址,默认提交本路由。
效果:
浏览器访问,展示表单。
在表单演示使用基础上,增加对表单参数的验证,数据处理并添加入库。
在原来基础上,增加单曲名称、单曲文件格式、歌词文件格式自定义验证。
自定义必填提示语内容。
内容如下:
from django import forms
from .models import *
from django.core.exceptions import ValidationError
def check_name(value):
if len(value.encode('utf-8')) > 60:
raise ValidationError('单曲名称不可超过20个字!')
def check_singe(value):
path_name = str(value)
if not path_name.endswith(".mp3"):
raise ValidationError('单曲文件格式为mp3文件!')
def check_lyric(value):
path_name = str(value)
if not path_name.endswith(".lrc"):
raise ValidationError('歌词文件格式为lrc文件!')
class SingeForm(forms.Form):
name = forms.CharField(
max_length=20,
label='单曲名称',
error_messages={'required': '单曲名称不可为空'},
validators=[check_name])
path = forms.FileField(
label='单曲文件',
error_messages={'required': '请上传单曲文件'},
validators=[check_singe]
)
lyric = forms.FileField(
label='歌词文件',
error_messages={'required': '请上传歌词文件'},
validators=[check_lyric]
)
# 设置下拉菜单
names = Singler.objects.all()
# 将数据以列表的形式标识,列表元素为元组的格式
singers = [(v.id, v.name) for v in names]
singer = forms.ChoiceField(label='所属歌手', choices=singers)
增加文件上传、歌曲时长计算、单曲关联歌手并增加相应歌手单曲数;
如果验证不通过,返回错误信息。
这里通过请求的方法进行判断,如果是GET就把表单显示到模板中。
如果是POST请求,就需要把请求获得的参数传递到form表单中,来实现表单验证数据。
这里需要注意的是:如果有文件上传需要设置模板中表单的enctype="multipart/form-data";
因为请求的文件需要单独的获取,故还要再把文件参数单独传给表单。否则就会提示没有上传文件。
下面就是文件上传,保存到服务器端,获取上传文件路径,拿歌曲路径获取歌曲时长。
然后模型操作:单曲的新增,关联歌手相应记录;最后更新相应歌手单曲数。
内容如下:
from .form import *
from django.http import HttpResponse
import eyed3
def test(request):
""" 演示form使用 """
if request.method == 'GET':
v = SingeForm()
return render(request, 'test/index.html', locals())
else:
v = SingeForm(data=request.POST, files=request.FILES)
if v.is_valid():
singe_path = save_media(request.FILES['path'])
# 上传单曲文件
singe_path = save_media(request.FILES['path'])
# 上传歌词文件
lyric_path = save_media(request.FILES['lyric'])
# 计算单曲时长
duration = get_duration_mp3(singe_path)
# 查询歌手
singer_id = v.cleaned_data['singer']
singerDb = Singler.objects.filter(id=singer_id).first()
# 新增单曲
singeDb = Singe()
singeDb.name = v.cleaned_data['name']
singeDb.duration = duration
singeDb.path = singe_path
singeDb.lyric = lyric_path
# 建立关联
singeDb.singler = singerDb
singeDb.save()
# 歌手增加单曲数
singerDb.singe_num = singerDb.singe_num + 1
singerDb.save()
return HttpResponse('操作成功!')
else:
# 获取错误信息,并以json格式返回
error_msg = v.errors.as_json()
return render(request, 'test/index.html', locals())
def save_media(file):
""" 保存文件到服务器 """
path = os.path.join(settings.MEDIA_ROOT, file.name)
with open(path, 'wb+') as fp:
for info in file.chunks():
fp.write(info)
return path
def get_duration_mp3(file_path):
""" 获取mp3音频文件时长 """
info = eyed3.load(file_path)
return int(info.info.time_secs)
效果:
验证不通过:
验证通过:
Django的表单功能是通过定义表单类,再由类的实例化生成HTML的表单元素控件,这样可以在模板文件中减少HTML的硬编码。每个HTML的表单元素控件由表单字段来决定。并且可以使用表单来进行服务端验证,把验证内容跟逻辑处理进行分离。