Django----ModelForm&文件上传

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_toc参数,用来指定上传上来的文件保存到哪里。当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_ROOTupload_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>

在这里插入图片描述

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_37662827/article/details/107501119
今日推荐