首页 > 其他分享 >django项目(博客二)

django项目(博客二)

时间:2023-09-14 17:24:20浏览次数:39  
标签:obj 项目 request 博客 django html user article id

扩展1:admin路由分发的本质

路由分发本质 include 可以无限制的 嵌套N多层

url(r'^index/',([],None,None))

扩展2:

由于url方法第一个参数是正则表达式,所有当路由特别多的时候,可能会出现被顶替的情况,针对这种情况有两种解决方式

建好路由,先和视图函数继续试验一下,避免路由被顶替

  1.修改正则表达式

  2.调整url方法的位置,越往上,url的优先级越高

扩展3:

  时区问题报错,setting.py文件里设置,修改时区

TIME_ZONE = 'Asia/Shanghai'
USE_TZ = False

一 文章详情页

  个人站点页面和文章详细页面,两个的导航页和左侧边栏相同,就建一个共同的base.html页面,两个页面继承base.html页面,

两个页面的左侧边栏从视图函数要的数据是样的,就自定义inclusion_tag,自定义inclusion_tag的工作就是建mytags.py,

并建left_menu.html文件。base.html的左侧栏,调用mytats.py,再调用left_menu函数,并传username参数。

base.html的左侧面建好,个人站点页面和文章详情页面继承base.html,各自建内容区,需要视图函数的数据,就再各自的视图函数要。

 
'''
文章详情页和个人站点基本一致,所以用模版继承
个人站点和文章详情的导航条和左侧边栏一样,这该两个html页面就继承base.html,个人站点页面和文章详情页面的内容区,写自己的内容,
个人站点页面和文章详情页面的左侧栏页面,需要向视图函数要同样的数据,解决代码冗余,就用到自定义inclusion_tags


侧边栏的渲染需要传输数据才能渲染,并且该侧边栏在很多页面都需要使用
1. 在那个地方用就拷贝需要的代码(不推荐 有点繁琐)
2. 将侧边栏制作成inclusion_tag,作用就是,不同的页面,需要共同的视图函数数据,就自定义inclusion_tag,不同的页面,都向inclusion要数据,解决代码冗余的问题
'''

url.py

# url设计
https://www.cnblogs.com/xiaoyuanqujing/articles/12208376.html

/username/article/1

# 先验证url是否被其他url顶替,排查BUG
# 文章详情页
url(r'^(?P<username>\w+)/article/(?P<article_id>\d+)/', views.article_detail)

views.py

# 文章详情页视图函数
def article_detail(request, username, article_id):
    '''
    可以先进行用户名校验是否存在
    :param request:
    :param username:
    :param article_id:
    :return:
    '''
    # 筛选出文章对象,显示对应的用户访问对应的文章,文章查个人站点,正向,个人查用户,反向,跨表用__
    article_obj = models.Article.objects.filter(id=article_id, blog__userinfo__username=username).first()
    if not article_obj:
        # return redirect('/errors/')   # 从定向,需要走url路由,没设置路由,走不通
        return render(request, 'errors.html')
    # 导航条需要个人站点名,就从数据库取给前端
    user_obj = models.UserInfo.objects.filter(username=username).first()
    blog = user_obj.blog
    # 获取当前文章所有的评论内容
    comment_list = models.Comment.objects.filter(article=article_obj)
    return render(request, 'article_detail.html', locals())

base.html

个人站点和文章详情页都从base中模版继承,左侧边栏需要向不同 的视图函数要同样的数据,

用到自定义inclusion_tag

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="referrer" content="no-referrer" />
    <title>{{ blog }}的个人站点</title>
    <link rel="stylesheet" href="/static/bootstrap-3.4.1-dist/css/bootstrap.min.css">
    <script src="/static/js/jQuery-3.6.1.js"></script>
    <script src="/static/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script>
    <script src="/static/js/my_setup.js"></script>
    {% block css %}

    {% endblock %}
</head>
<body>
<!--个人站点导航条-->
<div class="container-fluid">
    <nav class="navbar navbar-default">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <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="#">{{ blog.site_title }}</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="#">Link <span class="sr-only">(current)</span></a></li>
                    <li><a href="#">test</a></li>
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                           aria-expanded="false">Dropdown <span class="caret"></span></a>
                        <ul class="dropdown-menu">
                            <li><a href="#">Action</a></li>
                            <li><a href="#">Another action</a></li>
                            <li><a href="#">Something else here</a></li>
                            <li role="separator" class="divider"></li>
                            <li><a href="#">Separated link</a></li>
                            <li role="separator" class="divider"></li>
                            <li><a href="#">One more separated link</a></li>
                        </ul>
                    </li>
                </ul>
                <form class="navbar-form navbar-left">
                    <div class="form-group">
                        <input type="text" class="form-control" placeholder="Search">
                    </div>
                    <button type="submit" class="btn btn-default">Submit</button>
                </form>
                <ul class="nav navbar-nav navbar-right">
                    {% if request.user.is_authenticated %}
                        <li><a href="#">{{ request.user.username }}</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="#" data-toggle="modal" data-target=".bs-example-modal-lg">修改密码</a></li>
                                <li><a href="#">修改头像</a></li>
                                <li><a href="#">后台管理</a></li>
                                <li role="separator" class="divider"></li>
                                <li><a href="{% url 'logout' %}" id="logout">退出登录</a></li>
                            </ul>
                            <!-- Large modal -->
                            <div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog"
                                 aria-labelledby="myLargeModalLabel">
                                <div class="modal-dialog modal-lg" role="document">
                                    <div class="modal-content">
                                        <h3 class="text-center">修改密码</h3>
                                        <div class="row">
                                            <div class="col-md-8 col-md-offset-2">
                                                <div class="form-group">
                                                    <label for="">用户名</label>
                                                    <input type="text" disabled value="{{ request.user.username }}"
                                                           class="form-control">
                                                </div>
                                                <div class="form-group">
                                                    <label for="old_password">原密码</label>
                                                    <input type="password" id="old_password" class="form-control">
                                                    <span style="color: red" class="pull-right"></span>
                                                </div>
                                                <div class="form-group">
                                                    <label for="">新密码</label>
                                                    <input type="password" id="new_password" class="form-control">
                                                </div>
                                                <div class="form-group">
                                                    <label for="">确认密码</label>
                                                    <input type="password" id="confirm_password" class="form-control">
                                                    <span style="color: red" class="pull-right"></span>
                                                </div>
                                                <div class="form-group">
                                                    <button type="button" class="btn btn-default btn-sm"
                                                            data-dismiss="modal">
                                                        取消
                                                    </button>
                                                    <button type="button" class="btn btn-primary btn-sm"
                                                            id="set_commit">
                                                        确认修改
                                                    </button>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </li>
                    {% else %}
                        <li><a href="{% url 'reg' %}">注册</a></li>
                        <li><a href="{% url 'log' %}">登录</a></li>
                    {% endif %}
                </ul>
            </div><!-- /.navbar-collapse -->
        </div><!-- /.container-fluid -->
    </nav>
