学习日记之《Django3 Web应用开发实战》——第八章——表单和模型

本文详细介绍了在Django中创建和使用表单,包括自定义验证函数、ModelForm的使用、数据清洗方法以及多文件上传的处理。同时涵盖了表单提交和视图层的操作,以及错误信息的展示。
摘要由CSDN通过智能技术生成

第八章——表单和模型

完整的表单主要由4个部分组成:

  • 提交地址:即form标签的action属性
  • 请求方式:form标签的method属性
  • 元素控件: 控件
  • 提交按钮:控件
# forms.py 
from django import forms
from django.core.exceptions import ValidationError
from .models import PersonInfoed, Vocation
# 自定义数据验证函数
def payment_validate(value):
    if value > 30000:
        raise ValidationError('请输入合理的薪资')
    
class VocationForm(forms.Form):
    job = forms.CharField(max_length=20, label='职位')
    title = forms.CharField(max_length=20, label='职称',
                            widget=forms.widgets.TextInput(attrs={'class': 'c1'}),
                            error_messages={'required': '职称不能为空'})
    payment = forms.IntegerField(label='薪资',validators=[payment_validate])  # 数据验证
    values= PersonInfoed.objects.values('name')
    choices = [(i+1, v['name']) for i, v in enumerate(values)]
    person = forms.ChoiceField(choices=choices, label='姓名')
	
	# 数据清洗 clean_ + 字段名
    def clean_title(self):
        data = self.cleaned_data['title']
        return '初级' + data

ModelForm

class VocationForm(forms.ModelForm):
    LEVEL = (('L1', '初级'),
             ('L2', '中级'),
             ('L3', '高级'))
    level = forms.ChoiceField(choices=LEVEL, label='级别')

    class Meta:
        model = Vocation
        fields = ['job', 'title', 'payment', 'person']
        # fields = '__all__'
        # exclude = []
        labels = {
            'job': '职位',
            'title': '职称',
            'payment': '薪资',
            'person': '姓名'
        }
        widgets = {
            'job': forms.widgets.TextInput(attrs={'class': 'c1'}),
        }
        # 重新定义字段类型
        field_classes = {
            'job': forms.CharField
        }

        # 帮助信息
        help_texts = {
            'job': '请输入职位名称'
        }
        # 自定义错位信息
        error_messages = {
            # __all__ 设置全部错误信息
            '__all__': {
                'required': '请输入内容',
                'invalid': '请检查输入内容',
            # 设置某个字段的错误信息
            'title': {
                'required': '请输入职称内容',
                'invalid': '请检查职称输入内容',
            }
            }
        }
        
    def clean_payment(self):
        '''自定义表单字段清洗'''
        data = self.cleaned_data['payment'] + 100
        return data

视图里使用Form

forms.py
class VocationForm(forms.Form):
    job = forms.CharField(max_length=20, label='职位')
    title = forms.CharField(max_length=20, label='职称',
                            widget=forms.widgets.TextInput(attrs={'class': 'c1'}),
                            error_messages={'required': '职称不能为空'})
    payment = forms.IntegerField(label='薪资',validators=[payment_validate])
    values= PersonInfoed.objects.values('name')
    choices = [(i+1, v['name']) for i, v in enumerate(values)]
    person = forms.ChoiceField(choices=choices, label='姓名')

    def clean_title(self):
        data = self.cleaned_data['title']
        return '初级' + data
views.py
def index(request):
    if request.method == 'GET':
        id = request.GET.get('id', '')
        if id:
            d = Vocation.objects.filter(id=id).values()
            print('ddd', d)
            d = list(d)[0]
            d['person'] = d['person_id']
            i = dict(initial=d, label_suffix='*', prefix='vv')
            v = VocationForm(**i)
        else:
            v = VocationForm(prefix='vv')
        return render(request, 'index.html', locals())
    else:
        v = VocationForm(data=request.POST, prefix='vv')
        print('request_data', request.POST)
        # print('vv', v)
        if v.is_valid():
            title = v['title']
            # print('title1', title)
            ctitle = v.cleaned_data['title']
            # print('ctitle', ctitle)
            id = request.GET.get('id', '')
            d = v.cleaned_data
            print('d1d1d1', d)
            d['person_id'] = int(d['person'])
            Vocation.objects.filter(id=id).update(**d)
            return HttpResponse('提交成功')
        else:
            error_msg = v.errors.as_json()
            print('error_msg', error_msg)
            return render(request, 'index.html', locals())

视图里使用ModelForm

