设为首页 收藏本站
查看: 649|回复: 1

xxS项目,构建表关系、forms组件、登录获取图片验证码

[复制链接]
累计签到:528 天
连续签到:1 天
发表于 2022-10-17 01:08:30 | 显示全部楼层 |阅读模式
# 数据库表创建及同步
由于django自带的sqlite数据库对日期不敏感,所有我们换成MySQL

# 建表
from django.contrib.auth.models import AbstractUser

class UserInfo(AbstractUser):
    phone=models.BigIntegerField(verbose_name='手机号',null=True)
    # 头像
    avatar = models.FileField(upload_to='avatar/',default='avatar/default.png',verbose_name='用户头像')
    """
    给avatar字段传文件对象 该文件会自动存储到avatar文件下 然后avatar字段只保存文件路径avatar/default.png
    """
    create_time = models.DateField(auto_now_add=True)

    # 与站点是一对一
    blog = models.OneToOneField(to='Blog',null=True)

class Blog(models.Model):
    state_name = models.CharField(max_length=32,verbose_name='站点名称')
    state_title = models.CharField(max_length=32,verbose_name='站点标题')
    state_theme = models.CharField(max_length=64,verbose_name='站点样式') # 存css和js的文件路径

class Category(models.Model):
    name = models.CharField(verbose_name='文章发类',max_length=32)
    blog= models.ForeignKey(to='Blog',null=True)

class Tag(models.Model):
    name = models.CharField(verbose_name='文章标签',max_length=32)
    blog= models.ForeignKey(to='Blog',null=True)

class Article(models.Model):
    title = models.CharField(verbose_name='文章标题',max_length=64)
    desc = models.CharField(verbose_name='文章简介',max_length=255)
    # 文章内容有很多 一般情况下用text
    content = models.TextField(verbose_name='文章内容')
    create_time = models.DateField(auto_now_add=True)

    # 数据库字段设计优化
    up_num = models.BigIntegerField(default=0,verbose_name='点赞数')
    down_num = models.BigIntegerField(default=0,verbose_name='点踩数')
    comment_num = models.BigIntegerField(default=0,verbose_name='评论数')

    # 外键字段
    blog= models.ForeignKey(to='Blog',null=True)
    category = models.ForeignKey(to='Category',null=True)
    tags = models.ManyToManyField(to='Tag',
                                  through='Article2Tag',
                                  through_fields=('article','tag')
                                  )

class Article2Tag(models.Model):
    article= models.ForeignKey(to='Article')
    tag= models.ForeignKey(to='Tag')

class UpAndDown(models.Model):
    user = models.ForeignKey(to='UserInfo')
    article = models.ForeignKey(to='Article')
    is_up = models.BooleanField() # 传布尔值 存0/1

class Comment(models.Model):
    user = models.ForeignKey(to='UserInfo')
    article = models.ForeignKey(to='Article')
    content = models.CharField(verbose_name='评论内容',max_length=255)
    comment_time= models.DateTimeField(auto_now_add=True,verbose_name='评论时间') # 这个是记录修改时间,DateField记录创建时间

    # 自关联
    parent = models.ForeignKey(to='self',null=True) # 有些评论就是跟评论

# 注册功能
我们之前按直接在views.py中书写的forms组件代码
但是为了解耦合 我们应该将所有的forms组件代码单独写道一个地方

如果你的项目自始至终只用到一个forms组件那么你可以直接建一个py文件使用即可
    myforms.py
但是如果你的项目需要使用多个forms组件,那么你可以创建一个文件夹在在文件夹内根据
forms组件的功能的不同创建不同的py文件
    myforms文件夹
        regform.py
        loginform.py
        userform.py
        orderform.py
        ...

[后端]
def register(request):
    form_obj = MyRegForm()
    if request.method == 'POST':
        back_dic = {'code': 1000, 'msg': ''}
        # 校验数据是否合法
        form_obj = MyRegForm(request.POST)
        if form_obj.is_valid():
            #print(form_obj.cleaned_data) {'username': 'jason', 'password': '123', 'confirm_password': '123', 'email': '122@qq.com'}
            clean_data = form_obj.cleaned_data # 将校验通过的数据赋值给一个变量
            # 将字段里面的confirm_password键值对删除
            clean_data.pop('confirm_password') # {'username': 'jason', 'password': '123', 'email': '122@qq.com'}
            # 用户头像
            file_obj = request.FILES.get('avatar')
            """针对用户头像一定的要判断是否有值,不能直接添加到字典里面去"""
            if file_obj:
                clean_data['avatar']=file_obj
            # 直接操作数据库保存数据
            models.UserInfo.objects.create_user(**clean_data)
            back_dic['url'] = '/login/'
        else:
            back_dic['code'] = 2000
            back_dic['msg'] = form_obj.errors
        return JsonResponse(back_dic)
    return render(request,'register.html',locals())
