首页 > 其他分享 >BBS多人博客系统

BBS多人博客系统

时间:2022-09-18 19:56:35浏览次数:66  
标签:username form img models BBS 博客 多人 data id

今日内容

项目开发流程

项目名字:
BBS:多人博客系统

传统软件行业:给第三方做解决方案
互联网行业:饿了么,叮咚买菜...

软件开发流程:
-项目立项(高层,市场人员:市场调研,跟客户对接)
-项目设计(产品经理:设计软件功能,设计原型图)
-项目的具体设计(UI:切图)
-分任务开发
    -前端:pc,小程序,移动端
        -分任务开发
    -后端:
        -架构,数据库设计---(设计表,表关联)
        -多人系统开发
-联调
-测试:功能测试,自动化测试,接口测试、、、
-上线运行:运维

-出现bug,项目维护阶段
-版本迭代

bbs项目表设计及关联

# 要开发的功能
-注册功能
-登录功能
-首页:文章展示,导航栏,用户中心,广告位
-个人主页:文章展示,侧边栏过滤(分类,标签,时间)
-文章详情:点赞点踩,评论(父评论,子评论)
-后台管理:这个人文章展示(增加,删除,修改文章)
-发布文章:富文本编辑器,xss攻击处理

 
# 技术选型:python3.8  django2.2.2  mysql:5.7  jquery2.x   bootstrap@3


# 设计数据库---》数据库名字 bbs
-用户表(基于auth的user表扩写,扩写字段)
-博客表(跟用户表一对一)
-标签表
-分类表
-文章表
-点赞点踩表
-评论表


# 表的关联关系
-用户表(基于auth的user表扩写,扩写字段)
-博客表(跟用户表一对一)
-标签表:跟博客表一对多,跟文章是多对多
-分类表:跟博客表一对多,跟文章是一对多
-文章表:跟博客表是一对多
-点赞点踩表:跟用户一对多,跟文章表一对多
-评论表:跟用户一对多,跟文章表一对多

补充:
OneToOneField,ForeignKey,ManyToManyField
-related_name:反向操作时,使用的字段名,用于代替原反向查询时的’表名_set’。
-related_query_name:反向查询操作时,使用的连接前缀,用于替换表名

img

项目表字段编写和表迁移

创建项目

# 第一步:安装djagno 2.2.2
pip3 install django==2.2.2
# 第二步:使用pycharm创建项目
# 第三步:配置文件
-58行:
    'DIRS': [os.path.join(BASE_DIR , 'templates')]

-国际化
    LANGUAGE_CODE = 'zh-hans'   # 语言中文
    TIME_ZONE = 'Asia/Shanghai' # 时区使用上海时区
    USE_I18N = True
    USE_L10N = True
    USE_TZ = False

-使用mysql
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'HOST': '127.0.0.1',
            'PORT': 3306,
            'NAME': 'bbs',  # 去创建数据库---》navicat创建
            'USER': 'root',
            'PASSWORD': '123',
        }
    }
-创建bbs数据库
    navicat创建

# 扩写auth的user表
AUTH_USER_MODEL = 'blog.UserInfo'

在models中写表模型

from django.db import models
from django.contrib.auth.models import AbstractUser

# 7 张表
# 继承AbstractUser,扩写字段:头像字段,手机号字段, 一对一的博客字段
class UserInfo(AbstractUser):
    # username,password,email。。。。
    phone = models.CharField(max_length=32, null=True)
    # 存文件的字段---》本质还是varchar,可以把文件自动保存(avatar文件夹下),存文件地址
    avatar = models.FileField(upload_to='avatar', default='avatar/default.png')
    blog = models.OneToOneField(to='Blog', on_delete=models.CASCADE, null=True)  # 一对一关联了blog表


class Blog(models.Model):
    title = models.CharField(max_length=32, null=True, verbose_name='主标题')
    site_name = models.CharField(max_length=32, null=True, verbose_name='副标题')
    site_style = models.CharField(max_length=32, null=True, verbose_name='个人站点样式')


class Tag(models.Model):
    name = models.CharField(max_length=32, verbose_name='标签名字')
    # on_delete可以有很多选项,目前先用级联删除(很危险)
    blog = models.ForeignKey(to='Blog', on_delete=models.CASCADE)


class Category(models.Model):
    name = models.CharField(max_length=32, verbose_name='分类名字')
    # on_delete可以有很多选项,目前先用级联删除(很危险)
    blog = models.ForeignKey(to='Blog', on_delete=models.CASCADE)