forms.py
class VocationForm(forms.ModelForm):
    # LEVEL = (('L1', '初级'),
    #          ('L2', '中级'),
    #          ('L3', '高级'))
    # level = forms.ChoiceField(choices=LEVEL, label='级别')

    class Meta:
        model = Vocation
        fields = ['job', 'title', 'payment', 'person']
        # fields = '__all__'
        # exclude = []
        labels = {
            'job': '职位',
            'title': '职称',
            'payment': '薪资',
            'person': '姓名'
        }
        # widgets = {
        #     'job': forms.widgets.TextInput(attrs={'class': 'c1'}),
        # }
        # # 重新定义字段类型
        # field_classes = {
        #     'job': forms.CharField
        # }

        # 帮助信息
        # help_texts = {
        #     'job': '请输入职位名称'
        # }
        # 自定义错位信息
        error_messages = {
            # __all__ 设置全部错误信息
            '__all__': {
                'required': '请输入内容',
                'invalid': '请检查输入内容',
            # 设置某个字段的错误信息
            'title': {
                'required': '请输入职称内容',
                'invalid': '请检查职称输入内容',
            }
            }
        }

    def clean_payment(self):
        '''自定义表单字段清洗'''
        data = self.cleaned_data['payment'] + 100
        return data
views.py
def index(request):
    if request.method == 'GET':
        id = request.GET.get('id', '')
        if id:
            d = Vocation.objects.filter(id=id).first()
            print('dd', d)
            v = VocationForm(instance=d, prefix='vv')
        else:
            v = VocationForm(prefix='vv')
        return render(request, 'index.html', locals())
    else:
        v = VocationForm(data=request.POST, prefix='vv')
        print('request_POST', request.POST)
        print('request_GET', request.GET)
        # print('vv', v)
        if v.is_valid():
            id = request.GET.get('id', '')

            result = Vocation.objects.filter(id=id)
            if not result:
                # method one
                # v.save()
                # method two
                v1 = v.save(commit=False)
                v1.title = '初级' + v1.title
                v1.save()
                # method three
                # v.save_m2m()  # 保存manytomany的数据模型
                return HttpResponse('新增成功')
            else:
                d = v.cleaned_data
                d['title'] = '中级' + d['title']
                result.update(**d)
                return HttpResponse('修改成功')
        else:
            error_msg = v.errors.as_json()
            print('error_msg', error_msg)
            return render(request, 'index.html', locals())

同一个网页多个表单

views.py
def index(request):
    if request.method == 'GET':
        v = VocationForm(prefix='vv')
        w = VocationForm(prefix='ww')
        return render(request, 'index.html', locals())
    else:
        v = VocationForm(data=request.POST, prefix='vv')
        w = VocationForm(data=request.POST, prefix='ww')
        print('request_POST', request.POST)
        print('request_GET', request.GET)
        # print('vv', v)
        if v.is_valid():
            HttpResponse('表单一提交成功')
        elif w.is_valid():
            HttpResponse('表单二提交成功')
        else:
            error_msg = v.errors.as_json()
            print('error_msg', error_msg)
            return render(request, 'index.html', locals())
index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {% if v.errors %}
        <p>
            数据出错啦,错误信息:{{ v.errors }}
        </p>
    {% else %}
        <form action="" method="post">
        {% csrf_token %}
            <table>
                {{ v.as_table }}
            </table>
            <input type="submit" value="提交">
        </form>
        
        <form action="" method="post">
        {% csrf_token %}
            <table>
                {{ w.as_table }}
            </table>
            <input type="submit" value="提交">
        </form>
    {% endif %}
</body>
</html>

一个表单多个按钮

views.py
def index(request):
    if request.method == 'GET':
        v = VocationForm(prefix='vv')
        return render(request, 'index.html', locals())
    else:
        v = VocationForm(data=request.POST, prefix='vv')
        if v.is_valid():
            if 'add' in request.POST:
                return HttpResponse('提交成功')
            else:
                return HttpResponse('修改成功')
        else:
            error_msg = v.errors.as_json()
            print('error_msg', error_msg)
            return render(request, 'index.html', locals())
index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {% if v.errors %}
        <p>
            数据出错啦,错误信息:{{ v.errors }}
        </p>
    {% else %}
        <form action="" method="post">
        {% csrf_token %}
            <table>
                {{ v.as_table }}
            </table>
            <input type="submit" name="add" value="提交">
            <input type="submit" name="update" value="修改">
        </form>
    
    {% endif %}
</body>
</html>

表单的批量处理

