5.10关于文件上传

2.1基本操作

(获取文件,Django内部读取文件、操作文件)

1、在url.py中

from app01.views import depart,user,pretty,admin,account,task,order,chart,upload
# 上传文件 path('upload/list/', upload.upload_list),

 

这样就可以直接访问网页127.0.0.1:8000/upload/list/

 

 

 

 

 

 

 

新建 .html 表单

{% extends 'layout.html' %}

{% block  content %}
    <div  class="container">
         <form method="post"  enctype="multipart/form-data">
             {% csrf_token % #通过Django进行提交
             <input type="text"   name="username">
             <input type="file" name="avatar">  #名字为头像
             <input type="submit"  value="提交">
         </form>

    </div>

{% endblock %}

 

新建upload,py文件

from django.shortcuts import render,HttpResponse


def upload_list(request):
    if request.method == "GET": #用户看到页面
        return render(requests, 'upload_list.html')

    # # 'username':[big666'] 下面代码呈现的内容
    # print(request.POST)  # 请求体中数据
    # # {‘avatar':[<InMemoryUploadFile:图片1.png(image/png)>]}> 下面代码呈现的内容
    # print(request.FILES)  # 请求发过来的文件{}

    #先不进行request.POST的读取,先根据文件名获取文件对象
    file_object = request.FILES.get("avatar")
    print(file_object.name)  # 获取到文件名:图片1.png

    # 进行request.FILES文件对象内容的读取,并且上传。操作如下:
    f = open('a1.png', mode='wb')  #当前文件以二进制方式写入一个新的本地文件中 ,或者f = open('file_object.name', mode='wb') ,那么上传的文件名与新的本地文件名一致
    for chunk in file_object.chunks():
        f.write(chunk)
    f.close()

    return HttpResponse("...")

 得到如下效果:

 

 

TIP:

刚开始请求体中数据request.POST时请求的图片的名字而不是文件内容,即request.FILES中没有数据:

如下操作后:

 

 

 

 

 

 

request.FILES中出现了文件对象,即文件内容。如下图:

 注册部分全部代码ending:

在views\account.py中:

# 用户账户相关功能:注册、短信、登录、注销
from django.http import JsonResponse
from django.shortcuts import render, HttpResponse
from web.forms.account import RegisterModelForm, SendSmsForm
from app01 import models


def register(request):
    # 注册
    if request.method == 'GET':
        form = RegisterModelForm()
        return render(request, 'web/register.html', {'form': form})
    # print(request.POST)  # 后台拿到的数据,传到ModelForm进行校验

    form = RegisterModelForm(data=request.POST)
    if form.is_valid():
        # 验证通过后,写入数据库(密码要是密文)
        # form.instance.password ="iudasndfiajsd;fj" #在保存之前将instance中的password进行重置
        form.save()  # 自动剔除数据库中没有的字段
        return JsonResponse({'status': True, 'data': '/login/'})  # 注册成功后进行前端跳转页面
    # 写入数据库的另外一种写法
    # data =form.cleaned_data
    # data.pop('code')
    # data.pop('confirm_password')
    # instance = models.UserInfo.objects.create(**data)

    # print(form.cleaned_data)  # 校验成功,输出cleaned_data
    return JsonResponse({'status': False, 'error': form.errors})


def send_sms(request):
    # 发送短信
    # print(request.GET)
    # mobile_phone = request.GET.get('mobile phone')
    # tpl = request.GET.get('tpl')  # register/login  根据tpl取短信模版
    # sms_template_id = settings.TENCENT_SMS_TEMPLATE9(tpl)
    form = SendSmsForm(request, data=request.GET)
    # 只是校验手机号:不能为空,格式是否正确
    if form.is_valid():  # 判断在form中是否校验成功
        # 验证通过后,发短信
        # 写redis
        return JsonResponse({'status': True})  # 表示短信发送成功

    return JsonResponse({'status': False, 'error': form.errors})  # 表示校验失败,所有错误信息都会放在form.errors中
    # return HttpResponse('{"k1":123}')

在forms\account.py中:

import random
import requests
from django import forms
from django.conf import settings

from django_redis import get_redis_connection
from app01 import models
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError

from utils import encrypt
from utils.tencent.sms import send_sms_single


class RegisterModelForm(forms.ModelForm):
    # 重写规则,进行表单展示.增加正则表达式的校验
    mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
    password = forms.CharField(
        label='密码',
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': "密码长度不能小于8个字符",
            'max_length': "密码长度不能大于64个字符"
        },
        widget=forms.PasswordInput())

    confirm_password = forms.CharField(
        label='重复密码',
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': "重复密码长度不能小于8个字符",
            'max_length': "重复密码长度不能大于64个字符"
        },
        widget=forms.PasswordInput())
    code = forms.CharField(
        label='验证码',
        widget=forms.TextInput())

    class Meta:
        model = models.UserInfo
        fields = ['username', 'email', 'password', 'confirm_password', 'mobile_phone', 'code']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for name, field in self.fields.items():
            field.widget.attrs['class'] = 'form-control'
            field.widget.attrs['placeholder'] = '请输入%s' % (field.label,)

    # 利用钩子函数(用户名),对字段进行校验
    def clean_username(self):
        username = self.cleaned_data['username']

        exists = models.UserInfo.objects.filter(username=username).exists()
        if exists:
            raise ValidationError('用户名已存在')  #抛出异常后不会在返回值,所以cleaned_data中不会有username
            # self.add_error('username', '用户名已存在')  # 把错误信息进行添加但仍然会返回username的值
        return username

    # 利用钩子函数(邮箱),对字段进行校验
    def clean_email(self):
        email = self.cleaned_data['email']
        exists = models.UserInfo.objects.filter(email=email).exists()
        if exists:
            raise ValidationError('邮箱已存在')
        return email

    # 利用钩子函数(密码),对字段进行校验
    def clean_password(self):
        pwd = self.cleaned_data['password']
        # 加密&返回
        return encrypt.md5(pwd)

    # 利用钩子函数(确认密码),对字段进行校验
    def clean_confirm_password(self):
        # pwd = self.cleaned_data['password']
        pwd = self.cleaned_data.get('password')  # password是否校验通过,都可以拿到这个字段的值
        confirm_pwd = encrypt.md5(self.cleaned_data['confirm_password'])  # 密文与密文进行匹配
        if pwd != confirm_pwd:
            raise ValidationError('两次密码不一致')
        return confirm_pwd

    # 利用钩子函数(手机号),对字段进行校验
    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data['mobile_phone']
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if exists:
            raise ValidationError('手机号已注册')
        return mobile_phone

    # 利用钩子函数(验证码),对字段进行校验

    def clean_code(self):
        code = self.cleaned_data['code']
        # mobile_phone = self.cleaned_data['mobile_phone']
        mobile_phone = self.cleaned_data.get('mobile_phone')
        if not mobile_phone:
            return code
        conn = get_redis_connection()
        redis_code = conn.get(mobile_phone)  # 根据手机号获取redis 里的code
        if not redis_code:  # redis里没有code
            raise ValidationError('验证码失效或未发送,请重新发送')
        redis_str_code = redis_code.decode('utf-8')  # 字节变成字符串

        if code.strip() != redis_str_code():
            raise ValidationError('验证码错误,请重新输入')

        return code