class Article(models.Model):
    title = models.CharField(max_length=32, verbose_name='文章名字')
    desc = models.CharField(max_length=255, verbose_name='文章摘要')
    content = models.TextField(verbose_name='文章内容')  # 大文本
    # auto_now_add   auto_now
    create_time = models.DateTimeField(auto_now_add=True,verbose_name='文章创建时间')         #auto_now_add=True 新增文章这个字段可以不传,自动把当前时间加上

    blog = models.ForeignKey(to='Blog', on_delete=models.CASCADE)
    category = models.ForeignKey(to='Category', on_delete=models.CASCADE)
    # 多对多关系,需要创建第三张表,
    #  django的orm的ManyToManyField字段可以自动创建第三张表(ArticleToTag)
    #  手动创建第三张表:ManyToManyField一定要加两个参数,through=中间表 , through_fields通过哪两个字段关联
    tag = models.ManyToManyField(to='Tag')


class UpAndDown(models.Model):
    user = models.ForeignKey(to='UserInfo', on_delete=models.CASCADE)
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE)
    is_up = models.BooleanField(verbose_name="点赞或点踩")
    create_time = models.DateTimeField(auto_now_add=True)


class Comment(models.Model):
    user = models.ForeignKey(to='UserInfo', on_delete=models.CASCADE)
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE)
    content = models.CharField(max_length=64, verbose_name="评论的内容")
    create_time = models.DateTimeField(auto_now_add=True)
    # parent=models.ForeignKey(to='Comment', on_delete=models.CASCADE)
    # parent=models.IntegerField(null=True)
    parent = models.ForeignKey(to='self', on_delete=models.CASCADE, null=True)

    # 自关联字段---》父评论--》子评论
    '''
    id    user    article    content             parent
    1       2        1       写的真好               空
    2       2        2       写的一般               空
    3       3        2       人家明明写的很好         2
    '''


# 安装pymysql
    pip3 install pymysql
# 在__ini__.py中加入:djagno默认操作mysql数据库使用的是 mysqlDB模块,在python2中没问题,但是在python3中已经不维护了,不支持,python3中操作mysql咱么用pymysql比较多,但是需要加下面两句话才能正常使用-----》猴子补丁(动态替换--把源码使用mysqlDB替换成pymysql的东西)
# 但是在django2.0.7及以后版本,需要改源码才能使用:operations.py---》146行,改成query = query.encode(errors='replace')
    import pymysql
    pymysql.install_as_MySQLdb()

# 以后直接使用 mysqlclient:有可能装不上---》看人品
    pip3 install mysqlclient

注册forms编写

from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError
from models import  UserInfo  # 模块导入:相对导入和绝对导入