</div>
<!--主板 3,9比例-->
<div class="container-fluid">
    <div class="row">
        <div class="col-md-3">
            <!--侧边栏调用mytags.py-->
            {% load mytags %}
            <!--调用mytags里面的left_menu函数-->
            {% left_menu username %}
        </div>
        <!--主板内容-->
        <div class="col-md-9">
            {% block content %}
            
            {% endblock %}
        </div>
    </div>
</div>

{% block js %}

{% endblock %}
</body>
</html>

mytags.py

在应用aap01文件下,建mytags.py

'''
步骤:
    1.在应用下创建一个名字必须叫templatetags文件夹
    2.在该文件夹内创建一个任意名称的py文件
    3.在该py文件内先固定写两行代码
        from django import template
        register = template.Library()
        自定义过滤器
        自定义标签
        自定义inclusion_tag
'''
from django import template
register = template.Library()

# 自定义inclusion_tag
@register.inclusion_tag('left_menu.html')
def left_menu(username):
    # 构造侧边栏需要的数据
    from app01 import models
    from django.db.models import Count
    # 先从site视图函数,剪切过来 ,需要什么参数,就给什么参数
    user_obj = models.UserInfo.objects.filter(username=username).first()
    blog = user_obj.blog
    # 1 查询当前用户所有的分类及分类下的文章数
    category_queryset = models.Category.objects.filter(blog=blog).annotate(
        count_num=Count('article__pk')).values_list('name', 'count_num', 'pk')  # pk是分组后新的虚拟结果的主键值,也就是queryset的索引值
    # values是列表套字典,values_list是列表套元祖
    # 2 查询当前用户所有的标签及标签下的文章数
    tag_queryset = models.Tag.objects.filter(blog=blog).annotate(
        count_num=Count('article__pk')).values_list('name', 'count_num', 'pk')
    # 3 按照年月统计所有的文章
    from django.db.models.functions import TruncMonth
    # 第一个.annotate是按文章进行分组,第二个.annotate是跟在values('month'),month的虚拟表基础上在进行分组,计数取值
    article_month = models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values(
        'month').annotate(
        num=Count('pk')
    ).values_list('month', 'num')
    # print(article_month)
    return locals()

left_menu.html

自定义inclusio_tag中的左侧边栏页面

div class="panel panel-info">
    <div class="panel-heading text-center">文章分类</div>
    <div class="panel-body">
        {% for category in category_queryset %}
            <p><a href="/{{ username }}/category/{{ category.2 }}">{{ category.0 }}({{ category.1 }})</a></p>
        {% endfor %}
    </div>
</div>
<div class="panel panel-danger">
    <div class="panel-heading text-center">文章标签</div>
    <div class="panel-body">
        {% for tag in tag_queryset %}
            <a href="/{{ username }}/tag/{{ tag.2 }}"><p>{{ tag.0 }}<{{ tag.1 }}></p></a>
        {% endfor %}
    </div>
</div>
<div class="panel panel-success">
    <div class="panel-heading text-center">日期归档</div>
    <div class="panel-body">
        {% for month in article_month %}
            <p><a href="/{{ username }}/archive/{{ month.0|date:'Y-m' }}">{{ month.0|date:'Y年m月' }}({{ month.1 }})</a>
            </p>
        {% endfor %}
    </div>
</div>

article_detail.html

文章详情页

{% extends 'base.html' %}

{% block content %}
    <div class="container">
        <h1>{{ article_obj.title }}</h1>
        <div>
            {{ article_obj.content }}
        </div>
    </div>


{% endblock %}

site.html

通过模版继承后的个人站点页面

{% extends 'base.html' %}


{% block content %}
   <ul class="media-list">
                {% for article_obj in article_list %}
                    <li class="media">
                        <h3 class="media-heading"><a href="/{{ username }}/article/{{ article_obj.pk }}">{{ article_obj.title }}</a></h3>
                        <div class="media-left">
                            <a href="#">
                                <img class="media-object" src="/media/{{ article_obj.blog.userinfo.avatar }}" alt="..."
                                     width="50px"
                                     height="60px">
                            </a>
                        </div>
                        <div class="media-body">
                            {{ article_obj.desc }}
                        </div>

                        <div class="pull-right">
                            <span>posted</span>
                            <span>@</span>
                            <span>{{ article_obj.create_time|date:'Y-m-d' }}&nbsp;&nbsp;</span>
                            <span><a href="#">{{ username }}</a>&nbsp;&nbsp;</span>
                            <span><span class="glyphicon glyphicon-comment"></span>评论数({{ article_obj.comment_num }})&nbsp;&nbsp;</span>
                            <span><span class="glyphicon glyphicon-thumbs-up"></span>点赞数({{ article_obj.up_num }})&nbsp;&nbsp;</span>
                            <span><a href="#">编辑</a></span>
                        </div>
                    </li>
                    <hr> <!--分割线-->
                {% endfor %}
            </ul>
{% endblock %}

二 文章点赞点踩

文章点赞点踩功能是在,文章详情页面