class SendSmsForm(forms.Form):
    mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
    print("mobile_phone", mobile_phone)

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    # 验证手机号
    def clean_mobile_phone(self):
        """手机号校验的钩子函数"""

        # 校验数据前,都需要获取到被校验的数据
        mobile_phone = self.cleaned_data['mobile_phone']  # 获取用户提交的手机号

        # 判断短信模版是否有问题
        tpl = self.request.GET.get('tpl')
        mobile_phone = self.request.GET.get('mobile_phone')
        print(tpl)
        template_id = settings.TENCENT_SMS_TEMPLATE[tpl]
        if not template_id:
            # self.add_error('mobile_phone', '短信模版错误')
            raise ValidationError('短信模版错误')

        # 验证数据库中是否已有手机号
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if exists:
            raise ValidationError('手机号已存在')

        # 发短信
        code = random.randrange(1000, 9999)  # 生成随机验证码

        # 发送短信
        sms = send_sms_single(mobile_phone, template_id, [code, ])
        if sms['result'] != 0:
            raise ValidationError("短信发送失败,{}".format(sms['errmsg']))

        # 发送成功后,短信验证码写入redis(利用django-redis组件)
        conn = get_redis_connection()  # redis的连接获取到
        conn.set(mobile_phone, code, ex=60)  # 设置key,value,超时时间为60

        return mobile_phone

 

posted @ 2022-05-06 15:31  费皿啊  阅读(38)  评论(0)    收藏  举报