class RegisterForm(forms.Form):
    # 用户名,密码,确认密码,邮箱
    username = forms.CharField(max_length=8, min_length=3, required=True, error_messages={
        'max_length': '太长了',
        'min_length': '太短了',
        'required': '这个必填'
    }, widget=widgets.TextInput(attrs={'class': 'form-control'}))
    password = forms.CharField(max_length=8, min_length=3, required=True, error_messages={
        'max_length': '太长了',
        'min_length': '太短了',
        'required': '这个必填'
    }, widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    re_password = forms.CharField(max_length=8, min_length=3, required=True, error_messages={
        'max_length': '太长了',
        'min_length': '太短了',
        'required': '这个必填'
    }, widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(widget=widgets.EmailInput(attrs={'class': 'form-control'}))

    # 局部钩子校验
    # 方案一:
    def clean_username(self):
        username=self.cleaned_data.get('username')
        user=UserInfo.objects.filter(username=username).first()
        if user:
            # 已经存在,不合理
            raise ValidationError('该用户名已经存在')
        else:
            return username

    #方案二:
    # def clean_username(self):
    #     username=self.cleaned_data.get('username')
    #     try:
    #         UserInfo.objects.get(username=username) #有且只有一条才ok,否则就抛异常
    #         raise ValidationError('该用户名已经存在')
    #     except Exception:
    #         return username


    # 全局钩子校验
    def clean(self):
        # 比较两个密码是否一致  :cleaned_data存的是校验过后的数据,是个字典
        password = self.cleaned_data.get('password')
        re_password = self.cleaned_data.get('re_password')
        if password == re_password:
            # 合理,返回校验过后的数据
            return self.cleaned_data
        else:
            # 不合理,抛出校验失败的异常
            raise ValidationError('两次密码不一致!!')

注册页面搭建

# bootstrap 搭建
    -settings.py 中 加入static
        STATIC_URL = '/static/'
        STATICFILES_DIRS=[
            os.path.join(BASE_DIR,'static')
        ]
    -把bootstrap的静态资源copy到static文件夹下
# 新建模板文件 register.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1 class="text-center">注册功能</h1>
            <form id="id_form">

                {% for item in form %}
                    <div class="form-group">
                        <label for="{{ item.id_for_label }}">{{ item.label }}</label>
                        {{ item }}
                        <span class="pull-right text-danger"></span>
                    </div>
                {% endfor %}

                <div class="form-group">
                    <label for="id_file">头像
                        <img src="/static/img/default.png" alt="" height="80px" width="80px" style="margin-left: 10px">
                    </label>
                    <input type="file" id="id_file" accept="image/*" style="display: none">
                </div>
                <div class="form-group text-center">
                    {#                    如果input类型是submit或者button标签,放在form表单中,如果点提交,触发form的提交,如果我们写了ajax提交,会触发两次提交#}
                    <input type="button" value="注册" class="btn btn-danger" id="id_submit">
                    <span class="text-danger"></span>
                </div>
            </form>
        </div>
    </div>
</div>

</body>
</html>

头像动态显示

# js 控制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <script src="/static/jquery-3.3.1/jquery-3.3.1.min.js"></script>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1 class="text-center">注册功能</h1>
            <form id="id_form">

                {% for item in form %}
                    <div class="form-group">
                        <label for="{{ item.id_for_label }}">{{ item.label }}</label>
                        {{ item }}
                        <span class="pull-right text-danger"></span>
                    </div>
                {% endfor %}

                <div class="form-group">
                    <label for="id_file">头像
                        <img src="/static/img/default.png" alt="" height="80px" width="80px" style="margin-left: 10px" id="id_img">
                    </label>
                    <input type="file" id="id_file" accept="image/*" style="display: none">
                </div>
                <div class="form-group text-center">
                    {#                    如果input类型是submit或者button标签,放在form表单中,如果点提交,触发form的提交,如果我们写了ajax提交,会触发两次提交#}
                    <input type="button" value="注册" class="btn btn-danger" id="id_submit">
                    <span class="text-danger"></span>
                </div>
            </form>
        </div
    </div>
</div>
</body>
<script>
    $("#id_file").change(function () {
        // 把当前图片,放到img标签中
        // 把图片地址放到img标签上,标签就显示了图片
        //$("#id_img")[0].src='https://tva1.sinaimg.cn/large/00831rSTly1gd1u0jw182j30u00u043b.jpg'

        // 1 id_file这个标签的图片读出来,借助于文件阅读器,js提供的一个类
        var reader=new FileReader()
        // 2 拿到文件对象,赋值给一个变量
        var file =$("#id_file")[0].files[0]
        // 3 把文件读到文件阅读器中
        reader.readAsDataURL(file)
        // 4 等读完后,把文件阅读器的内容写到img标签上
        //$("#id_img")[0].src=reader.result
        reader.onload=function (){
            //$("#id_img")[0].src=reader.result
            $('#id_img').attr('src', reader.result)
        }
    })
</script>
</html>

注册功能后端

def register(request):
    if request.method == 'GET':
        register_form = RegisterForm()
        # context: 上下文
        return render(request, 'register.html', context={'form': register_form})
    else:  # post请求的时候
        res = {'code': 100, 'msg': '注册成功'}
        # 取出用户名密码,使用form校验数据,如果校验通过,存到数据库中,如果校验不通过,返回错误信息
        register_form = RegisterForm(data=request.POST)
        if register_form.is_valid():
            # 数据字段自己的规则,局部钩子,全局钩子都校验过后,通过了
            # 1  re_password字段,不存到数据库的,剔除
            register_data = register_form.cleaned_data
            register_data.pop('re_password')
            # 2 头像:如果携带了要存,头像是文件,在request.FILES中
            my_file = request.FILES.get('my_file')
            if my_file:
                register_data['avatar'] = my_file
            # 2 存到数据库
            UserInfo.objects.create_user(**register_data)
            # UserInfo.objects.create_user(username=register_data.get('username'),) 等同于上面,但是麻烦
            return JsonResponse(res)
        else:
            res['code'] = 101
            res['msg'] = '注册失败'
            res['errors'] = register_form.errors
            return JsonResponse(res)

注册功能前端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <script src="/static/jquery-3.3.1/jquery-3.3.1.min.js"></script>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1 class="text-center">注册功能</h1>
            <form id="id_form" >
                {% csrf_token %}

                {% for item in form %}
                    <div class="form-group">
                        <label for="{{ item.id_for_label }}">{{ item.label }}</label>
                        {{ item }}
                        <span class="pull-right text-danger"></span>
                    </div>
                {% endfor %}

                <div class="form-group">
                    <label for="id_file">头像
                        <img src="/static/img/default.png" alt="" height="80px" width="80px" style="margin-left: 10px"
                             id="id_img">
                    </label>
                    <input type="file" id="id_file" accept="image/*" style="display: none">
                </div>
                <div class="form-group text-center">
                    {#                    如果input类型是submit或者button标签,放在form表单中,如果点提交,触发form的提交,如果我们写了ajax提交,会触发两次提交#}
                    <input type="button" value="注册" class="btn btn-danger" id="id_submit">
                    <span class="text-danger"></span>
                </div>
            </form>
        </div>
    </div>
</div>
</body>

<script>
    $("#id_file").change(function () {
        // 把当前图片,放到img标签中
        // 把图片地址放到img标签上,标签就显示了图片
        //$("#id_img")[0].src='https://tva1.sinaimg.cn/large/00831rSTly1gd1u0jw182j30u00u043b.jpg'

        // 1 id_file这个标签的图片读出来,借助于文件阅读器,js提供的一个类
        var reader = new FileReader()
        // 2 拿到文件对象,赋值给一个变量
        var file = $("#id_file")[0].files[0]
        // 3 把文件读到文件阅读器中
        reader.readAsDataURL(file)
        // 4 等读完后,把文件阅读器的内容写到img标签上
        //$("#id_img")[0].src=reader.result
        reader.onload = function () {
            //$("#id_img")[0].src=reader.result
            $('#id_img').attr('src', reader.result)
        }
    })

    // 当点击注册按钮,发送ajax请求到后端的注册功能
    $("#id_submit").click(function () {
        // 上传文件,借助于formdata对象
        var formdata = new FormData()
        // 方式一
    /*formdata.append('username',$('#id_username').val())
        formdata.append('password',$('#id_username').val())
        formdata.append('re_password',$('#id_username').val())
        formdata.append('my_file',$('#id_file')[0].files[0])
        // csrftoken也要加上*/
        // 方式二:借助于form表单批量处理
        var data = $("#id_form").serializeArray()
        console.log(data)
        // 使用for循环,把data中得数据,转存到formdata中  jquery的each循环
        $.each(data, function (i, v) {
            //console.log("索引是:",i)
            //console.log("值是:",v)
            //console.log("------")
            formdata.append(v.name, v.value)
        })
        // 文件单独再放进去
        formdata.append('my_file', $('#id_file')[0].files[0])

        // 使用ajax向后端发送请求
        // 1 三种编码格式:urlencoded,form-data,json
        // {name:lqz,age:19}--->name=lqz&age=19
        $.ajax({
            url: '/register/',
            type: 'post',
            processData: false,
            contentType: false,
            data: formdata,
            success: function (data) {
                console.log(data)
                if(data.code==100){
                    // 表示注册成功,跳转到登录页面
                    location.href='/login/'
                }else {
                    // 在前端显示错误信息
                }
            }
        })
    })
</script>
</html>

头像上传路径问题

# 咱们写项目后台静态资源中得图片,一般放在static文件夹下
# 用户上传的文件,图片等,一般放在media文件夹下
    -我们想做的是,avatar文件夹要在media文件夹下

# django中只需要在配置文件中加一句话
# 以后再上传的文件路径是从media文件夹下开始
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

注册错误信息渲染

$("#id_submit").click(function () {
        // 上传文件,借助于formdata对象
        var formdata = new FormData()
        // 方式一
        /*
        formdata.append('username',$('#id_username').val())
        formdata.append('password',$('#id_username').val())
        formdata.append('re_password',$('#id_username').val())
        formdata.append('my_file',$('#id_file')[0].files[0])
        // csrftoken也要加上
        */
        // 方式二:借助于form表单批量处理
        var data = $("#id_form").serializeArray()
        console.log(data)
        // 使用for循环,把data中得数据,转存到formdata中  jquery的each循环
        $.each(data, function (i, v) {
            //console.log("索引是:",i)
            //console.log("值是:",v)
            //console.log("------")
            formdata.append(v.name, v.value)
        })
        // 文件单独再放进去
        formdata.append('my_file', $('#id_file')[0].files[0])

        // 使用ajax向后端发送请求
        // 1 三种编码格式:urlencoded,form-data,json
        // {name:lqz,age:19}--->name=lqz&age=19
        $.ajax({
            url: '/register/',
            type: 'post',
            processData: false,
            contentType: false,
            data: formdata,
            success: function (data) {
                console.log(data)
                if(data.code==100){
                    // 表示注册成功,跳转到登录页面
                    location.href='/login/'
                }else {
                    // 在前端显示错误信息
                    console.log(data)
                    // 两次密码不一致的错误渲染
                    /*
                    if(data.errors['__all__']){
                        $(".error").html(data.errors['__all__'][0])
                    }*/
                    // 其他标签的错误渲染
                    $.each(data.errors,function (k,v){
                        if (k=='__all__'){ //两次密码不一致的错误渲染
                            $(".error").html(v[0])
                        }else {
                            // 链式调用,在对应的input后的span中插入错误文字,把父div加入has-error类,整个框变红
                            $("#id_"+k).next().html(v[0]).parent().addClass('has-error')
                        }
                    })
                    // 起一个定时任务
                    setTimeout(function (){
                        // 把所有span的文字去掉,把父div中得has-error类去掉
                        $('.text-danger').html("").parent().removeClass('has-error')
                    },3000)
                }
            }
        })
    })

用户存在校验功能

后端校验接口

# 前端通过get把用户名传入,我们根据用户名查询数据库,如果用户名存在,返回存在,如果不存在,返回不存在
/check_username/?username=lqz

def check_username(request):
    # /check_username/?username=lqz
    res = {'code': 100, 'msg': '用户存在'}
    username = request.GET.get('username')
    user = UserInfo.objects.filter(username=username).first()
    if user:
        # 用户存在
        return JsonResponse(res)
    else:
        # 用户不存在
        res['code'] = 101
        res['msg'] = '用户不存在'
        return JsonResponse(res)

前端

// username输入框,失去焦点,触发ajax执行
$('#id_username').blur(function () {

    $.ajax({
        url: '/check_username/?username=' + $(this).val(),
        type: 'get',
        success: function (data) {
            if (data.code == 100) {
                // 在span中插入错误信息
                {#alert(data.msg)#}
                $('#id_username').next().html(data.msg)
            }
        }
    })
})

登录页面搭建

1 创建login.html
2 登录后台,get返回该页面


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <script src="/static/jquery-3.3.1/jquery-3.3.1.min.js"></script>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1 class="text-center">登录功能</h1>
            <form id="id_form">
                {% csrf_token %}
                <div class="form-group">
                    <label for="id_username">用户名</label>
                    <input type="text" id="id_username" name="username" class="form-control">
                </div>
                <div class="form-group">
                    <label for="id_password">密码</label>
                    <input type="password" id="id_password" name="password" class="form-control">
                </div>
                <div class="form-group">
                    <label for="id_code">验证码</label>

                        <div class="row">
                            <div class="col-md-6">
                                <input type="text" id="id_code" name="code" class="form-control">
                            </div>
                            <div class="col-md-6">
                                <img src="/static/img/default.png" alt="" width="500px" height="40px">
                            </div>
                    </div>
                </div>

                <div class="form-group text-center">
                    <input type="button" value="登录" class="btn btn-danger" id="id_submit">
                    <span class="text-danger error"></span>
                </div>
            </form>
        </div>
    </div>
</div>
</body>
</html>

自定义图片验证码

# 验证码:数字,大写字母,小写字母的组合 5位
# 第三方的模块,可以直接生成
# 自己写
    # 方式一,直接返回一张图片
    # 方式二:自己生成一张图片,返回
    # 方式三:把生成的图片写到内存中
    # 方法四:在图片上写文字
    # 方式五:图片上写文字,字体是指定的字体,字的颜色随机
    # 方式6 最终方案

def get_rgb():
    return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))


def get_code(request):
    # 方式一,直接返回一张图片
    # with open('./media/avatar/2.jpeg', 'rb') as f:
    #     data = f.read()
    # return HttpResponse(data)

    # 方式二:自己生成一张图片,返回  --->借助模块,pillow
    # # rgb:三原色 red green blue
    # # 宽度和高度
    # # 三原色的色值  0-255
    # #1 创建图片
    # img=Image.new('RGB',(500,40),(0,255,255))
    # #2 保存到本地
    # with open('code.png','wb') as f:
    #     img.save(f)
    # # 3 打开图片,返回给前端
    # with open('./code.png', 'rb') as f:
    #     data = f.read()
    # return HttpResponse(data)

    # 方式三:把生成的图片写到内存中
    # # 1 创建图片
    # img = Image.new('RGB', (500, 40), (0, 0, 0))
    # # 2 保存到内存
    # byte_io = BytesIO()
    # img.save(byte_io,'png') # 指定图片格式
    # # 3 从BytesIO取出二进制,返回给前端
    # return HttpResponse(byte_io.getvalue())

    # 方法四:在图片上写文字
    # # 1 创建图片
    # img = Image.new('RGB', (500, 40), (0, 255, 0))
    # # 2 在图片上写文字 ,相当于画板
    # img_draw=ImageDraw.Draw(img)
    # img_draw.text((0,0),"lqz")
    # # 2 保存到内存
    # byte_io = BytesIO()
    # img.save(byte_io,'png') # 指定图片格式
    # # 3 从BytesIO取出二进制,返回给前端
    # return HttpResponse(byte_io.getvalue())

    # 方式五:图片上写文字,字体是指定的字体,字的颜色随机
    # img = Image.new('RGB', (500, 40), get_rgb())
    #
    # # 2 创建一个字体对象
    # font=ImageFont.truetype('./static/font/xgdl.ttf',30)
    # # 2 在图片上写文字 ,相当于画板
    # img_draw = ImageDraw.Draw(img)
    # img_draw.text((0, 0), "lqz",font=font)
    # # 2 保存到内存
    # byte_io = BytesIO()
    # img.save(byte_io, 'png')  # 指定图片格式
    # # 3 从BytesIO取出二进制,返回给前端
    # return HttpResponse(byte_io.getvalue())

    # 第六:终极方案自己造 验证码
    # img = Image.new('RGB', (500, 40), get_rgb())
    img = Image.new('RGB', (500, 40), (255,255,255))

    # 2 创建一个字体对象
    font = ImageFont.truetype('./static/font/ss.TTF', 30)
    # 3 在图片上写文字 ,相当于画板
    img_draw = ImageDraw.Draw(img)
    # 4 在图片上写文字(数字,大写字母,小写字母组合  5个)
    code_str = ''
    for i in range(5):
        num = random.randint(0, 9)
        upper = chr(random.randint(65, 90))
        low = chr(random.randint(97, 122))
        ran = str(random.choice([num, upper, low]))
        code_str += ran # python 是强类型语言,不同类型直接不能直接相加,需要类型转换
        img_draw.text((60+i*60, 0), ran, fill=get_rgb(),font=font)
    request.session['code'] = code_str
    #5 在图片上画点画线
    width = 450
    height = 30
    for i in range(10):
        x1 = random.randint(0, width)
        x2 = random.randint(0, width)
        y1 = random.randint(0, height)
        y2 = random.randint(0, height)
        # 在图片上画线
        img_draw.line((x1, y1, x2, y2), fill=get_rgb())

    for i in range(50):
        # 画点
        img_draw.point([random.randint(0, width), random.randint(0, height)], fill=get_rgb())
        x = random.randint(0, width)
        y = random.randint(0, height)
        # 画弧形
        # img_draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_rgb())
    # 2 保存到内存
    byte_io = BytesIO()
    img.save(byte_io, 'png')  # 指定图片格式
    # 3 从BytesIO取出二进制,返回给前端
    return HttpResponse(byte_io.getvalue())

点击图片验证码更新

$('#id_img').click(function (){
    // 把图片的src地址变成原来地址?
    //alert($(this)[0].src)
    let timestamp=new Date().getTime()
    $(this)[0].src='/get_code/?t='+timestamp
    })

登录功能后端

def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        # 前端传入的用户名,密码,验证码,  校验验证码   去数据库比较用户名和密码
        res = {'code': 100, 'msg': '成功'}
        # 1 拿出用户名,密码,验证码,校验验证码
        print(request.POST)
        code = request.POST.get('code')
        username = request.POST.get('username')
        password = request.POST.get('password')
        old_code = request.session.get('code')
        if code.lower() == old_code.lower():  # 不区分大小写
            # 2 校验用户名或密码
            # user=UserInfo.objects.filter(username=username,password=password) # 这个不行
            user = authenticate(username=username, password=password)
            if user:
                # 3 登录成功,返回json格式字符串
                return JsonResponse(res)
            else:
                res['code'] = 101
                res['msg'] = '用户名或密码错误'
                return JsonResponse(res)
        else:
            res['code'] = 102
            res['msg'] = '验证码错误'
            return JsonResponse(res)

登录功能前端

    $("#id_submit").click(function () {
        // 取出填入的数据,发送ajax的post请求
        //$('#id_form').serializeArray()
        $.ajax({
            url:'/login/',
            type:'post',
            data:{
                'username':$('#id_username').val(),
                'password':$('#id_password').val(),
                'code':$('#id_code').val(),
                'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val(),  //根据属性取标签
            },
            success:function (data){
                console.log(data)
                if(data.code==100){
                    location.href='/'
                }else {
                    $('.error').html(data.msg)
                }
            }
        })
    })

首页导航和首页轮播图

新建index.html---》写一个路由---》get请求返回index.html页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <script src="/static/jquery-3.3.1/jquery-3.3.1.min.js"></script>
    <script src="/static/bootstrap/js/bootstrap.min.js"></script>
    <title>首页</title>
</head>

<body>
<div class="mynavbar">
    <nav class="navbar navbar-default">
        <div class="container-fluid">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                        data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="#">博客园</a>
            </div>

            <!-- Collect the nav links, forms, and other content for toggling -->
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav">
                    <li class="active"><a href="#">文章 <span class="sr-only">(current)</span></a></li>
                    <li><a href="#">新闻</a></li>
                </ul>
                <form class="navbar-form navbar-left">
                    <div class="form-group">
                        <input type="text" class="form-control" placeholder="搜索文章">
                    </div>
                    <button type="submit" class="btn btn-default">搜</button>
                </form>
                <ul class="nav navbar-nav navbar-right">
                    <li><a href="#">用户名</a></li>
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                           aria-expanded="false">更多 <span class="caret"></span></a>
                        <ul class="dropdown-menu">
                            <li><a href="#">修改密码</a></li>
                            <li><a href="#">后台管理</a></li>
                            <li><a href="#">修改头像</a></li>
                            <li role="separator" class="divider"></li>
                            <li><a href="#">退出</a></li>
                        </ul>
                    </li>
                </ul>
            </div><!-- /.navbar-collapse -->
        </div><!-- /.container-fluid -->
    </nav>
</div>
<div class=container-fluid">
    <div class="row">
        <div class="col-md-2">
            <div class="panel panel-primary">
                <div class="panel-heading">
                    <h3 class="panel-title">广告招商</h3>
                </div>
                <div class="panel-body">
                    <div>
                        重金求子 <a href="">点我</a>
                    </div>
                    <hr>
                    <div>
                        重金求子 <a href="">点我</a>
                    </div>
                    <hr>
                    <div>
                        重金求子 <a href="">点我</a>
                    </div>
                </div>
            </div>
            <div class="panel panel-warning">
                <div class="panel-heading">
                    <h3 class="panel-title">广告招商</h3>
                </div>
                <div class="panel-body">
                    <div>
                        重金求子 <a href="">点我</a>
                    </div>
                    <hr>
                    <div>
                        重金求子 <a href="">点我</a>
                    </div>
                    <hr>
                    <div>
                        重金求子 <a href="">点我</a>
                    </div>
                </div>
            </div>
        </div>
        <div class="col-md-7">
            <div class="top">
                <div id="carousel-example-generic" class="carousel slide" data-ride="carousel">
                    <ol class="carousel-indicators">
                        <li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li>
                        <li data-target="#carousel-example-generic" data-slide-to="1"></li>
                    </ol>

                    <div class="carousel-inner" role="listbox">
                        <div class="item active">
                            <img src="/static/img/banner1.jpg" alt="...">
                            <div class="carousel-caption">
                                博客开博了
                            </div>
                        </div>
                        <div class="item">
                            <img src="/static/img/banner2.jpg" alt="...">
                            <div class="carousel-caption">
                                欢迎大家投稿
                            </div>
                        </div>
                    </div>

                    <!-- Controls -->
                    <a class="left carousel-control" href="#carousel-example-generic" role="button" data-slide="prev">
                        <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
                        <span class="sr-only">Previous</span>
                    </a>
                    <a class="right carousel-control" href="#carousel-example-generic" role="button" data-slide="next">
                        <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
                        <span class="sr-only">Next</span>
                    </a>
                </div>
            </div>

            <div class="article">
                文章详情
            </div>

        </div>
        <div class="col-md-3">
            <div class="panel panel-primary">
                <div class="panel-heading">
                    <h3 class="panel-title">广告招商</h3>
                </div>
                <div class="panel-body">
                    <div>
                        重金求子 <a href="">点我</a>
                    </div>
                    <hr>
                    <div>
                        重金求子 <a href="">点我</a>
                    </div>
                    <hr>
                    <div>
                        重金求子 <a href="">点我</a>
                    </div>
                </div>
            </div>
            <div class="panel panel-warning">
                <div class="panel-heading">
                    <h3 class="panel-title">广告招商</h3>
                </div>
                <div class="panel-body">
                    <div>
                        重金求子 <a href="">点我</a>
                    </div>
                    <hr>
                    <div>
                        重金求子 <a href="">点我</a>
                    </div>
                    <hr>
                    <div>
                        重金求子 <a href="">点我</a>
                    </div>
                </div>
            </div>
            <div class="panel panel-danger">
                <div class="panel-heading">
                    <h3 class="panel-title">广告招商</h3>
                </div>
                <div class="panel-body">
                    <div>
                        重金求子 <a href="">点我</a>
                    </div>
                    <hr>
                    <div>
                        重金求子 <a href="">点我</a>
                    </div>
                    <hr>
                    <div>
                        重金求子 <a href="">点我</a>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

</body>
</html>

首页文章列表页面

# admin 后台录入数据  文章表录入数据

# admin 是djagno的一个app,它是一个后台管理,内置的,可以方便我们做二次开发和录入数据
-在app的admin.py 把表注册一下 ,登录到后台就可以看到了
-直接录入数据,跟在数据表中录入数据本质一样的

开启media访问

# 上传的图片,目前访问不到,要开启media的访问,才能看到图片
# 开启media访问,本质就是把项目中某个目录可以让客户端(浏览器)直接访问,很危险,咱们只能开启media,以后meida下不能放重要的东西
# 本身static 文件夹从浏览器可以访问,默认这个目录已经开放了

# 如何开启
-1 在配置文件中
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
-2 在路由中
    path('media/<path:path>', serve, {'document_root': settings.MEDIA_ROOT}),

图片防盗链

# 有的网站有上传图片功能,我们可以把图片传上去,然后在自己的网站中使用,这样不会耗费我们自己网站的带宽,比如cnblogs,cnblogs就屏蔽了这种行为,这就是图片防盗链

# 本质原理是:
因为发送http请求,请求头中有个参数叫referer,是一个url地址,它表示上一次访问的地址
图片防盗链可以通过这个来控制,判断一下referer是不是我们自己的地址,如果不是就不返回
通过请求头中得referer来控制不被非自己的地址请求

标签:username,form,img,models,BBS,博客,多人,data,id
From: https://www.cnblogs.com/wwjjll/p/16705576.html

相关文章

  • 博客一
    大家好,很高兴认识大家,我个人平时比较喜欢运动,比如打打篮球跑跑步啥的,如果有同学打篮球缺人的话可以找我,虽然技术不咋滴,但是抢个篮板还是绰绰有余的。还有如果想减肥的,也可......
  • 第一篇博客
    这是我的第一篇博客。我是一名普普通通的大学生,喜欢打游戏,游泳,打篮球。关于游泳这项运动我个人是很喜欢的,因为我比较喜欢水,但是呢,从小我的父母因为在......
  • 我的第一篇博客
    前言:从中学到大学,时光飞逝转眼间就从一个懵懵懂懂的孩子变成一个逐渐成熟的成年人。过去的事情无法改变,我现在要做得是在今后的学习中不断收获,不断成长。......
  • 使用docker-compose创建wordpress博客网站
    1.简述wordpress是一款开源的博客CMS,dockerhub上有着官方的容器镜像,使用docker能够很简单的创建一个wordpress站点,本文简要介绍了如何使用docker-compose来创建。2.......
  • 以太坊完成合并,很多人认为大规模显卡挖矿时代结束,耗电量也将降低。而事实是显卡挖矿并
    当地时间9月15日,以太坊联合创始人VitalikButerin发布推文称,“我们终于完成了(以太坊合并)!”,并表示这是以太坊生态系统的重要时刻。按他们的叙述,因为以太坊合并,让全世界的电......
  • 多人联机之研究
    多人联机之研究原文链接.虽说多人联机技术已经存在很多年,众多上古游戏就已经支持多人联机,但随着业务复杂度提高,多人联机仍然有许多挖掘空间。从业很多年,参与的项目清......
  • 我的第一篇博客——入园博客
    1.自我介绍大家好,我叫周帅,来自湖南益阳,是中南林业科技大学涉外学院软件工程专业的一名大三学生,因大专专业是计算机信息管理,对于软件工程这个专业差不多算是跨专业吧。......
  • SpringBoot校园博客系统 博客管理系统 开源博客系统 个人博客系统Java Vue MySQL数据
    ......
  • 博客园美化
    博客园配置信息1.页面定制CSS代码勾选禁用模版默认css*,.Cal{padding:0}::-moz-selection{background:#807dd4;color:#FFF}::selection{background:#807dd4;color:#......
  • 关于介绍我这个博客园的诞生原因
    主要还是以笔记为主,方便督促我去学习,我也准备去实习,再这里卷一下不过分吧可能会写一些其他方面的技术吧,就是不是Java方向的,我顺带培养一下自己的兴趣爱好吧。还有就是笔......