views.py
from django.shortcuts import render
from django.http import HttpResponse
from django import forms
from .forms import VocationForm
from .models import Vocation

# Create your views here.
def index(request):
    # 参数extra 设置表单数量
    # max_num 限制表单的最大数量
    pfs = forms.formset_factory(VocationForm, extra=2, max_num=5)
    # 另一种
    # pfs = forms.modelformset_factory(model=Vocation, form=VocationForm, extra=0, max_num=5)
    if request.method == 'GET':
        p = pfs()
        return render(request, 'index.html', locals())
    else:
        p = pfs(request.POST)
        if p.is_valid():
            for i in p:
                i.save()
            return HttpResponse('新增成功')
        else:
            error_msg = p.errors.as_json()
            print('error_msg', error_msg)
            return render(request, 'index.html', locals())

多文件批量上传

# models.py
# 设置模型,配置上传地址
class Mystorage(FileSystemStorage):   # 对上传文件做自定义处理
    def get_available_name(self, name, max_length=None):
        print('name', name)
        if self.exists(name):
            print('jjj')
            os.remove(os.path.join(settings.MEDIA_ROOT, name))
        return name

class CertificateInfo(models.Model):
    id = models.AutoField(primary_key=True)
    certificate = models.FileField(blank=True, upload_to='images/', storage=Mystorage())  # 文件上传
    person = models.ForeignKey(PersonInfoed, on_delete=models.CASCADE, blank=True, null=True)

    def __str__(self):
        return str(self.person)

    class Meta:
        verbose_name = '证件信息'
# forms.py
class PersonInfoForm(forms.ModelForm):
    certificate = forms.FileField(label='证件', allow_empty_file=True,
                                  widget=forms.ClearableFileInput(attrs={'multiple': True}))  # 多文件上传

    class Meta:
        model = PersonInfoed
        fields = '__all__'
        labels = {
            'name': '姓名',
            'age': '年龄'
        }
# views.py
# 视图中处理
def index(request):
    if request.method == 'GET':
        id = request.GET.get('id', '')
        if id:
            i = PersonInfoed.objects.filter(id=id).first()
            p = PersonInfoForm(instance=i)
        else:
            p = PersonInfoForm()
        return render(request, 'index.html', locals())
    else:
        p = PersonInfoForm(data=request.POST, files=request.FILES)
        if p.is_valid():
            name = p.cleaned_data['name']
            result = PersonInfoed.objects.filter(name=name)
            if not result:
                p.save()
                id = p.id
                print('here1', request.FILES.getlist('certificate'))

                for f in request.FILES.getlist('certificate'):
                    f.name = f"{id}.".join(f.name.split('.'))
                    d = dict(person_id=id, certificate=f)
                    CertificateInfo.objects.create(**d)

                return HttpResponse('新增成功')
            else:
                age = p.cleaned_data['age']
                d = dict(name=name, age=age)
                result.update(**d)
                id = result.first().id
                print('id', id)
                for c in CertificateInfo.objects.filter(person_id=id):
                    fn = c.certificate.name
                    os.remove(os.path.join(settings.MEDIA_ROOT, fn))
                    c.delete()

                print('here2', request.FILES.getlist('certificate'))
                for f in request.FILES.getlist('certificate'):
                    print('here3', f.name, type(f.name))
                    f.name = f"{id}.".join(f.name.split('.'))
                    print('hh', f.name)
                    d = dict(person_id=id, certificate=f)
                    CertificateInfo.objects.create(**d)
                return HttpResponse('修改成功')
        else:
            error_msg = p.errors.as_json()
            print('error_msg', error_msg)
            return render(request, 'index.html', locals())
项目路由 urls.py
from django.contrib import admin
from django.urls import path, include, re_path
from django.views.static import serve
from django.conf import settings

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('app1.urls')),
]

urlpatterns += [re_path("media/(?P<path>.*)", serve, {"document_root": settings.MEDIA_ROOT}, name='media')]

# settings.py  配置
MEDIA_URL = "/media/"  # 媒体路由地址
MEDIA_ROOT = BASE_DIR / "media"  # 媒体路径信息
# html 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {% if p.errors %}
        <p>
            数据出错啦,错误信息:{{ p.errors }}
        </p>
    {% else %}
        <form action="" method="post" enctype="multipart/form-data">
        {% csrf_token %}
            <ul>
                <li>姓名:{{ p.name }}</li>
                <li>年龄:{{ p.age }}</li>
                <li>证件:{{ p.certificate }}</li>
            </ul>
            <input type="submit" value="提交">
        </form>
    {% endif %}
</body>
</html>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值