[前端]
-[HTML部分]
<div class="container-fluid">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <h1 class="text-center">注册</h1>
            <form id="myform"> <!--这里我们不用form表单提交数据 只是单纯用一下form标签而已-->
                {% csrf_token %}
                {% for form in form_obj %}
                    <div class="form-group">
                        <label for="">{{ form.label }}</label>
                        {{ form }}
                        <span style="color: red" class="pull-right">{{ form.errors.0 }}</span>
                    </div>
                {% endfor %}
            <div class="form-group">
                <label for="myfile">头像
                    {% load static %}
                    <img src="{% static 'img/default.png' %}" alt="" width="100" style="padding-left: 20px" id="myimg">
                </label>
                <input type="file" id="myfile" name="avatar" style="display: none">
            </div>
            <input type="button" class="btn btn-primary pull-right" value="注册" id="id_commit">
            </form>
        </div>
    </div>
</div>

-[事件1 文本域变化,获取头像显示]
$('#myfile').change(function (){
        // 文件阅读器对象
        // 1、先生成一个文件阅读器对象
        let myFileReaderObj= new FileReader();
        // 2、获取用户上传的文件头像
        let fileObj = $(this)[0].files[0]
        // 3、将文件对象交给阅读读取
        myFileReaderObj.readAsDataURL(fileObj) // 异步操作 IO操作
        // 4、利用文件阅读器将文件展示到前端页面 修改src属性
        // 等待文件阅读器加载完毕之后在执行
        myFileReaderObj.onload = function () {
            $('#myimg').attr('src', myFileReaderObj.result)
        }
    })