'''
浏览器上你看到的花里胡哨的页面,内部都是HTML(前端)代码
在文章内容应该写什么?   --->html代码

如何拷贝文章
    copy outerhtml
解决加载不了图片,用到图片防盗链技术   
<meta name="referrer" content="no-referrer" />

1.拷贝文章
2.拷贝点赞点踩
    1) 拷贝前端点赞点踩图片,只拷贝html
    2) css也要拷贝
        由于有图片防盗链的问题,所以将图片直接下载到本地
        
课下思考:
    前端如何区分用户是点了赞还是点了踩
    1.给标签各自绑定一个事件
        两个标签对应的代码其实基本一样,仅仅是否点赞点踩这一个参数不一样而已
    2.二合一
        给两个标签绑定一个事件
        // 给所有的action类绑定事件
        $('.action').click(function () {
            alert($(this).hasClass('diggit'))
        })
        
3.书写ajax代码朝后端提交数
由于点赞点踩内部有一定的业务逻辑,所以后端单独开设视图函数
建议在写项目的时候吗,先把业务逻辑写完,在进行修正

4.后端逻辑书写完毕之后,前端针对点赞点踩动作实现需要动态展示提示信息
    1)前端点赞点踩数字自增1 需要注意数据类型的问题
    2)用户没有登录 需要展示没有登录提示  并且登录可以点击跳转
    html()
    |safe
    mark_safe()    后端向前端发送html代码
'''


'''
后端逻辑(先写成功逻辑,再写错误逻辑
1.先判断用户是否登录
    request.user.authenticated()
2.再判断当前文字是否是当前用户自己写的
     通过文章主键值获取文章对象
     之后利用orm查询获取文章对象对应的用户对象与request.user比对
3.判断当前用户是否已经给当前文章点了
    利用article_obj文章对象和request.user用户对象去点赞点踩表中筛选数据如果
有数据则点没哟
4.操作数据库,需要注意要同时操作两张表
前端发送过来的是否点赞是一个字符串 需要你自己转成布尔值或者用字符串判断
'''
'''
总结:在书写较为复杂的业务逻辑的时候,可以先按照一条线写下去
之后再去弥补其他线路情况 类似于先走树的主干  之后再分散
'''

urls.py

# 点赞点踩
    url(r'^up_or_down/', views.up_or_down),

views.py

# 点赞点踩视图函数
def up_or_down(request):
    '''
    1.校验用户是否登录
    2.判断当前文章是否是当前用户自己写的(自己不能点自己的文章)
    3.当前用户是否已经给当前文章点过了
    4.操作数据库
    :param request:
    :return:
    '''
    if request.is_ajax():
        back_dic = {'code': 1000, 'msg': ''}
        # 1.先判断用户是否登录
        if request.user.is_authenticated():
            article_id = request.POST.get('article_id')
            print(article_id, type(article_id))
            is_up = request.POST.get('is_up')
            import json
            # 前端发送过来的,是json格式的,需要转成python格式的bool值
            is_up = json.loads(is_up)
            print(is_up, type(is_up))
            # 2.判断当前文章是否是当前用户自己写的 根据文章id查询文章对象,根据文章对象查作者 跟request.user.username比对
            article_obj = models.Article.objects.filter(pk=article_id).first()
            print(article_obj.blog.userinfo.username, type(article_obj.blog.userinfo.username))
            print(request.user, type(request.user))
            if not article_obj.blog.userinfo.username == request.user.username:
                # 3. 校验当前用户是否已经点了    那个地方记录了用户到底点没点
                is_click = models.UpAndDown.objects.filter(user=request.user, article=article_obj)
                if not is_click:
                    # 4 操作数据库 记录数据    要同步操作普通字段
                    # 判断当前用户点了赞还是踩 从而决定给那个字段加一
                    from django.db.models import F  # F新的虚拟字段和原有字段进行比对
                    if is_up:
                        # 给点赞数加一
                        models.Article.objects.filter(pk=article_id).update(up_num=F('up_num') + 1)
                        back_dic['msg'] = '点赞成功'
                    else:
                        # 给点踩数加一
                        models.Article.objects.filter(pk=article_id).update(down_num=F('down_num') + 1)
                        back_dic['msg'] = '点踩成功'
                    # 操作点赞点踩表,记录数据
                    models.UpAndDown.objects.create(user=request.user, article=article_obj, is_up=is_up)
                else:
                    back_dic['code'] = 2000
                    back_dic['msg'] = '你已点过了,不能在点'
            else:
                back_dic['code'] = 3000
                back_dic['msg'] = '不能自己点自己'
        else:
            back_dic['code'] = 4000
            back_dic['msg'] = '需<a href="/login/">登录</a>了,才能点击'
        return JsonResponse(back_dic)

article_detail.html

{% extends 'base.html' %}

