文章目录
ModelForm
Model写法以及自定义错误信息
大家在写表单的时候,会发现表单中的Field和模型中的Field基本上是一模一样的,而且表单中需要验证的时候,也就是我们模型中需要保存的数据,我们可以在表单中可以从from django import froms
下的froms.ModelForm
来进行绑定字段
- 导入:
from django import froms
- 定义的类要继承于
forms.ModelFrom
- 在类中定义meta对应的类,该类的写法是固定的
- model : 对应的模型
- fields : 验证的字段 。当需要验证全部字段时:
__all__
: 代表着为验证全部字段 ,[]
:代表着只需要验证的字段 - exclude : 不验证的字段。表示不验证该字段
- error_messages : 自定义错误信息
forms.py
# @Time : 2020/7/21 0:18
# @Author : Small-J
from django import forms
from .models import Article
# forms.Form:代表着为导入表单
# forms.ModelForm:代表着导入模型的表单
class AddForms(forms.ModelForm):
"""
Meta : 该类是必须继承的,但是该字段是
model :对应的模型类
fields : 当为‘__all__就是验证全部字段’,当只想验证其中部分的字段的时候,需要使用[]包裹起来
"""
class Meta:
model = Article
# fields = '__all__'
# 当只想验证某几个字段的情况下可以使用[]的形式
# fields = ['title'] # 表示只验证title这个字段
exclude = ['title'] # exclude->排除的意思 表示不验证title这个字段
error_messages = {
'title': {
'required': '该字段是必须要填的',
'min_length': '最小长度为3',
'max_length': '最大长度为20'
},
'content': {
'required': '该字段是必须要填的',
'max_length': '最大长度为100'
},
'author': {
'required': '该字段是必须要填的',
'max_length': '最大长度为15'
}
}
views.py
from django.shortcuts import render
from django.views import View
# 导入表单验证
from .forms import AddForms
from django.http import HttpResponse
class AddArticle(View):
def get(self, request):
return render(request, 'add.html')
def post(self, request):
form = AddForms(request.POST)
# is_valid:代表验证通过的情况下
if form.is_valid():
return HttpResponse('success')
else:
print(form.errors.get_json_data())
return HttpResponse('fail')
add.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
{# csrf_token:代表着解除csrf的一个保护 #}
{% csrf_token %}
<table>
<tr>
<td>标题:</td>
<td><input type="text" name="title"></td>
</tr>
<tr>
<td>内容:</td>
<td><input type="textarea" name="content"></td>
</tr>
<tr>
<td>作者:</td>
<td><input type="text" name="author"></td>
</tr>
<td><input type="submit" value="提交"></td>
</table>
</form>
</body>
</html>
models.py
from django.db import models
# 自定义验证器
from django.core import validators
class Article(models.Model):
# 当我们想设置最小长度的时候,但是在字段中没有的话,可以借助自定义验证器
# MinLengthValidator
title = models.CharField(max_length=20, validators=[validators.MinLengthValidator(limit_value=3)])
content = models.TextField(max_length=100, validators=[validators.MinLengthValidator(limit_value=3)])
author = models.CharField(max_length=15)
create_time = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'article'
app/urls.py
# @Time : 2020/7/21 0:11
# @Author : Small-J
from . import views
from django.urls import path
urlpatterns = [
path('', views.AddArticle.as_view())
]
save方法
ModelForm还有save方法,可以验证完成后直接调用save方法,就可以将这个数据库保存到数据库中
from django.shortcuts import render
from django.views import View
# 导入表单验证
from .forms import AddForms
from django.http import HttpResponse
class AddArticle(View):
def get(self, request):
return render(request, 'add.html')
def post(self, request):
form = AddForms(request.POST)
# is_valid:代表验证通过的情况下
if form.is_valid():
# 接收请求并保存到数据库
form.save() # 保存完之后在数据库中显示正常
return HttpResponse('success')
else:
print(form.errors.get_json_data())
return HttpResponse('fail')
这个方法必须要在clean没有问题后才能使用,如果在clean之前使用,会抛出异常。另外,我们在调用save方法的时候,如果传入一个commit=False
,那么只会生成这个模型的对象,而不会把这个对象真正的插入到数据库中。比如表单上验证的字段没有包含模型中所有的字段,这时候就可以先创建对象,再根据填充其他字段,把所有字段的只都补充完成后,再保存到数据库中。
views.py
class RegisterArticle(View):
def get(self, request):
return render(request, 'register.html')
def post(self, request):
form = RegisterForm(request.POST)
if form.is_valid():
user = form.save()
user.password = form.cleaned_data.get('pwd1')
user.save()
return HttpResponse('注册成功')
else:
print(form.errors.get_json_data())
return HttpResponse('fail')
forms.py
class RegisterForm(forms.ModelForm):
pwd1 = forms.CharField(min_length=3, max_length=10)
pwd2 = forms.CharField(min_length=3, max_length=10)
email = forms.EmailField()
# clean映射多字段
def clean(self):
changed_data = super().clean()
# print(changed_data) # 该返回的是一个字典形式
# changed_data:该返回是有变化的列表
pwd1 = changed_data.get('pwd1')
pwd2 = changed_data.get('pwd2')
if pwd1 != pwd2:
raise forms.ValidationError('请输入两次相同的密码')
class Meta:
# 使用的模型
model = Register
# 不验证密码
exclude = ['password']
error_messages = {
'telephone': {
'required': '请填写该字段',
},
'email': {
'required': '请填写该字段',
'invalid': '请输入正确的邮箱地址'
}
}
models.py
class Register(models.Model):
# 当不能设置最小长度的时候,可以使用自定义验证器来弄最小长度值
# 对应的字段里面都会对应的自定义验证器使用
username = models.CharField(max_length=10, validators=[validators.MinLengthValidator(limit_value=3)])
password = models.CharField(max_length=10, validators=[validators.MinLengthValidator(limit_value=3)])
telephone = models.CharField(max_length=11, validators=[validators.RegexValidator(r'1[3456789]\d{9}', message='请输入正确的手机号码')])
email = models.CharField(max_length=20, validators=[validators.EmailValidator(message='请输入正确的邮箱地址')])
class Meta:
db_table = 'register'
urls.py
from . import views
from django.urls import path
urlpatterns = [
path('register/', views.RegisterArticle.as_view())
]
register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<table>
<td>
<tr>用户:</tr>
<tr><input type="text" name="username"></tr>
<br>
<tr>密码:</tr>
<tr><input type="password" name="pwd1"></tr>
<br>
<tr>再次输入密码:</tr>
<tr><input type="password" name="pwd2"></tr>
<br>
<tr>电话</tr>
<tr><input type="text" name="telephone"></tr>
<br>
<tr>邮箱</tr>
<tr><input type="text" name="email"></tr>
<br>
<tr><input type="submit" value="提交"></tr>
</td>
</table>
</form>
</body>
</html>
文件上传
完成文件上传必须完成两部分,一部分是前端页面,另外一部分是后端页面
前端HTML代码实现
- 在前端中,我们需要填入一个form标签,然后在这个form标签中指定
enctype="multipart/form-data"
,不然就不能上传文件。 - 在form标签中添加一个input标签,然后指定input标签的name,以及
type="file"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="images"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
后端的代码实现
后端的主要工作是接收文件。然后存储文件。接收文件的方式跟接收POST的方式是一样的,只不过是通过FILES来实现的
- 注意:
images = request.FILES.get('images')
该对应的是一个类,当我们想读取里面的内容出来的时候需要使用到read()
来读取
class UploadFiles(View):
def get(self, request):
return render(request, 'upload.html')
def post(self, request):
# print(request.POST)
# 当选择文件上传的时候,应该选择的是FILES该文件
print(request.FILES) # <MultiValueDict: {'images': [<InMemoryUploadedFile: thumb-1920-771788.png (image/png)>]}>
images = request.FILES.get('images')
print(images) # thumb-1920-771788.png 打印的是图片的名字
print(type(images)) # 但是这张图片是一个类
with open('demo.png', 'wb')as f:
f.write(images.read())
return HttpResponse('success')
使用模型的方式来处理上传的文件
在定义模型的时候,我们可以给存储文件字段指定为FileField
,这个Field
可以传递一个upload_to
c参数,用来指定上传上来的文件保存到哪里。当upload_to="file"
的时候,对应的参数会保存到file
这个文件夹下。注意:(当上传的文件的名字一样的话,对应的Django会帮助我们处理好图片,并不会覆盖)
models.py
class Article(models.Model):
# 当我们想设置最小长度的时候,但是在字段中没有的话,可以借助自定义验证器
# MinLengthValidator
title = models.CharField(max_length=20, validators=[validators.MinLengthValidator(limit_value=3)])
content = models.TextField(max_length=100, validators=[validators.MinLengthValidator(limit_value=3)])
author = models.CharField(max_length=15)
create_time = models.DateTimeField(auto_now_add=True)
# FileField 为文件上传功能
# upload_to:对应的files创建的文件夹目录
images = models.FileField(upload_to='files', null=True)
class Meta:
db_table = 'article'
views.py
class UploadFiles(View):
def get(self, request):
return render(request, 'upload.html')
def post(self, request):
# print(request.POST)
# 当选择文件上传的时候,应该选择的是FILES该文件
# print(request.FILES) # <MultiValueDict: {'images': [<InMemoryUploadedFile: thumb-1920-771788.png (image/png)>]}>
# images = request.FILES.get('images')
# print(images) # thumb-1920-771788.png 打印的是图片的名字
# print(type(images)) # 但是这张图片是一个类
# with open('demo.png', 'wb')as f:
# f.write(images.read())
# ----------------------------------------------------------------
title = request.POST.get('title')
content = request.POST.get('content')
author = request.POST.get('author')
create_time = request.POST.get('create_time')
# 当接收文件的时候使用的是FILES这个文件方式来进行接收
images = request.FILES.get('images')
article = Article(title=title, content=content, author=author, images=images)
article.save()
return HttpResponse('success')
指定MEDIA_ROOT和MEDIA_URL
以上我们是使用了upload_to
来指定上传的文件的目录。我们也可以指定MEDIA_ROOT
,就不需要在FielField
中指定upload_to
,他会自动的将文件上传到MEDIA_ROOT
的目录下
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
在urls.py
中添加MEDIA_ROOT
目录下的访问路径
from django.urls import path
from front import views
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('', views.index),
] + static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
如果我们同时知道MEDIA_ROOT
和upload_to
,那么会将文件上传到MEDIA_ROOT
下的upload_to
文件夹中
upload_to
: %Y/%M/%D
- 这样写的好处是在于对图片的一个进行分类
class Article(models.Model):
# 当我们想设置最小长度的时候,但是在字段中没有的话,可以借助自定义验证器
# MinLengthValidator
title = models.CharField(max_length=20, validators=[validators.MinLengthValidator(limit_value=3)])
content = models.TextField(max_length=100, validators=[validators.MinLengthValidator(limit_value=3)])
author = models.CharField(max_length=15)
create_time = models.DateTimeField(auto_now_add=True)
# FileField 为文件上传功能
# upload_to:对应的files创建的文件夹目录
images = models.FileField(upload_to='%Y/%M/%D', null=True)
class Meta:
db_table = 'article'
限制文件的文件拓展名
如果想要限制上传的文件的拓展名,那么我们就需要用到表单来进行限制。我们可以使用普通的Form表单,也可以使用ModelForm
,直接从模型中读取字段。注意:(当进行文件上传的时候,通过表单验证的时候,需要在对应的参数上面加上request.FILES
),使用ImageField
必须要安装pip install Pillow
models.py
class Article(models.Model):
# 当我们想设置最小长度的时候,但是在字段中没有的话,可以借助自定义验证器
# MinLengthValidator
title = models.CharField(max_length=20, validators=[validators.MinLengthValidator(limit_value=3)])
content = models.TextField(max_length=100, validators=[validators.MinLengthValidator(limit_value=3)])
author = models.CharField(max_length=15)
create_time = models.DateTimeField(auto_now_add=True)
# FileField 为文件上传功能
# upload_to:对应的files创建的文件夹目录
# 自定义验证器对文件上传做相对应的数据验证
# FileExtensionValidator:对应的限制文件上传
# ImageField :默认只允许让你传相对应的图片文件 pip install Pillow
images = models.ImageField(upload_to='%Y/%m/%d', null=True, validators=[validators.FileExtensionValidator(['jpg', 'png'])])
class Meta:
db_table = 'article'
views.py
class UploadFiles(View):
def get(self, request):
return render(request, 'upload.html')
def post(self, request):
# print(request.POST)
# 当选择文件上传的时候,应该选择的是FILES该文件
# print(request.FILES) # <MultiValueDict: {'images': [<InMemoryUploadedFile: demo.png (image/png)>]}>
# images = request.FILES.get('images')
# print(images) # demo.png 打印的是图片的名字
# print(type(images)) # 但是这张图片是一个类
# with open('demo.png', 'wb')as f:
# f.write(images.read())
# ----------------------------------------------------------------
# 当对应需要验证表单和FIELD的时候,需要进行在对应的上面加上参数
form = UploadForm(request.POST, request.FILES)
if form.is_valid():
title = request.POST.get('title')
content = request.POST.get('content')
author = request.POST.get('author')
create_time = request.POST.get('create_time')
# 当接收文件的时候使用的是FILES这个文件方式来进行接收
images = request.FILES.get('images')
article = Article(title=title, content=content, author=author, images=images)
article.save()
return HttpResponse('success')
else:
print(form.errors.get_json_data())
return HttpResponse('fail')
forms.py
class UploadForm(forms.ModelForm):
class Meta:
# 表单验证的一个模型
model = Article
fields = '__all__'
error_messages = {
'images': {
'invalid_extension': '只能上传png和jpg文件格式'
}
}
upload.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<table>
<td>
<tr>标题:</tr>
<tr><input type="text" name="title"></tr>
<br>
<tr>内容:</tr>
<tr><input type="text" name="content"></tr>
<br>
<tr>作者:</tr>
<tr><input type="text" name="author"></tr>
<br>
<tr>文件上传:</tr>
<tr><input type="file" name="images"></tr>
<br>
<tr><input type="submit" value="提交"></tr>
</td>
</table>
</form>
</body>
</html>