-[事件2 点击事件 ajax发送文件、接收报错]
$('#id_commit').click(function (){
        // 发送ajax请求 我们发送请求既包含普通键值对,也包含文件
        let forDataObj = new FormData();
        // 1、添加普通键值对
        {#console.log($('#myform').serializeArray())#} // [{},{},{},{} 只包含普通键值对 ]
        $.each($('#myform').serializeArray(),function (index,obj){
            // console.log(index,obj) obj={}  前端字典又称为对象
            forDataObj.append(obj.name,obj.value)
        })
        // 2、添加文件数据
        forDataObj.append('avatar',$('#myfile')[0].files[0])
        // 3、发送ajax请求
        $.ajax({
            url:'',
            type:'post',
            data:forDataObj,
            // 需要指定两个关键性参数
            contentType:false,
            processData:false,
            success:function (args){
                if(args.code == 1000){
                    // 跳转到登录页面
                    window.location.href = args.url
                }else{
                    // 如何将对应的报错信息提示到对应的input框
                    $.each(args.msg,function (index,obj){
                        let targetId= '#id_'+index;
                        $(targetId).next().text(obj[0]).parent().addClass('has-error')
                    })
                }
            }
        })
    })
-[事件3 获取焦点事件]
// 给所有的input框获取焦点事件
    $('input').focus(function (){
        // 将input下面的span标签的文本和input的has-error的class属性消除
        $(this).next().text(' ').parent().removeClass('has-error')
    })

# 登录功能
img标签的src属性
1、图片路径
2、url
3、图片的二进制数据

我们的计算机上面之所以能够敲出各种字体样式
内部其实对应的是一个个.ttf结尾文件

# 后端代码获取验证码操作
def login(requerst):
    return render(requerst,'login.html')

[图片相关模块]
    pip3 install pillow

from PIL import Image,ImageDraw,ImageFont

-[Image:生成图片]
-[ImageDraw:能够在图片上乱涂乱画]
-[ImageFont:控制字体的样式]

from io import BytesIO,StringIO

-[BytesIO:临时存储数据,返回格式二进制]
-[StringIO:临时存储数据,返回格式字符串]

import random
def get_random():
    return random.randint(0,255),random.randint(0,255),random.randint(0,255)# [自动会将这些用元组组织起来]

def get_code(request):
    # 推到步骤1:直接获取够短现成的图片二进制数据发送给前端
    # with open(r'static/img/111.png','rb')as f:
    #     data=f.read()
    # return HttpResponse(data)

    # 推到步骤2:利用pillow模块动态生成图片
    # img_obj = Image.new('RGB',(430,35),get_random())
    # 将图片存进去
    # with open('xx.png','wb')as f:
    #     img_obj.save(f,'png')
    # 将图片取出来
    # with open('xx.png','rb')as f:
    #     data= f.read()
    # return HttpResponse(data)

    # 推导步骤3:文件存储繁琐IO操作效率低 借助于内存管理器模块
    # img_obj = Image.new('RGB', (430, 35), get_random())
    # io_obj = BytesIO() # 生成一个内置管理对象  你可以看成文件句柄
    # img_obj.save(io_obj,'png')
    # return HttpResponse(io_obj.getvalue()) # 从内存管理器中读取二进制的图片数据返回给前端

[最终步骤4:写图片验证码]
img_obj = Image.new('RGB', (430, 35), get_random())
img_draw = ImageDraw.Draw(img_obj) # 产生画笔对象
img_font = ImageFont.truetype('static/font/222.ttf',30) # 字体样式 大小

[随机验证码 五位数的随机验证码  数字 小写字符 大写字母]
code=''
for i in range(5):
    randon_upper= chr(random.randint(65,90))
    randon_lower= chr(random.randint(95,122))
    randon_int= chr(random.randint(0,99))
    # 从上面三个随机选择一个
    tmp=random.choice([randon_int,randon_upper,randon_lower])
    # 将随机生成字符串写入到图片上面
    img_draw.text((i*60+60,-2),tmp,get_random(),img_font)
    # 拼接到code里面
    code += tmp
print(code)
[随机验证码在登录的视图函数里面需要用到 要比对 所以找地方存起来并且其他视图函数也能拿到]
request.session['code']=code
io_obj=BytesIO()
img_obj.save(io_obj,'png')
return HttpResponse(io_obj.getvalue())

# 前端
<div class="container">
    <div class="row">
        <div class="col-md-8 col-offset-2">
            <h1 class="text-center">登录</h1>
            <div class="form-group">
                <label for="username">用户名</label>
                <input type="text" name="username" id="username" class="form-control">
            </div>
            <div class="form-group">
                <label for="passowrd"></label>
                <input type="password" name="password" id="password" class="form-control">
            </div>
            <div class="form-group">
                <label for="">验证码</label>
                <div class="row">
                    <div class="col-md-6 ">
                        <input type="text" name="code" id="id-code" class="form-control">
                    </div>
                    <div class="col-md-6"  >
                        <img src="/get_code/" alt="" width="380" height="35" id="id_img">
                    </div>
                </div>
            </div>
            <input type="button" class="btn btn-success" value="登录">
        </div>
    </div>
</div>

-[图片点击事件]
<script>
    $('#id_img').click(function (){
        // 先获取到标签的src
        let img_obj = $(this).attr('src');
        // 在src后面加任意字符
        $(this).attr('src',img_obj+='?')
    })
</script>

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-1002574-1-1.html 上篇帖子: Django之forms组件使用 下篇帖子: Django之forms组件
累计签到:528 天
连续签到:1 天
 楼主| 发表于 2022-10-23 18:34:58 | 显示全部楼层
本帖最后由 郑统京 于 2022-10-23 18:39 编辑

# 补充
# 后端登录代码
def login(request):
    if request.method == 'POST':  # 可以用request.ajax方法
        back_dic = {'code': 1000, 'msg': ''} # 注意用ajax方法一定要定义一个全局标志位
        username = request.POST.get('username')
        password = request.POST.get('password')
        code = request.POST.get('code')
        # 1 先校验验证码是否正确      自己决定是否忽略            统一转大写或者小写再比较
        if request.session.get('code').upper() == code.upper(): # 后端代码发送给session,从session中获取验证码比对
            user_obj = auth.authenticate(request, username=username, password=password)
            if user_obj:
                # 保存用户登录状态
                auth.login(request,user_obj)
                back_dic['url'] = '/home/'
            else:
                back_dic['code'] = 2000
                back_dic['msg'] = '用户名或密码错误'
        else:
            back_dic['code'] = 3000
            back_dic['msg'] = '验证码错误'
        return JsonResponse(back_dic)
    return render(request, 'login.html')
[前端代码]
关于home页的script的后续代码
//像后端发送ajax请求,并处理返回结果
    $('#login').click(function (){
        $.ajax({
            url:'',
            type:'post',
            data:{
                'username':$('#username').val(),
                'password':$('#password').val(),
                'code':$('#id-code').val(),
                // 自己结合自己需求 合理选择
                'csrfmiddlewaretoken':'{{ csrf_token }}'
            },
        success:function (args){
                if(args.code==1000){
                    window.location.href =args.url
                }else{
                    // 渲染错误信息
                    $('#error').text(args.msg)
                }
        }
        })
    })

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表