{% block css %}

    <style>
        /* 点赞点踩样式 */
        #div_digg {
            float: right;
            margin-bottom: 10px;
            margin-right: 30px;
            font-size: 12px;
            width: 128px;
            text-align: center;
            margin-top: 10px;
        }

        .diggit {
            float: left;
            width: 46px;
            height: 52px;
            background: url('/static/img/upup.gif') no-repeat;
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .buryit {
            float: right;
            margin-left: 20px;
            width: 46px;
            height: 52px;
            background: url('/static/img/downdown.gif') no-repeat;
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .clear {
            /*清除浮动*/
            clear: both;
        }
    </style>
{% endblock %}

{% block content %}

    <h1 class="text-center">{{ article_obj.title }}</h1>

    <div class="article_content" style="overflow: hidden">
        {{ article_obj.content|safe }}
    </div>
    <!--点赞点踩开始-->
    <div class="clearfix">
        <div id="div_digg">
            <div class="diggit action">
                <span class="diggnum" id="digg_count">{{ article_obj.up_num }}</span>
            </div>
            <div class="buryit action">
                <span class="burynum" id="bury_count">{{ article_obj.down_num }}</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips" style="color: red;"></div>
        </div>
    </div>
    <!--点赞点踩结束-->

    <!--根评论开始-->
    <!--评论楼开始-->
    <ul class="list-group" id="id_floor">
        {% for comment in comment_list %}
            <span>#{{ forloop.counter }}楼</span>
            <span>{{ comment.content_time|date:'Y-m-d h:i:s' }}</span>
            <span>{{ comment.user.username }}</span>

            <li class="list-group-item">
                <div>
                    {% if comment.parent_id %}
                        <!--字段parent是自关联字段,跨表还是跨的自己,在再通过外键user跨表查根评论用户-->
                        <p>@{{ comment.parent.user.username }}</p>
                    {% endif %}
                    <span>{{ comment.content }}</span>
                    <!--a标签自定义属性-->
                    <span class="pull-right"><a class="reply" username="{{ comment.user.username }}" comment_id="{{ comment.pk }}">回复</a></span>
                </div>

            </li>
        {% endfor %}
    </ul>
    <!--评论楼结束-->

    <!--前端判断是否登录-->
    {% if request.user.is_authenticated %}
        <div>
            <p><span class="glyphicon glyphicon-comment"></span>发表评论</p>
            <div>
                <textarea name="comment" id="id_comment" cols="60" rows="10"></textarea>
            </div>
            <input type="button" class="btn btn-success" id="id_submit" value="提交评论">
            <span style="color: red" id="id_error"></span>

        </div>
    {% else %}
        <ul class="nav navbar-nav navbar-link">
            <li><a href="{% url 'reg' %}">注册</a></li>
            <li><a href="{% url 'log' %}">登录</a></li>
        </ul>
    {% endif %}

    <!--根评论结束-->
{% endblock %}

{% block js %}
    <script>
        $(function () {
            // ajax向后端发送点赞点踩数
            $('.action').click(function () {
                // alert($(this).hasClass('diggit'))
                // isUp是bool值
                let isUp = $(this).hasClass('diggit');
                // 将点击过的div存起来
                let cilckDiv = $(this);
                // 朝后端发送ajax请求
                $.ajax({
                    url: '/up_or_down/',
                    type: 'post',
                    data: {
                        'article_id': '{{ article_obj.pk }}',
                        'is_up': isUp,
                    },
                    success: function (args) {
                        if (args.code == 1000) {
                            // 取得原来的数,并存给变量Num
                            let Num = cilckDiv.children().text();
                            // 点击过,该数加1,并修改过来,记得将数转成数字形,不然就是字符串拼接
                            cilckDiv.children().text(Number(Num) + 1);
                            $('#digg_tips').text(args.msg)
                        } else {
                            $('#digg_tips').html(args.msg)
                        }
                    }
                })
            });
            // ajax向后端发送评论
            // 全局设置一个parentId
                let parentId = null;
                console.log(parentId)
            $('#id_submit').click(function () {
                let dataComment = $('#id_comment').val();
                // 判断当前评论是否是子评论,如果是,需要将我们之前手动渲染的@username去除
                if (parentId){
                    // 找到\n对应的索引值,然后利用切片,但是切片顾头不顾尾,所以索引要加1
                    let indexNum = dataComment.indexOf('\n')+1
                    dataComment = dataComment.slice(indexNum) // 把换行符之前的切去
                }
                $.ajax({
                    url: '/comment/',
                    type: 'post',
                    data: {
                        'article_id': '{{ article_obj.pk }}',
                        'comment': dataComment,
                        // 前端parentId没有值,后端就存null
                        'parent_id':parentId
                    },
                    success: function (args) {
                        if (args.code = 1000) {
                            $('#id_error').text(args.msg);
                            // 情况评论框里的内容
                            $('#id_comment').val('');
                            // 临时渲染评论数
                            let userName = '{{ request.user.username }}'
                            let temp = `
            <span style="color: red">${userName}</span>
            <li class="list-group-item">
                <div>
                    ${dataComment}
                </div>
            </li>
                            `
                            // 将生成好的标签添加到ul标签内
                            $('#id_floor').append(temp);
                            // 提交后,需要将全局变量parentId恢复初始值
                            parentId = null;
                        }
                    }
                })
            });
            // 点击根评论的回复点击事件
            $('.reply').click(function () {
                // 需要评论对应的评论人姓名  还需要评论的主键值
                // 获取用户名
                let commentUsername = $(this).attr('username');
                // 获取主键值  直接修改parentId全局变量名
                parentId = $(this).attr('comment_id');

                // 拼接信息,聚焦事件传给评论框
                $('#id_comment').val('@'+commentUsername+'\n').focus();
            })
        })
    </script>
{% endblock %}

三 文章评论

'''
先写根评论
再写子评论

点击评论按钮需要将评论框里面的内容清空

根评论有两步渲染方式
    1.DOM临时渲染
    2.页面刷新render渲染
    
子评论
    点击回复按钮发生了几件事
        1.评论框自动聚焦
        2.将回复按钮所在的哪一行评论人的姓名
            @username
        3.评论框内部自动换行
        
根评论子评论都是点击一个按钮朝后端提交数据的
    parent_id
根评论子评论区别在哪?
    parent_id
'''

urls.py

# 点赞点踩
    url(r'^up_or_down/', views.up_or_down),

views.py

# 评论视图函数
def comment(request):
    if request.is_ajax():
        back_dic = {'code': 1000, 'msg': ''}
        article_id = request.POST.get('article_id')
        comment = request.POST.get('comment')
        parent_id = request.POST.get('parent_id')
        print(comment)
        print(parent_id)
        # 后端判断用户是否登录
        if request.user.is_authenticated():
            # 操作数据库,两张表,复习一下数据库的事务(两张表操作成功,才能成功)
            from django.db import transaction
            # 给文章表的评论数更新数据
            with transaction.atomic():
                from django.db.models import F
                models.Article.objects.filter(pk=article_id).update(comment_num=(F('comment_num') + 1))
                # 记录评论表
                models.Comment.objects.create(user=request.user, article_id=article_id, content=comment,
                                              parent_id=parent_id)
            back_dic['msg'] = '评论成功'
        else:
            back_dic['code'] = '2000'
            back_dic['msg'] = '请先登录'
        return JsonResponse(back_dic)

article_detal.html

{% extends 'base.html' %}

{% block css %}

    <style>
        /* 点赞点踩样式 */
        #div_digg {
            float: right;
            margin-bottom: 10px;
            margin-right: 30px;
            font-size: 12px;
            width: 128px;
            text-align: center;
            margin-top: 10px;
        }

        .diggit {
            float: left;
            width: 46px;
            height: 52px;
            background: url('/static/img/upup.gif') no-repeat;
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .buryit {
            float: right;
            margin-left: 20px;
            width: 46px;
            height: 52px;
            background: url('/static/img/downdown.gif') no-repeat;
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .clear {
            /*清除浮动*/
            clear: both;
        }
    </style>
{% endblock %}

{% block content %}

    <h1 class="text-center">{{ article_obj.title }}</h1>

    <div class="article_content" style="overflow: hidden">
        {{ article_obj.content|safe }}
    </div>
    <!--点赞点踩开始-->
    <div class="clearfix">
        <div id="div_digg">
            <div class="diggit action">
                <span class="diggnum" id="digg_count">{{ article_obj.up_num }}</span>
            </div>
            <div class="buryit action">
                <span class="burynum" id="bury_count">{{ article_obj.down_num }}</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips" style="color: red;"></div>
        </div>
    </div>
    <!--点赞点踩结束-->

    <!--根评论开始-->
    <!--评论楼开始-->
    <ul class="list-group" id="id_floor">
        {% for comment in comment_list %}
            <span>#{{ forloop.counter }}楼</span>
            <span>{{ comment.content_time|date:'Y-m-d h:i:s' }}</span>
            <span>{{ comment.user.username }}</span>

            <li class="list-group-item">
                <div>
                    {% if comment.parent_id %}
                        <!--字段parent是自关联字段,跨表还是跨的自己,在再通过外键user跨表查根评论用户-->
                        <p>@{{ comment.parent.user.username }}</p>
                    {% endif %}
                    <span>{{ comment.content }}</span>
                    <!--a标签自定义属性-->
                    <span class="pull-right"><a class="reply" username="{{ comment.user.username }}" comment_id="{{ comment.pk }}">回复</a></span>
                </div>

            </li>
        {% endfor %}
    </ul>
    <!--评论楼结束-->

    <!--前端判断是否登录-->
    {% if request.user.is_authenticated %}
        <div>
            <p><span class="glyphicon glyphicon-comment"></span>发表评论</p>
            <div>
                <textarea name="comment" id="id_comment" cols="60" rows="10"></textarea>
            </div>
            <input type="button" class="btn btn-success" id="id_submit" value="提交评论">
            <span style="color: red" id="id_error"></span>

        </div>
    {% else %}
        <ul class="nav navbar-nav navbar-link">
            <li><a href="{% url 'reg' %}">注册</a></li>
            <li><a href="{% url 'log' %}">登录</a></li>
        </ul>
    {% endif %}

    <!--根评论结束-->
{% endblock %}

{% block js %}
    <script>
        $(function () {
            // ajax向后端发送点赞点踩数
            $('.action').click(function () {
                // alert($(this).hasClass('diggit'))
                // isUp是bool值
                let isUp = $(this).hasClass('diggit');
                // 将点击过的div存起来
                let cilckDiv = $(this);
                // 朝后端发送ajax请求
                $.ajax({
                    url: '/up_or_down/',
                    type: 'post',
                    data: {
                        'article_id': '{{ article_obj.pk }}',
                        'is_up': isUp,
                    },
                    success: function (args) {
                        if (args.code == 1000) {
                            // 取得原来的数,并存给变量Num
                            let Num = cilckDiv.children().text();
                            // 点击过,该数加1,并修改过来,记得将数转成数字形,不然就是字符串拼接
                            cilckDiv.children().text(Number(Num) + 1);
                            $('#digg_tips').text(args.msg)
                        } else {
                            $('#digg_tips').html(args.msg)
                        }
                    }
                })
            });
            // ajax向后端发送评论
            // 全局设置一个parentId
                let parentId = null;
                console.log(parentId)
            $('#id_submit').click(function () {
                let dataComment = $('#id_comment').val();
                // 判断当前评论是否是子评论,如果是,需要将我们之前手动渲染的@username去除
                if (parentId){
                    // 找到\n对应的索引值,然后利用切片,但是切片顾头不顾尾,所以索引要加1
                    let indexNum = dataComment.indexOf('\n')+1
                    dataComment = dataComment.slice(indexNum) // 把换行符之前的切去
                }
                $.ajax({
                    url: '/comment/',
                    type: 'post',
                    data: {
                        'article_id': '{{ article_obj.pk }}',
                        'comment': dataComment,
                        // 前端parentId没有值,后端就存null
                        'parent_id':parentId
                    },
                    success: function (args) {
                        if (args.code = 1000) {
                            $('#id_error').text(args.msg);
                            // 情况评论框里的内容
                            $('#id_comment').val('');
                            // 临时渲染评论数
                            let userName = '{{ request.user.username }}'
                            let temp = `
            <span style="color: red">${userName}</span>
            <li class="list-group-item">
                <div>
                    ${dataComment}
                </div>
            </li>
                            `
                            // 将生成好的标签添加到ul标签内
                            $('#id_floor').append(temp);
                            // 提交后,需要将全局变量parentId恢复初始值
                            parentId = null;
                        }
                    }
                })
            });
            // 点击根评论的回复点击事件
            $('.reply').click(function () {
                // 需要评论对应的评论人姓名  还需要评论的主键值
                // 获取用户名
                let commentUsername = $(this).attr('username');
                // 获取主键值  直接修改parentId全局变量名
                parentId = $(this).attr('comment_id');

                // 拼接信息,聚焦事件传给评论框
                $('#id_comment').val('@'+commentUsername+'\n').focus();
            })
        })
    </script>
{% endblock %}

四 后台管理

新建页面,将文章个人文章展示出来,用到table标签,新增文章和其他功能的页面,导航条和左侧边栏一样,需要用到模版继承。

'''
当一个文件夹下文件比较多的时候,你还可以继续创建文件夹分类处理
    templates文件夹
        backend文件夹
        应用1文件
        应用2文件
'''

urls.py

 # 后台管理
    url(r'^backend/$', views.backend),

views.py

@login_required
def backend(request):
    article_list = models.Article.objects.filter(blog=request.user.blog)
    return render(request, 'backend/backend.html', locals())

base.html

用了 bootstrap中标签页,列表组

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ request.user.username }}的后台管理</title>
    <link rel="stylesheet" href="/static/bootstrap-3.4.1-dist/css/bootstrap.min.css">
    <script src="/static/js/jQuery-3.6.1.js"></script>
    <script src="/static/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script>
</head>
<body>
<!--导航条-->
<div class="container">
    <nav class="navbar navbar-default">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <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="/home/">{{ request.user.blog.site_title }}</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="#">Link <span class="sr-only">(current)</span></a></li>
                    <li><a href="#">test</a></li>
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                           aria-expanded="false">Dropdown <span class="caret"></span></a>
                        <ul class="dropdown-menu">
                            <li><a href="#">Action</a></li>
                            <li><a href="#">Another action</a></li>
                            <li><a href="#">Something else here</a></li>
                            <li role="separator" class="divider"></li>
                            <li><a href="#">Separated link</a></li>
                            <li role="separator" class="divider"></li>
                            <li><a href="#">One more separated link</a></li>
                        </ul>
                    </li>
                </ul>
                <form class="navbar-form navbar-left">
                    <div class="form-group">
                        <input type="text" class="form-control" placeholder="Search">
                    </div>
                    <button type="submit" class="btn btn-default">Submit</button>
                </form>
                <ul class="nav navbar-nav navbar-right">
                    {% if request.user.is_authenticated %}
                        <li><a href="#">{{ request.user.username }}</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="#" data-toggle="modal" data-target=".bs-example-modal-lg">修改密码</a></li>
                                <li><a href="#">修改头像</a></li>
                                <li><a href="#">后台管理</a></li>
                                <li role="separator" class="divider"></li>
                                <li><a href="{% url 'logout' %}" id="logout">退出登录</a></li>
                            </ul>
                            <!-- Large modal -->
                            <div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog"
                                 aria-labelledby="myLargeModalLabel">
                                <div class="modal-dialog modal-lg" role="document">
                                    <div class="modal-content">
                                        <h3 class="text-center">修改密码</h3>
                                        <div class="row">
                                            <div class="col-md-8 col-md-offset-2">
                                                <div class="form-group">
                                                    <label for="">用户名</label>
                                                    <input type="text" disabled value="{{ request.user.username }}"
                                                           class="form-control">
                                                </div>
                                                <div class="form-group">
                                                    <label for="old_password">原密码</label>
                                                    <input type="password" id="old_password" class="form-control">
                                                    <span style="color: red" class="pull-right"></span>
                                                </div>
                                                <div class="form-group">
                                                    <label for="">新密码</label>
                                                    <input type="password" id="new_password" class="form-control">
                                                </div>
                                                <div class="form-group">
                                                    <label for="">确认密码</label>
                                                    <input type="password" id="confirm_password" class="form-control">
                                                    <span style="color: red" class="pull-right"></span>
                                                </div>
                                                <div class="form-group">
                                                    <button type="button" class="btn btn-default btn-sm"
                                                            data-dismiss="modal">
                                                        取消
                                                    </button>
                                                    <button type="button" class="btn btn-primary btn-sm"
                                                            id="set_commit">
                                                        确认修改
                                                    </button>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </li>
                    {% else %}
                        <li><a href="{% url 'reg' %}">注册</a></li>
                        <li><a href="{% url 'log' %}">登录</a></li>
                    {% endif %}
                </ul>
            </div><!-- /.navbar-collapse -->
        </div><!-- /.container-fluid -->
    </nav>
</div>

<!--主板2,10-->
<div class="container">
    <div class="row">
        <!--左侧栏-->
        <div class="col-md-2">
            <div class="list-group">
                <a href="#" class="list-group-item active">
                    操作
                </a>
                <a href="/add/article/" class="list-group-item">新建文章</a>
                <a href="#" class="list-group-item">新建随笔</a>
                <a href="#" class="list-group-item">回收站</a>
                <a href="#" class="list-group-item">建议</a>
            </div>
        </div>
        <!--右侧内容区-->
        <div class="col-md-10">
            <div>

                <!-- Nav tabs -->
                <ul class="nav nav-tabs" role="tablist">
                    <li role="presentation" class="active"><a href="#home" aria-controls="home" role="tab"
                                                              data-toggle="tab">文章</a></li>
                    <li role="presentation"><a href="#profile" aria-controls="profile" role="tab" data-toggle="tab">随笔</a>
                    </li>
                    <li role="presentation"><a href="#messages" aria-controls="messages" role="tab" data-toggle="tab">文件</a>
                    </li>
                    <li role="presentation"><a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">设置</a>
                    </li>
                </ul>

                <!-- Tab panes -->
                <div class="tab-content">
                    <div role="tabpanel" class="tab-pane active" id="home">
                         {% block content %}
                         
                         {% endblock %}
                    </div>
                    <div role="tabpanel" class="tab-pane" id="profile">随笔页面</div>
                    <div role="tabpanel" class="tab-pane" id="messages">文件页面</div>
                    <div role="tabpanel" class="tab-pane" id="settings">设置</div>
                </div>

            </div>
        </div>
    </div>
</div>
{% block js %}

{% endblock %}
</body>
</html>

五 文章添加

属于后台管理页面中的功能

'''
有两个需要注意的问题
    1.文章的简介
        不能直接切去
            应该先想办法获取到当前页面的文本内容之后截取150个文本字符
    
    2.XSS攻击
        针对支持用户直接编写html代码的网址
        针对用户直接书写的script标签 需要处理
            (1)注释标签内部的内容
            (2)直接将script删除

如何解决?
    针对1 后端通过正则表达式筛选
    针对2 首先需要确定及获取script标签
    
    beautifulsoup模块   bs4模块
        专门用来帮你处理html页面的
        该模块主要用于爬虫程序
    pip3 install beautifulsoup4
'''


# 模块使用
soup = BeautifulSoup(content, 'html.parser')
# 获取所有的标签
tags = soup.find_all()
for tag in tags:
    # print(tag.name)  # 获取页面所有的标签
    # print(tags)
    # 针对script标签  直接删除
    if tag.name == 'script':
        # 删除标签
        tag.decompose()

# 文章简介
# 1 先简单暴力的直接切去content  150个字符
# desc = content[0:150]
# 2 截取文本150个
desc = soup.text[0:150]

'''
当你发现一个数据起来不是很方便的时候
可以考虑百度搜索有没有现成的模块帮你完成相应的功能
'''

urls.py

 # 增加文章
    url(r'^add/article/', views.add_article),

views.py

@login_required
def add_article(request):
    if request.method == 'POST':
        title = request.POST.get('title')
        # content是含html标签的字符串
        content = request.POST.get('content')
        category_id = request.POST.get('category')
        # print(category_id)
        tag_id_list = request.POST.getlist('tag')
        # print(tag_id_list)
        # beautifulsoup模块使用
        from bs4 import BeautifulSoup
        # 'html.parser'说明是用的python内置标准库
        soup = BeautifulSoup(content, 'html.parser')
        print(soup, type(soup))
        # tags是整个html标签页,含文本内容
        tags = soup.find_all()
        # 从tags中获取标签,只有标签
        for tag in tags:
            print(tag.name)  # 获取页面所有的标签
            # 防止xss攻击,删除script标签
            if tag.name == 'script':
                # 删除script标签,删除给script标签
                tag.decompose()
        # 文章简介
        # 1 截取文本150个字符,之前的直接截取,是含有html标签的
        desc = soup.text[0:150]
        # 操作数据,记录文章
        article_obj = models.Article.objects.create(
            title=title,
            desc=desc,
            # 接受删除script标签的内容
            content=str(soup),
            category_id=category_id,
            blog=request.user.blog
        )
        # 文章和标签的关系表  半自动是我们自己创建的 没法使用add set remove clear方法
        # 自己去操作关系表  一次性可能需要创建多条数据  批量插入bulk_create()
        article_obj_list = []
        for i in tag_id_list:
            # 用批量插入,是对象添加到列表,bulk_create(列表),是对象,不能用orm,不然要报错
            tag_article_obj = models.ArticleToTag(article=article_obj, tag_id=i)
            print(tag_article_obj)
            article_obj_list.append(tag_article_obj)
        # 批量插入,文章和标签的第三张表
        print(article_obj_list)
        try:
            models.ArticleToTag.objects.bulk_create(article_obj_list)
        except Exception as e:
            print(e)
        obj = models.ArticleToTag.objects.filter(pk=1).first()
        print(obj)
        return redirect('/backend/')
    category_list = models.Category.objects.filter(blog=request.user.blog)
    tag_list = models.Tag.objects.filter(blog=request.user.blog)
    return render(request, 'backend/add_article.html', locals())

add_articel.html

{% extends 'backend/base.html' %}


{% block content %}
    <h3>添加文章</h3>
    <form action="" method="post">
        {% csrf_token %}
        <p>标题</p>
        <input type="text" name="title" id="id_title" class="form-control">
        <div>
            <p>内容</p>
            <textarea name="content" id="editor_id" cols="40" rows="10"></textarea>
        </div>
        <p>分类</p>
        <div>
            <select name="category">
                {% for category in category_list %}
                    <option value="{{ category.pk }}">{{ category.name }}</option>
                {% endfor %}
            </select>
        </div>
        <p>标签</p>
        <div>
            <select multiple name="tag">
                {% for tag in tag_list %}
                    <option value="{{ tag.pk }}">{{ tag.name }}</option>
                {% endfor %}
            </select>
        </div>
        <input type="submit" class="btn btn-danger">
    </form>

{% endblock %}

{% block js %}
    {% load static %}
    <script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script>
    <script>
        KindEditor.ready(function (K) {
            window.editor = K.create('#editor_id', {
                // 设置编辑 器大小
                width: '100%',
                height: '600px',
                // 编辑器编辑按钮设置
                items: [
                    'source', '|', 'undo', 'redo', '|', 'preview', 'print', 'template', 'code', 'cut', 'copy', 'paste',
                    'plainpaste', 'wordpaste', '|', 'justifyleft', 'justifycenter', 'justifyright',
                    'justifyfull', 'insertorderedlist', 'insertunorderedlist', 'indent', 'outdent', 'subscript',
                    'superscript', 'clearhtml', 'quickformat', 'selectall', '|', 'fullscreen', '/',
                    'formatblock', 'fontname', 'fontsize', '|', 'forecolor', 'hilitecolor', 'bold',
                    'italic', 'underline', 'strikethrough', 'lineheight', 'removeformat', '|', 'image', 'multiimage',
                    'flash', 'media', 'insertfile', 'table', 'hr', 'emoticons', 'baidumap', 'pagebreak',
                    'anchor', 'link', 'unlink', '|', 'about'
                ],
                // 编辑器,左右固定,上下可以拖动,改变大小
                resizeType: 1,
                // 上传图片的后端提交路径
                uploadJson: '/upload/',
                // 上传图片、Flash、视音频、文件时,支持添加别的参数一并传到服务器。
                extraFileUploadParams: {
                    'csrfmiddlewaretoken': '{{ csrf_token }}',
                }
            });
        });
    </script>
{% endblock %}

六 kindeditor富文本编辑器

编辑器的种类很多,可以自己去网上搜索,

<a href="http://kindeditor.net/doc.php">kindeditor官网</a>

绑定textarea标签的id

<div>
            <p>内容</p>
            <textarea name="content" id="editor_id" cols="40" rows="10"></textarea>
        </div>

七 编辑器如何上传图片

别人写好了接口 但是接口不是你自己的 需要手动去修改

在使用别人的框架或者模块的时候 出现了问题不要慌 看看文档就用对应的处理方法

urls.py

# 修改用户头像
    url(r'^set/avatar/', views.set_avatar),

views.py

# 富文本编辑器上传文件视图函数
def upload(request):
    '''
    富文本编辑器,需要返回的格式数据
    //成功时
{
        "error" : 0,
        "url" : "http://www.example.com/path/to/file.ext"
}
//失败时
{
        "error" : 1,
        "message" : "错误信息"
}
    :param request:
    :return:
    '''
    # 用户写文章上传的图片  也算静态资源 也应该放到media文件夹下
    back_dic = {'error': 0}  # 先定义给返回给前端编辑器的数据格式
    if request.method == 'POST':
        # 获取用户上传的图片对象
        print(request.FILES)  # {'imgFile': [<InMemoryUploadedFile: 1603159551507.mp4 (video/mp4)>]}
        file_obj = request.FILES.get('imgFile')
        print(file_obj, file_obj.name)
        # 手动拼接存储文件的路径
        from bbs01 import settings
        import os
        file_dir = os.path.join(settings.BASE_DIR, 'media', 'article')
        # 优化操作,先判断当前文件夹是否存在 不存在 自动创建
        if not os.path.isdir(file_dir):
            os.mkdir(file_dir)  # 创建一层目录结构  article
        # 拼接图片的完整路径,如果要 唯一的文件名,可以用uuid模块
        file_path = os.path.join(file_dir, file_obj.name)
        print(file_path)
        with open(file_path, 'wb') as f:
            for line in file_obj:
                f.write(line)
        # 返回给编辑器的数据
        back_dic['url'] = '/media/article/{}'.format(file_obj.name)

    return JsonResponse(back_dic)

八 修改用户头像

用的方式跟注册用户 头像一致,需要注意的是,修改后,在操作数据库的时候,

不用update方式修改数据库,obj.save(),第一种,要修改保存路径

urls.py

# 修改用户头像
    url(r'^set/avatar/', views.set_avatar),

views.py

# 修改用户头像
@login_required
def set_avatar(request):
    if request.method == 'POST':
        file_obj = request.FILES.get('avatar')
        # update的更新方式,会将userinfo表的avatar字段的路径改动了,所以跳转到home页面时,显示不了头像
        # models.UserInfo.objects.filter(blog=request.user.blog).update(avatar=file_obj)
        # 所以要用一下方式
        # 1 自己手动添加avatar路径
        # 2 新的更新方式
        user_obj = request.user
        user_obj.avatar = file_obj
        user_obj.save()
        return redirect('/home/')
    blog = request.user.blog
    username = request.user.username
    return render(request, 'set_avatar.html', locals())

set_avatar.html

后期,建议用模态框的形式进行修改,或者另起一页

{% extends 'base.html' %}

{% block content %}
    <h3 class="text-center">修改头像</h3>
    <!--form表单上传文件,一定要设置enctype-->
    <form action="" method="post" enctype="multipart/form-data">
        {% csrf_token %}
        <p>
            原头像:
            <img src="/media/{{ request.user.avatar }}" alt="">
        </p>
        <p>
            新头像:
            <label for="my_file">头像
                {% load static %}
                <img src="{% static 'img/defaut.png' %}" id="my_img" alt="" class="img-rounded"
                     style="margin-left: 10px">
            </label>
            <input type="file" id="my_file" name="avatar" style="display: none">
        </p>
        <input type="submit" class="btn btn-info">
    </form>
{% endblock %}

{% block js %}
    <script>
        $(function () {
            $("#my_file").change(function () {
                // 文件阅读器对象
                // 1 先生成一个文件阅读器对象,内置对象
                let myFileReaderObj = new FileReader();
                // 2 获取用户上传的头像文件
                let fileObj = $(this)[0].files[0];
                // console.log(fileObj)
                // 3 将文件对象交给阅读器对象读取
                myFileReaderObj.readAsDataURL(fileObj)  // 异步操作 IO操作
                // 4 利用文件阅读器将文件展示到前端页面  修改src属性
                // 等待文件阅读器加载完毕之后再执行
                myFileReaderObj.onload = function () {
                    // 修改图像地址属性
                    $('#my_img').attr('src', myFileReaderObj.result)
                }
            })
        })
    </script>
{% endblock %}

 九 BBS项目总结

在开发任意的web项目的时候,到了后期需要写的代码越来越少 都是用已经写好的url填写到a标签href属性完成跳转即可

主要功能总结

表设计

开发流程(粗糙流程 还可以细化)

注册功能 forms组件使用 头像动态展示 错误信息提示

登录功能 图片验证码 滑动验证码

首页展示 media配置 主动暴露任意资源接口

个人站点展示 侧边栏展示 侧边栏筛选 侧边栏inclusion_tag

文章详情页 点赞点踩 评论

后台管理

针对bbs需要掌握每一个功能的书写思路 内部逻辑 之后再去敲代码熟悉 找感觉

 

标签:obj,项目,request,博客,django,html,user,article,id
From: https://www.cnblogs.com/coderxueshan/p/17702931.html

相关文章

  • 软件测评中心测试项目及测试过程简析,CMA、CNAS软件测试报告获取
    软件测试是产品周期中必不可少的一步,可以更好的保障软件质量,那么我们所知的软件测评中心一般有哪些测试项目以及测试流程是如何?和小编一起往下看看吧!一、软件测评中心的测试项目1、功能测试:通过模拟用户使用场景,测试软件的各项功能是否正常、稳定。2、性能测试:通......
  • java项目log4j配置
    1.添加maven依赖<dependencies><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency>&l......
  • vue3项目 基于vuedraggable插件实现拖拽上下移动
    //父页面<template><divclass="main_body"><blockTitletitle="事件详情"/><a-formref="formRef":model="formValue"style="width:100%"class="form_bo......
  • 项目开发中难点-项目使用v-if控制表单/元素/组件显示隐藏,例如调用接口后赋值需重新加
    项目中使用v-if="show"  控制组件的显示或隐藏,当接口返回后this.show=false,进行赋值,后this.show=true显示 。但是页面没有正常显示,此时使用this.$nextTick。 一、$nextTick()概述1.$nextTick()原理$nextTick()是Vue.js框架中的一个方法,它主要用于DOM操作......
  • .NET Core创建API项目
    新建项目类型:ASP.NETCoreWebAPIWebAPI控制器通常应派生自ControllerBase而不是Controller。Controller派生自ControllerBase,并添加对视图的支持,因此它用于处理Web页面,而不是WebAPI请求。如果同一控制器必须支持视图和WebAPI,则派生自Controller。API接口返回......
  • Android Studio中无法显示main.dart(Flutter项目在Android Studio中显示不全)
    问题描述创建完项目后只出现android文件选择ProjectFiles就会显示整个目录内容设置后......
  • 7 项目总结
    Webserver项目webserver项目总结1项目介绍使用多线程模型,利用信号量实现线程间加锁;利用I0复用技术Epoll与线程池实现多线程的Reactor高并发模型;利用RAII机制实现了数据库连接池,减少数据库连接建立与关闭的开销;利用正则与状态机解析HTTP请求报文实现处理静态资源的请求;基......
  • 大数据平台建设方案?如何进行大数据平台创新建设?大数据项目实施方法?
    大数据平台建设方案、创新建设和项目实施方法如下所述:1.大数据平台建设方案:  -需求分析:明确业务需求和目标,确定需要处理的数据类型、规模和频率。  -数据采集与存储:选择适合的数据采集工具和技术,设计数据存储方案,考虑数据的结构化和非结构化特点。  -数据处理与......
  • 正点 Linux C mqtt 项目技能点(测试然也物联)
    ①、开发板自带驱动的外设文件:1.LED:/sys/class/leds/sys-led/brightness调节亮度。2.CPU温度:/sys/class/thermal/thermal_zone0/temp,读取内容转换成浮点数除以1000就是当前的摄氏温度。 ②、然也物联测试:1.如果没有社区版mqtt账号,可以用免费版:地址为#defineBROKER......
  • vue项目打包编译后如何修改后台请求地址
    1、在public文件夹下新建config.js文件2、config.js文件中,编写配置地址代码 3、在index中引入js文件 4、使用config.js中的变量,vue页面,js页面都可以用window.setURL.publicBaseUrl ......