• BBS项目


    一、表结构关系图

      

     二、建表

    from django.db import models
    
    # Create your models here.
    
    from django.contrib.auth.models import AbstractUser
    
    
    class UserInfo(AbstractUser):
        """
        用户信息表
        """
        phone = models.CharField(max_length=11, null=True, unique=True)  # 手机号
        avatar = models.FileField(upload_to="avatars/", default="avatars/default.png")  # 头像
    
        blog = models.OneToOneField(to="Blog", null=True)
    
        def __str__(self):
            return self.username
    
        class Meta:
            verbose_name = "用户信息"
            verbose_name_plural = verbose_name
    
    
    class Blog(models.Model):
        """
        博客信息
        """
        title = models.CharField(max_length=64)  # 个人博客标题
        theme = models.CharField(max_length=32)  # 博客主题
    
        def __str__(self):
            return self.title
    
        class Meta:
            verbose_name = "博客"
            verbose_name_plural = verbose_name
    
    
    class Category(models.Model):
        """
        个人博客文章分类
        """
        title = models.CharField(max_length=32)  # 分类标题
        blog = models.ForeignKey(to="Blog")  # 外键关联博客,一个博客站点可以有多个分类
    
        def __str__(self):
            return "{}-{}".format(self.blog.title, self.title)
    
        class Meta:
            verbose_name = "文章分类"
            verbose_name_plural = verbose_name
    
    
    class Tag(models.Model):
        """
        标签
        """
        title = models.CharField(max_length=32)  # 标签名
        blog = models.ForeignKey(to="Blog")  # 所属博客
    
        def __str__(self):
            return self.title
    
        class Meta:
            verbose_name = "标签"
            verbose_name_plural = verbose_name
    
    
    class Article(models.Model):
        """
        文章
        """
        title = models.CharField(max_length=50)  # 文章标题
        desc = models.CharField(max_length=255)  # 文章描述
        create_time = models.DateTimeField(auto_now_add=True)  # 创建时间
        category = models.ForeignKey(to="Category", null=True)  # 文章分类
        user = models.ForeignKey(to="UserInfo")  # 作者
        tags = models.ManyToManyField(  # 文章的标签
            to="Tag",
            through="Article2Tag",
            through_fields=("article", "tag"),
        )
    
        def __str__(self):
            return self.title
    
        class Meta:
            verbose_name = "文章"
            verbose_name_plural = verbose_name
    
    
    class ArticleDetail(models.Model):
        """
        文章详情表
        """
        content = models.TextField()  # 文章内容
        article = models.OneToOneField(to="Article")
    
        class Meta:
            verbose_name = "文章详情"
            verbose_name_plural = verbose_name
    
    
    class Article2Tag(models.Model):
        """
        文章和标签的多对多关系表
        """
        article = models.ForeignKey(to="Article")
        tag = models.ForeignKey(to="Tag")
    
        def __str__(self):
            return "{}-{}".format(self.article, self.tag)
    
        class Meta:
            unique_together = (("article", "tag"),)
            verbose_name = "文章-标签"
            verbose_name_plural = verbose_name
    
    
    class ArticleUpDown(models.Model):
        """
        点赞表
        """
        user = models.ForeignKey(to="UserInfo", null=True)
        article = models.ForeignKey(to="Article", null=True)
        is_up = models.BooleanField(default=True)  # 点赞还是踩灭
    
        def __str__(self):
            return "{}-{}".format(self.user_id, self.article_id)
    
        class Meta:
            unique_together = (("article", "user"),)  # 同一个人只能给一篇文章点一次赞
            verbose_name = "点赞"
            verbose_name_plural = verbose_name
    
    
    class Comment(models.Model):
        """
        评论表
        """
        article = models.ForeignKey(to="Article")
        user = models.ForeignKey(to="UserInfo")
        content = models.CharField(max_length=255)  # 评论内容
        create_time = models.DateTimeField(auto_now_add=True)
    
        parent_comment = models.ForeignKey("self", null=True)  # 自己关联自己
    
        def __str__(self):
            return self.content
    
        class Meta:
            verbose_name = "评论"
            verbose_name_plural = verbose_name
    models.py

      1.多对多三种实现方式

      2.联合唯一

    三、登录

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登录</title>
        <link rel="stylesheet" href="/static/css/bootstrap/bootstrap3.3.7-min.css">
        <link rel="stylesheet" href="/static/css/login.css">
    </head>
    <body>
    
    <div class="container col-md-4 col-md-push-3">
        <form class="form-horizontal" novalidate>
            {% csrf_token %}
            <div class="form-group">
                <label class="col-sm-3 control-label" for="inputUser">用户名:</label>
                <div class="col-sm-9">
                    <input type="text" class="form-control" id="inputUser" placeholder="请输入用户名" autofocus="">
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-3 control-label" for="inputPwd">密 码:</label>
                <div class="col-sm-9">
                    <input type="password" class="form-control" id="inputPwd" placeholder="请输入密码">
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-3 control-label" for="inputVcode">验证码:</label>
                <div class="col-sm-4">
                    <input type="text" class="form-control" id="inputVcode" placeholder="请输入验证码">
                </div>
                <div class="col-sm-5" id="divImgVcode">
                    <img src="{% url 'v_code' %}" alt="" id="imgVcode">
                </div>
            </div>
            <div class="form-group">
                <div class="col-sm-offset-3 col-sm-3">
                    <div class="checkbox">
                        <label>
                            <input type="checkbox" id="remember-me"> 记住我
                        </label>
                    </div>
                </div>
            </div>
            <div class="form-group">
                <div class="col-sm-push-4 col-sm-7">
                    <button type="button" class="btn btn-block btn-lg btn-info" id="btnLogin">登录</button>
                    <p id="pErrMsg"></p>
                </div>
            </div>
        </form>
    
    </div>
    
    <script src="/static/js/jquery/jquery3.3.1-min.js"></script>
    <script src="/static/js/setAjax.js"></script>
    <script>
    
        $("#btnLogin").click(function () {
            let username = $("#inputUser").val();
            let password = $("#inputPwd").val();
            let vCode = $('#inputVcode').val();
            $.ajax({
                url:{% url 'login' %},
                type: 'post',
                data: {
                    username: username,
                    password: password,
                    vcode:vCode,
                },
                success: function (res) {
                    if (res.code !== 0) {
                        // 认证失败
                        $("#pErrMsg").text(res.msg)
                    }
                    else {
                        location.href = '{% url 'index' %}'
                    }
                }
            })
        });
    
        $('input').focus(function () {
            $('#pErrMsg').text('')
        });
    
        let orgUrlLenth = $('#imgVcode')[0].src.length;
        $('#imgVcode').click(function () {
            let timeStamp = new Date().valueOf();
            this.src = this.src.substring(0, orgUrlLenth) + timeStamp;
        })
    
    </script>
    </body>
    </html>
    login.html
    from django.shortcuts import render, redirect, HttpResponse
    from django.contrib.auth import authenticate, login, logout
    from django.contrib.auth.decorators import login_required
    from PIL import Image, ImageDraw, ImageFont, ImageFilter
    from django.http import JsonResponse
    from django import views
    from io import BytesIO
    import random
    
    
    # Create your views here.
    
    
    class Login(views.View):
    
        def get(self, request):
            return render(request, 'login.html')
    
        def post(self, request):
            res = {'code': 0}
            username = request.POST.get('username')
            password = request.POST.get('password')
            vcode = request.POST.get('vcode')
            if vcode.upper() != request.session.get('vcode').upper():
                res['code'] = 1
                res['msg'] = '验证码有误'
            else:
                # auth认证
                user = authenticate(request, username=username, password=password)
                if user:
                    # 认证成功,进行登录
                    login(request, user)
                else:
                    res['code'] = 1
                    res['msg'] = '用户名或密码有误'
            return JsonResponse(res)
    
    
    def v_code(request):
        # 随机颜色,相对较浅
        def random_color1():
            return random.randint(64, 255), random.randint(64, 255), random.randint(64, 255)
    
        # 随机颜色,相对较深
        def random_color2():
            return random.randint(32, 127), random.randint(32, 127), random.randint(32, 127)
    
        # 等概率随机生成 n 个数字、字母(大小写)组合的验证码列表
        def random_code(n):
            rec_list = []
            while n:
                n -= 1
                rdm_first = random.randint(0, 1)
                if rdm_first == 0:
                    rec_list.append(str(random.randint(0, 9)))
                else:
                    rdm_second = random.randint(0, 1)
                    if rdm_second == 0:
                        rec_list.append(chr(random.randint(65, 90)))
                    else:
                        rec_list.append(chr(random.randint(97, 122)))
            return rec_list
    
        vcode_list = random_code(4)
    
        # 由最终验证码组成的字符串, 存入session中用来校验
        vcode = ''.join(vcode_list)
        request.session['vcode'] = vcode
    
        # 生成一张图片
        img_obj = Image.new(
            'RGB',  # 生成图片的模式
            (162, 33),  # 大小
            random_color1()  # 颜色
        )
    
        # 生成一个画笔,指定画板为img_obj
        draw_obj = ImageDraw.Draw(img_obj)
    
        # 加载本地字体文件,生成字体对象
        font_obj = ImageFont.truetype('static/font/kumo.ttf', size=28)
    
        # 依次在画板上写上n个验证码
        for i in range(len(vcode_list)):
            draw_obj.text(
                (i * 32 + 20, 0),  # 坐标
                vcode_list[i],  # 内容
                fill=random_color2(),  # 画笔颜色,即字体颜色
                font=font_obj,  # 字体对象
            )
    
        # # 加干扰线
        # width = 250  # 图片宽度(防止越界)
        # height = 35
        # for i in range(5):
        #     x1 = random.randint(0, width)
        #     x2 = random.randint(0, width)
        #     y1 = random.randint(0, height)
        #     y2 = random.randint(0, height)
        #     draw_obj.line((x1, y1, x2, y2), fill=random_color())
        #
        # # 加干扰点
        # for i in range(40):
        #     draw_obj.point([random.randint(0, width), random.randint(0, height)], fill=random_color())
        #     x = random.randint(0, width)
        #     y = random.randint(0, height)
        #     draw_obj.arc((x, y, x+4, y+4), 0, 90, fill=random_color())
    
        # # 进行模糊处理
        # img_obj = img_obj.filter(ImageFilter.BLUR)
    
        iof = BytesIO()  # 生成IO句柄
        img_obj.save(iof, 'png')  # 把图片对象放进IO句柄里,即保存到内存中
        data = iof.getvalue()  # 从内存中读取图片数据
    
        # 从服务器静态文件夹内读取图片数据
        # with open('static/img/oo.png', 'rb') as f:
        #     data = f.read()
    
        return HttpResponse(data, content_type='image/png')
    
    
    @login_required
    def index(request):
        user_pro = request.user
        print(user_pro.userinfo.address)
        return HttpResponse('Index')
    
    
    def log_out(request):
        logout(request)
        return redirect('/login/')
    views.py
    from django.contrib import admin
    from django.urls import path, re_path
    from Authapp import views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('login/', views.Login.as_view(), name='login'),
        path('index/', views.index, name='index'),
        path('logout/', views.log_out, name='logout'),
        re_path('v-code/d*', views.v_code, name='v_code'),
    ]
    urls.py
    STATICFILES_DIRS = [
        os.path.join(BASE_DIR, 'static')
    ]
    
    LOGIN_URL = '/login/' 
    
    AUTH_PROFILE_MODULE = 'Authapp.UserInfo'
    settings.py

      注意:

      1. ajax,auth,bootstrap(组长教的)       

        python基础 random.randint()  字母65-90、97-122  chr(数字)  颜色(0~255)三个一组(0,0,0)

      2. 制作验证码

        1.pillow     import PIL

          Image.new()  img对象.filter(ImageFilter.BLUR)        

          ImageFont.truetype()

          ImageDraw.Draw(img对象)  draw对象.text()  draw对象.line()  draw对象.arc()

          点击切换验证码实现方式:

            1.一直加?

            2.加时间戳

            3.加?删?交替      import  django.views.decorators.cache  import  never_cache (前段禁用缓存, 火狐浏览器有bug)    

        2.BytesIO

          BytesIO()

          img对象.save(IO对象,‘png等格式’)

          IO对象.getvalue()

        3.HttpResponse  可以为 src 提供资源

          HttpResponse(data,content_type='image/png') 返回一张图片

      3.前段JS   focus     blur       jQuery对象$(“ #id ”)[ 0 ] 对象DOM       length       substring()

        Date().valueOf()    p标签 .text(" ")  location.href="url"  col-sm-offset-4 (push-pull)

    三、注册

         1. 上传头像,前端

        上传文件的input 标签

        <input type="file" accept="image/*" id="inpAvatar">

      2. 上传头像后台(views视图函数)

        接收文件:

        avatar_file = request.FILES.get("avatar")

        保存注册信息到数据库:

        models.UserInfo.objects.create_user(**form_obj.cleaned_data, avatar=avatar_file)

        (因为是File文件,录入数据库时,会按字段保存路径,按路径找目录保存文件)

      3. 上传头像后台(models模型层)

        UserInfo表中:

          avatar是一个FileField 字段,FileField(upload_to="avatars/",)

          avatar具体保存的是一个路径,而不是一个文件

          具体保存文件的是:这个路径对应的目录(配置了media就另当别论)

          (配置midia后)具体保存文件的目录是:在上传路径前面拼接上media后的新路径对应的目录

      3.  关于配置media(不配置就是上面的那样)

        关于static,通过settings设置别名,即让别名代表其绝对路径。放置可提供的静态文件。

          不能是所有文件都是可以访问的,涉及安全、泄密等问题。像用户上传的东西要有

          指定的目录存放。(部署时要做区分)(MEDIA_URL,MEDIA_ROOT是Django提供,也就是说别的就不认识了)

          (这样用户上传的文件是存放在media/ 下了,但是用户数据库中相关字段存的还是原来的路径,所以用时要拼接)

        1.settings.py

          MEDIA_URL = '/media/'    (# 别名)

          MEDIA_ROOT = os.path.join(BASE_DIR, 'media')     (只有一个目录,不像static可以添加多个)

            (给用户上传的所有文件(FileField、request.FILES)指定一个存放目录)

          TEMPLATES 中添加上下文环境(可选配置)(自动把 MEDIA_URL 注册到前端的模板中)

            'django.template.context_processors.media',  (这样前端拼接时候,可以做活路径)

        2.urls.py

          from django.views.static import serve

          re_path('^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT})

    四、主页面、个人主页、分类标签归档页

       0.关于路由urls :各种路由都是开发者设计的。

       1.评论点赞,bootstrap小图标 

    <span class="glyphicon glyphicon-comment">评论({{ article.comment_count }})</span>
    <span class="glyphicon glyphicon-thumbs-up">点赞({{ article.up_count }})</span>

       2.模板中,时间格式化  

          模板语言,过滤器

          <span>发布于&nbsp;{{ article.create_time|date:'Y-m-d H:i:s' }}</span>

       3.二级路由 (名称空间)

        项目urls: from  django.urls  import  include      from  app01  import  urls  as  app01_urls  (别名)

        app01中urls:  from django.urls import path     from  app01  import  views

               urlpatterns = [ 各种后续path ]

      4.orm 插入原生sql语句

         1.一半一半  (利用extra函数在执行orm查询的同时,额外执行一段sql语句,子查询)

        

           2. 对象连接数据库,光标对象执行原生sql

               from  django.db  import  connection

               cusor = connection.cursor()     # 连接数据库,获得一个游标对象

               cursor.execute(" sql语句 ")  # 游标处:输入sql语句

               cursor.fetchall()  # 取查询结果

      5. 注释(分组)、聚合  

         详见 :

         https://www.cnblogs.com/jeavy/p/10926676.html

           QuerySet.aggregate()    -->  Django的aggregate()方法作用是对一列值 ( 比如字段 ) 进行统计计算,并以字典(Dict)格式返回统计计算结果。

                      是QuerySet 的一个终止子句(也就是返回的不再是一个QuerySet集合的时候)

                     它返回一个字典。键的名称是标识符,值是计算出来的聚合值。

                     键是 "字段名__聚合函数名" 自动生成出来的。可以向聚合子句提供一个名。

                       Hobby.objects.annotate(age__max=Max(student__age))

         QuerySet.annotate()    -->   加注释。加注释意味着要加字段(modle)、加列(表)。

                   如果你想要对数据集先进行筛选分组(例如 filter)后再进行某些复杂的(聚合操作或排序)时,需要使用.annotate方法来注释。

                   给前一个紧挨着的结果集,加注释、加列(存复杂的聚合操作结果)。

                  Course.objects.filter(name__startswith='d').annotate(student_num=Count('student')).order_by('-student_num')[:5]

      6. 多条视图路由整合一条    

            路由中正则表达式(无名分组、有名分组不可混合使用)(位置传参必须要在关键字传参前面)

            re_path(r"(d+)/mid/(w+)",....) 写了几个分组就得匹配几个参数,没有就算匹配不到

    五、文章详情页

      1.模板继承   {%  extends  "xx.html"  %}

      2.inclusion_tag  

        1. 返回一段HTML代码,用数据填充的

        2. 用法:

          1. 在app下面创建一个名为 templatetags 的Python Package

          2. 写个例如“mytags.py”:

            from  django  import  template

            register = template.Library()

            @register.inclusion_tag(file="xx.html")

            def  show_menu(*args):

                ...            (此处很多查询操作)

                return  {"k1" : "v1"}

                (xx.html 中使用 k1这个变量)

            (用这个自定义标签只是为了少书写查询语句的代码,并没有减少查询次数)

      3.点赞

        1.orm事务操作

          from django.db import  transaction

          with  transaction.atomic():

            sql1

            sql2

        2.模板语言

          1.{{ xx }}表示的是单值,不在是变量了,

            所以在js中用到这样的值了,要用引号 "{{ request.user }}"

          2.把 js 代码写入js文件中在导入的时候,因为render渲染时不会去渲染引入的js,所以要处理

            (补) 

      4.QuerySet 惰性求值        

         1.len()  及 count()        

         2,.修改单个属性,两个方法的区别     

         3.debug-tool-bar

      5.阶段问题transaction.atomic()    {% extends 'xx.html' %}    {% load  mytags %}{% func 'xx.html' %}

         分组、聚合  F、Q  js中正则包裹 /RegExp /  RegExp.test(str)  RegExp.exec(str)

          

     六、文章评论

    五、代码集

    def get_vcode(request):
        # 随机颜色,相对较浅
        def random_color1():
            return random.randint(64, 255), random.randint(64, 255), random.randint(64, 255)
    
        # 随机颜色,相对较深
        def random_color2():
            return random.randint(32, 127), random.randint(32, 127), random.randint(32, 127)
    
        # 等概率随机生成 n 个数字、字母(大小写)组合的验证码列表
        def random_code(n):
            rec_list = []
            while n:
                n -= 1
                rdm_first = random.randint(0, 1)
                if rdm_first == 0:
                    rec_list.append(str(random.randint(0, 9)))
                else:
                    rdm_second = random.randint(0, 1)
                    if rdm_second == 0:
                        rec_list.append(chr(random.randint(65, 90)))
                    else:
                        rec_list.append(chr(random.randint(97, 122)))
            return rec_list
    
        vcode_list = random_code(4)
    
        # 由最终验证码组成的字符串, 存入session中用来校验
        vcode = ''.join(vcode_list)
        request.session['vcode'] = vcode
    
        # 生成一张图片
        img_obj = Image.new(
            'RGB',  # 生成图片的模式
            (162, 33),  # 大小
            random_color1()  # 颜色
        )
    
        # 生成一个画笔,指定画板为img_obj
        draw_obj = ImageDraw.Draw(img_obj)
    
        # 加载本地字体文件,生成字体对象
        font_obj = ImageFont.truetype('static/font/kumo.ttf', size=28)
    
        # 依次在画板上写上n个验证码
        for i in range(len(vcode_list)):
            draw_obj.text(
                (i * 32 + 20, 0),  # 坐标
                vcode_list[i],  # 内容
                fill=random_color2(),  # 画笔颜色,即字体颜色
                font=font_obj,  # 字体对象
            )
    
        # # 加干扰线
        # width = 250  # 图片宽度(防止越界)
        # height = 35
        # for i in range(5):
        #     x1 = random.randint(0, width)
        #     x2 = random.randint(0, width)
        #     y1 = random.randint(0, height)
        #     y2 = random.randint(0, height)
        #     draw_obj.line((x1, y1, x2, y2), fill=random_color())
        #
        # # 加干扰点
        # for i in range(40):
        #     draw_obj.point([random.randint(0, width), random.randint(0, height)], fill=random_color())
        #     x = random.randint(0, width)
        #     y = random.randint(0, height)
        #     draw_obj.arc((x, y, x+4, y+4), 0, 90, fill=random_color())
    
        # # 进行模糊处理
        # img_obj = img_obj.filter(ImageFilter.BLUR)
    
        iof = BytesIO()  # 生成IO句柄
        img_obj.save(iof, 'png')  # 把图片对象放进IO句柄里,即保存到内存中
        data = iof.getvalue()  # 从内存中读取图片数据
    
        # 从服务器静态文件夹内读取图片数据
        # with open('static/img/oo.png', 'rb') as f:
        #     data = f.read()
    
        return HttpResponse(data, content_type='image/png')
    生成随机验证码

      

    ok

    https://www.cnblogs.com/jeavy/p/10926676.html

  • 相关阅读:
    【CSS】盒子模型的计算
    【CSS】定义元素的位置
    【Jenkins】安装插件
    安装【Jenkins】
    Python【unittest】模块
    Python【pyyaml】模块
    CentOS下安装gcc和gdb
    汇编学习笔记(15)综合研究
    汇编学习笔记(14)BIOS对键盘输入的处理
    汇编学习笔记(13)直接定址表
  • 原文地址:https://www.cnblogs.com/kingon/p/9466424.html
Copyright © 2020-2023  润新知