bbs项目:个人主页搭建、个人站点

一、登陆功能完善

验证码功能实现

ps:

1.使用pillow模块我们需要用一个ttf文件来设置字体的样式

2.因为大写的i和小写的l看起来比较相似,因此代码中我们把这两个字母删掉了。

from PIL import Image, ImageFont, ImageDraw

"""
Image           产生图片
ImageFont       字体样式
ImageDraw       画笔对象
"""
from io import BytesIO, StringIO

"""
BytesIO         在内存中临时存储 读取的时候以bytes格式为准
StringIO        在内存中临时存储 读取的时候以字符串格式为准
"""
import random

# 这个函数是用于生成图片颜色的
def get_random():
    return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)

# 这里是产生验证码图片的主体代码
def get_code_func(request):
    img_obj = Image.new('RGB', (350, 35), get_random())
    # 将图片对象交给画笔对象
    draw = ImageDraw.Draw(img_obj)
    # 生成字体对象
    font = ImageFont.truetype(font='static/HanYiYanKaiW-2.ttf', size=30)
    # 生成随机字符串
    code = ''
    # 这里的code是用来存储生成的字符串结果的
    for i in range(5):
        ran_num = str(random.randint(0, 9))
        ran_upper = chr(random.randint(65, 90))
        # 生成大写的随机字母
        # 去除I和L
        while ran_upper == 'L' or ran_upper == 'I':
            ran_upper = chr(random.randint(65, 90))
        ran_lower = chr(random.randint(97, 122))
        # 生成小写的随机字母
        # 去除i和l
        while ran_lower == 'i' or ran_lower == 'l':
            ran_lower = chr(random.randint(97, 122))
        res = random.choice([ran_num, ran_upper, ran_lower])
        # 将生成的随机字符画到图片中
        # 这里用变量i控制字符的横向位置
        draw.text(xy=(10 + i * 60, 0), text=res, font=font, fill=get_color())
        code += res
        # 把生成的随机字符串存到code中记录,并把这个字符写到验证码图片中
    # 这里我们用session模块保存验证码用来跟用户输入的数据进行对比,也可以在views中定义一个全局变量进行对比
    request.session['code'] = code
    # 昨天学习pillow模块的时候我们讲到,他产生的图片需要保存后才能使用,因此这里我们先把他保存成io对象再返回给前端的页面
    io_obj = BytesIO()
    img_obj.save(fp=io_obj, format='png')
    return HttpResponse(io_obj.getvalue())


# 这是用与生成字体颜色的函数
def get_color():
    x, y = 0, 255
    return (random.randint(x, y), random.randint(x, y), random.randint(x, y))

单机验证码实现验证码刷新(局部刷新)

在实现了验证码后我们参考实际网页的登陆界面,发现验证码可以通过单击刷新,这里我们可以使用事件来实现这个功能。

        // 1.验证码动态刷新
        $('#d1').click(function () {
            let oldSrc = $(this).attr('src');
            {#这里的this就是指代id为d1的img标签,后面的attr就是查找这个标签的src属性的值#}
            $(this).attr('src', oldSrc + '?')
            {#这里用到一个小知识点,当我们在产生验证码的路由后加上一个问号就会重新触发路由对应的视图函数产生新的验证码,当我们每次点击图片想要刷新验证码的时候就会通过在路由后加上问号产生新的验证码#}
        })

点击登陆提交数据进行校验

登陆页面编写到这里已经基本完成,只需要对用户提交的数据进行校验,这里我们使用ajax进行提交数据(因为验证码的存在,使用form表单不方便)

我们可以整理一下思路,目前我们需要实现的功能就是点击登陆按钮提交数据然后获取后端判断后的数据返回结果,而我们需要使用ajax进行数据提交,因此需要用事件来触发ajax。

  • 先在html中写上ajax请求
        // 2.登录按钮发送ajax请求
        $('#loginBtn').click(function () {
            // 可以再次使用form标签序列化功能 也可以自己挨个获取
            $.ajax({
                url:'',
                type:'post',
                {#这里我们自行拼接数据,组成字典的形式,方便后端用对象的方式获取数据#}
                data:{'username':$('#name').val(), 'password':$('#password').val(), 'code':$('#code').val(), 'csrfmiddlewaretoken':'{{ csrf_token }}'},
                success:function (args) {
                    {#当后端接收了发送的数据,返回结果的时候,我们通过自行设置的code属性来判断是否登陆成功,并依据code的值设置结果#}
                    
                }
            })
        })
  • 接下来去视图层写后端的处理数据的代码

ps:因为用到了auth模块,所以需要导入

from django.contrib import auth

def login_func(request):
    back_dict = {'code': 10000, 'msg': ''}
    # 类似之前的操作,先自行定义ajax返回的信息的格式,然后再根据情况填充数据
    if request.method == 'POST':
        # 先获取用户名跟密码以及验证码
        username = request.POST.get('username')
        password = request.POST.get('password')
        code = request.POST.get('code')
        # 接着先判断验证码是否正确(这里我们简单处理验证码,把字符全部转成大写后再进行判断)
        if code.upper() == request.session.get('code').upper():
            # 这里就是用auth模块查找是否有这个用户存在,并判断密码是否正确,不得不说auth是真的方便
            user_obj = auth.authenticate(request, username=username, password=password)
            if user_obj:
                # 如果我们的auth模块查找到了对应的用户信息就说明信息是全部正确的已经登陆成功了
                # 保存用户登录状态
                auth.login(request, user_obj)  # 执行之后就可以使用request.user获取登录用户对象
                back_dict['msg'] = '登陆成功'
                back_dict['url'] = '/home/'
            else:
                # 如果我们的auth模块没有查到对应的用户对象,说明用户名或密码不对
                back_dict['code'] = 10001
                back_dict['msg'] = '用户名或密码'
        else:
            # 这里就是验证码错了的情况,返回错误信息即可
            back_dict['code'] = 10002
            back_dict['msg'] = '验证码错误'
        # 当接收post请求需要返回数据的时候需要返回back_dict给前端返回结果,用JsonResponse返回结果,可以在前端用点的方式获取数据
        return JsonResponse(back_dict)

        # 在判断用户名跟密码是否正确

    return render(request, 'loginPage.html')

后端代码写好后,我们回到html文件中的ajax的接收返回数据的代码进行编写。(还需要编写一个csrf_token,否则csrf策略那过不去)

完整的前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    {% load static %}
    <script src="{% static 'jquery-3.6.1.js' %}"></script>
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
    <div class="container">
        <div class="col-md-8 col-md-offset-2">
            <h2 class="text-center">用户登录</h2>
            {% csrf_token %}
            <div class="form-group">
                <label for="name">用户名</label>
                <input type="text" id="name" class="form-control" name="username">
            </div>
            <div class="form-group">
                <label for="password">密码</label>
                <input type="password" id="password" class="form-control" name="password">
            </div>
            <div class="form-group">
                <label for="code">验证码</label>
                <div class="row">
                    <div class="col-md-6">
                        <input type="text" id="code" class="form-control" name="code">
                    </div>
                    <div class="col-md-6">
                        <img src="/get_code/" alt="" width="350" height="35" id="d1">
                    </div>
                </div>
            </div>
            <input type="button" class="btn btn-success btn-block" value="登录" id="loginBtn">
        </div>
    </div>

    <script>
        // 1.验证码动态刷新
        $('#d1').click(function () {
            let oldSrc = $(this).attr('src');
            {#这里的this就是指代id为d1的img标签,后面的attr就是查找这个标签的src属性的值#}
            $(this).attr('src', oldSrc + '?')
            {#这里用到一个小知识点,当我们在产生验证码的路由后加上一个问号就会重新触发路由对应的视图函数产生新的验证码,当我们每次点击图片想要刷新验证码的时候就会通过在路由后加上问号产生新的验证码#}
        })

        // 2.登录按钮发送ajax请求
        $('#loginBtn').click(function () {
            // 可以再次使用form标签序列化功能 也可以自己挨个获取
            $.ajax({
                url:'',
                type:'post',
                {#这里我们自行拼接数据,组成字典的形式,方便后端用对象的方式获取数据#}
                data:{'username':$('#name').val(), 'password':$('#password').val(), 'code':$('#code').val(), 'csrfmiddlewaretoken':'{{ csrf_token }}'},
                success:function (args) {
                    {#当后端接收了发送的数据,返回结果的时候,我们通过自行设置的code属性来判断是否登陆成功,并依据code的值设置结果#}
                    if (args.code === 10000) {
                        window.location.href = args.url
                    }else {
                        {#这里也可以使用sweetalert插件进行美化#}{#swal(args.msg,'error')#}
                        alert(args.msg)
                    }
                }
            })
        })

    </script>
</body>
</html>

二、主页搭建

首先还是添加主页对应的路由

urlpatterns = [
    path('admin/', admin.site.urls),
    # 用户注册功能
    path('register/', views.register_func, name='register_view'),
    # 用户登录功能
    path('login/', views.login_func, name='login_view'),
    # 图片验证码相关功能
    path('get_code/', views.get_code_func),
    # 网址首页
    path('home/', views.home_func, name='home_view'),
]

创建主页的html文件

  • 首先我们需要去bookstrap官网找一个导航条,并进行一些更改。
  • 然后我们参考学习auth模块时的作业,在导航条出实现当用户未登陆时显示登陆注册,登录后显示登陆的用户和一些操作的功能标签(用导航条的下拉框实现)。
  • 接着我们设置登陆后可以进行操作的标签,先实现修改密码和注销登陆的功能,其中修改密码我们使用模态框来展示。

html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script>
    {% load static %}
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
    {% block css %}

    {% endblock %}
</head>
<body>
<!--导航条开始-->
<nav class="navbar navbar-inverse">
    <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="#">
                {% block title %}
                    BBS
                {% endblock %}
                </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">

                {% 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="#myModal">修改密码</a></li>
                            <li><a href="#">修改头像</a></li>
                            <li><a href="#">后台管理</a></li>
                            <li role="separator" class="divider"></li>
                            <li><a href="/logout/">注销登录</a></li>
                        </ul>
                    </li>
                {% else %}
                    <li><a href="{% url 'register_view' %}">注册</a></li>
                    <li><a href="{% url 'login_view' %}">登录</a></li>
                {% endif %}
            </ul>
        </div><!-- /.navbar-collapse -->
    </div><!-- /.container-fluid -->
</nav>
<!--导航条结束-->
<!--模态框开始-->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span>
                </button>
                <h4 class="modal-title text-center" id="myModalLabel">修改密码</h4>
            </div>
            <div class="modal-body">
                <div class="form-group">
                    <label for="">用户名</label>
                    <input type="text" value="{{ request.user.username }}" disabled class="form-control">
                </div>
                <div class="form-group">
                    <label for="">原密码</label>
                    <input type="text" id="old_pwd" class="form-control">
                </div>
                <div class="form-group">
                    <label for="">新密码</label>
                    <input type="text" id="new_pwd" class="form-control">
                </div>
                <div class="form-group">
                    <label for="">确认密码</label>
                    <input type="text" id="confirm_pwd" class="form-control">
                </div>
            </div>
            <div class="modal-footer">
                <span id="error" style="color: red"></span>
                <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
                <button type="button" class="btn btn-warning" id="setBtn">修改</button>
            </div>
        </div>
    </div>
</div>
<!--模态框结束-->


<!--内容区域开始-->
<div class="container-fluid">
    <div class="row">
        {#这里也是用母版语法让子网页可以自定义#}
        {% block content %}
            {#主页的主要内容分成三大部分,左右两边用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">
                    事后必有重谢:wuyong123
                  </div>
                </div>
                 <div class="panel panel-warning">
                  <div class="panel-heading">
                    <h3 class="panel-title">百万大奖</h3>
                  </div>
                  <div class="panel-body">
                    恭喜你幸运儿:zhanghong321
                  </div>
                </div>
                <div class="panel panel-danger">
                  <div class="panel-heading">
                    <h3 class="panel-title">广告招租</h3>
                  </div>
                  <div class="panel-body">
                    旺铺吃一辈子:suiyuanli222
                  </div>
                </div>
            </div>
            <div class="col-md-8">
                {% for article_obj in page_queryset %}
                        <div class="media">
                         <h4 class="media-heading"><a href="#">{{ article_obj.title }}</a></h4>
                          <div class="media-left">
                            <a href="#">
                              <img class="media-object" src="/media/{{ article_obj.site.userinfo.avatar }}/" alt="..." width="80">
                            </a>
                          </div>
                          <div class="media-body" style="padding:10px">
                            {{ article_obj.desc }}
                          </div>
                            <br>
                            <div>
                                <span><a href="/{{ article_obj.site.userinfo.username }}/">&nbsp;{{ article_obj.site.userinfo.username }}&nbsp;&nbsp;</a></span>
                                <span>&nbsp;{{ article_obj.create_time|date:'Y-m-d H:i:s' }}&nbsp;&nbsp;</span>
                                <span class="glyphicon glyphicon-thumbs-up">&nbsp;{{ article_obj.up_num }}&nbsp;&nbsp;</span>
                                <span class="glyphicon glyphicon-thumbs-down">&nbsp;{{ article_obj.down_num }}&nbsp;&nbsp;</span>
                                <span class="glyphicon glyphicon-comment">&nbsp;{{ article_obj.comment_num }}&nbsp;&nbsp;</span>
                            </div>
                        </div>
                    <hr>
                {% endfor %}
                <div class="text-center">{{ page_obj.page_html|safe }}</div>
            </div>
            <div class="col-md-2">
                <div class="panel panel-primary">
                  <div class="panel-heading">
                    <h3 class="panel-title">重金求子</h3>
                  </div>
                  <div class="panel-body">
                    事后必有重谢:wuyong123
                  </div>
                </div>
                 <div class="panel panel-warning">
                  <div class="panel-heading">
                    <h3 class="panel-title">百万大奖</h3>
                  </div>
                  <div class="panel-body">
                    恭喜你幸运儿:zhanghong321
                  </div>
                </div>
                <div class="panel panel-danger">
                  <div class="panel-heading">
                    <h3 class="panel-title">广告招租</h3>
                  </div>
                  <div class="panel-body">
                    旺铺吃一辈子:suiyuanli222
                  </div>
                </div>
            </div>
        {% endblock %}
    </div>
</div>


<!--内容区域结束-->
<script>
    $('#setBtn').click(function () {
        $.ajax({
            url: '/set_pwd/',
            type: 'post',
            data: {
                'old_pwd': $('#old_pwd').val(),
                'new_pwd': $('#new_pwd').val(),
                'confirm_pwd': $('#confirm_pwd').val(),
                'csrfmiddlewaretoken': '{{ csrf_token }}',
            },
            success: function (args) {
                if (args.code === 10000){
                    window.location.href = args.url
                }else{
                    $('#error').text(args.msg)
                }
            }
        })
    })
</script>

{% block js %}

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

网页中我们用到了修改密码和注销登陆状态的功能,这两个功能需要在路由和视图层中进行注册和编写才能使用

    # 修改密码功能
    path('set_pwd/', views.set_pwd_func),
    # 注销登录功能
    path('logout/', views.logout),

views.py代码

def home_func(request):
    # 查询所有用户编写的文章
    article_queryset = models.Article.objects.all()
    '''文章过多的情况下应该考虑添加分页器,分页器代码在下面'''
    page_obj = mypage.Pagination(current_page=request.GET.get('page'), all_count=article_queryset.count())
    page_queryset = article_queryset[page_obj.start:page_obj.end]
    return render(request, 'homePage.html', locals())


因为修改密码和注销登陆状态,需要在登陆后才能执行,因此我们需要使用auth模块提供的装饰器进行区分
而使用auth模块的装饰器需要在配置文件中指定跳转的页面,代码如下:
LOGIN_URL = '/login/'

@login_required
def set_pwd_func(request):
    back_dict = {'code': 10000, 'msg': ''}
    if request.method == 'POST':
        old_pwd = request.POST.get('old_pwd')
        new_pwd = request.POST.get('new_pwd')
        confirm_pwd = request.POST.get('confirm_pwd')
        # 先校验原密码是否正确
        if request.user.check_password(old_pwd):
            # 再校验两次密码是否一致 并且不能为空
            if new_pwd == confirm_pwd and new_pwd:
                request.user.set_password(new_pwd)
                request.user.save()
                back_dict['msg'] = '密码修改成功'
                back_dict['url'] = '/login/'
            else:
                back_dict['code'] = 10001
                back_dict['msg'] = '两次密码不一致或者为空'
        else:
            back_dict['code'] = 10002
            back_dict['msg'] = '原密码错误'
        return JsonResponse(back_dict)


@login_required
def logout(request):
    auth.logout(request)
    return redirect('home_view')

主页内容部分

后台添加数据

网页代码中的内容代码部分,我们需要先添加数据才能进行展示。这里我们用django的admin后台管理来添加数据,这里可以回顾一下知识点,因为我们需要创建一个管理员用户才能登陆进去添加数据,我们通过注册页面创建的是普通用户。

创建管理员的关键字为createsuperuser

img

接着在浏览器中访问admin页面

http://127.0.0.1:8000/admin/

输入刚刚注册的管理员账号后进入如图界面:

img

这里我们可以看到,虽然进入了后台管理界面,但是什么都没有,我们无法操作也无法添加数据,这时候就需要到应用下的admin.py文件中编写代码添加配置,代码如下:

from django.contrib import admin
'因为我们需要注册models.py文件中的表,所以需要导入'
from app01 import models
# Register your models here.
'''只要注册了 admin就会自动生产针对该注册表的增删改查至少四个功能'''
admin.site.register(models.UserInfo)
admin.site.register(models.Site)
admin.site.register(models.Article)
admin.site.register(models.Category)
admin.site.register(models.Tag)
admin.site.register(models.Article2Tag)
admin.site.register(models.UpAndDown)
admin.site.register(models.Comment)

结果如图:

img

ps:如果不喜欢注册后的英文名,我们可以回顾面向对象中的双下str方法,我们可以在模型表中给他们添加双下str方法,让admin管理界面中,读取表名称的时候显示中文,如果想让注册后的名称显示成中文,需要给表增加配置。同时这些配置都不需要执行数据库迁移操作,他们只是配置信息。

# 修改admin后台管理的表名
class Meta:
    verbose_name_plural = '用户表'
# 修改admin管理界面读取表的时候的名称,我们在绑定外键的时候会用到
def __str__(self):
    return f'用户对象:{self.username}'

结果如果:

img

之所以我们会使用admin后台添加数据,就是因为他具有父子页面通信的功能,在创建表数据的时候更加方便

界面如下:

img

后续部分就是添加数据,比较枯燥,但是添加的时候需要信息,如果数据绑定外键除了问题,可能会出现一些大问题,比如个人站点绑定错了的情况。

到了添加了站点跟用户进行绑定的时候需要给phone电话字段添加配置,让admin系统不强制要求电话号码,代码如下:

    phone = models.BigIntegerField(verbose_name='手机号', null=True, blank=True)

分页器

在设置主页文章展示的时候,考虑到文章会很多,需要用分页器分批展示。

首先创建文件导入分页器代码,这里我们设置每页展示10篇文章

mypage.py

class Pagination(object):
    def __init__(self, current_page, all_count, per_page_num=10, pager_count=11):
        """
        封装分页相关数据
        :param current_page: 当前页
        :param all_count:    数据库中的数据总条数
        :param per_page_num: 每页显示的数据条数
        :param pager_count:  最多显示的页码个数
        """
        try:
            current_page = int(current_page)
        except Exception as e:
            current_page = 1

        if current_page < 1:
            current_page = 1

        self.current_page = current_page

        self.all_count = all_count
        self.per_page_num = per_page_num

        # 总页码
        all_pager, tmp = divmod(all_count, per_page_num)
        if tmp:
            all_pager += 1
        self.all_pager = all_pager

        self.pager_count = pager_count
        self.pager_count_half = int((pager_count - 1) / 2)

    @property
    def start(self):
        return (self.current_page - 1) * self.per_page_num

    @property
    def end(self):
        return self.current_page * self.per_page_num

    def page_html(self):
        # 如果总页码 < 11个:
        if self.all_pager <= self.pager_count:
            pager_start = 1
            pager_end = self.all_pager + 1
        # 总页码  > 11
        else:
            # 当前页如果<=页面上最多显示11/2个页码
            if self.current_page <= self.pager_count_half:
                pager_start = 1
                pager_end = self.pager_count + 1

            # 当前页大于5
            else:
                # 页码翻到最后
                if (self.current_page + self.pager_count_half) > self.all_pager:
                    pager_end = self.all_pager + 1
                    pager_start = self.all_pager - self.pager_count + 1
                else:
                    pager_start = self.current_page - self.pager_count_half
                    pager_end = self.current_page + self.pager_count_half + 1

        page_html_list = []
        # 添加前面的nav和ul标签
        page_html_list.append('''
                    <nav aria-label='Page navigation>'
                    <ul class='pagination'>
                ''')
        first_page = '<li><a href="?page=%s">首页</a></li>' % (1)
        page_html_list.append(first_page)

        if self.current_page <= 1:
            prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
        else:
            prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,)

        page_html_list.append(prev_page)

        for i in range(pager_start, pager_end):
            if i == self.current_page:
                temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
            else:
                temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
            page_html_list.append(temp)

        if self.current_page >= self.all_pager:
            next_page = '<li class="disabled"><a href="#">下一页</a></li>'
        else:
            next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,)
        page_html_list.append(next_page)

        last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,)
        page_html_list.append(last_page)
        # 尾部添加标签
        page_html_list.append('''
                                           </nav>
                                           </ul>
                                       ''')
        return ''.join(page_html_list)

前端获取头像文件

我们会发现遇到了刚开始学django时候遇到的问题,前端没有访问后端图片的权限,而我们在注册的时候是把所有的头像文件都存在了avatar文件夹下的,这里就需要用到media媒体目录,实现自定义开放接口给前端访问。

首先我们需要在配置文件中注册我们想要开放的文件夹的路径

python

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

配置信息添加好后并不需要自行创建文件夹,这时候django会帮我们创建。我们可以创建一个用户,这样就可以看到文件夹了。

但是现在我们还不能让前端访问,还需要在路由层写上接口

from django.views.static import serve
from django.conf import settings
    
# 自定义暴露资源接口
re_path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),

通过他的功能我们可以暴露后端任意的文件夹给前端访问,因此我们要谨慎使用,避免源文件代码暴露出去。

三、个人站点页面搭建

通过查看博客园的网页,我们发现主线展示文章的时候展示了作者的名称,而这个名称是一个a标签,点击后可以跳转到对应的个人站点页面,而这个页面是根据用户名来充当网址后缀的,因此需要使用转换器或是正则进行匹配。

首先是创建路由

# 个人站点接口
    path('<str:username>/', views.site_func)

接着是视图层创建视图函数

def site_func(request, username):
    # 查询个人站点是否存在
    site_obj = models.Site.objects.filter(siti_name=username).first()
    if not site_obj:
        return render(request, 'errorPage.html')
    # 查询个人站点下所有的文章
    article_queryset = models.Article.objects.filter(site=site_obj)
    '''如果文章较多也应该添加分页器'''
    # 查询个人站点下所有的分类名称以及每个分类下的文章数
    category_queryset = models.Category.objects.filter(site=site_obj).annotate(article_num=Count('article__pk')).values(
        'name', 'article_num')
    # 查询个人站点下所有的标签名称以及每个标签下的文章数
    tag_queryset = models.Tag.objects.filter(site=site_obj).annotate(article_num=Count('article__pk')).values(
        'name', 'article_num')
    # 年月分组并统计文章个数
    from django.db.models.functions import TruncMonth
    date_queryset = models.Article.objects.filter(site=site_obj).annotate(month=TruncMonth('create_time')).values('month').annotate(
        article_num=Count('pk')).values('month', 'article_num')
    print(date_queryset)
    return render(request, 'sitePage.html', locals())

接下去我们考虑到个人站点十分类似主页面,因此我们使用母版继承的方式来编写个人站点,这里在编写主页的时候已经写好了,主页部分代码就不展示了。

个人站点页面实现的功能有以下几个:类似主页的文章展示,但是用户名处不用使用a标签,左侧需要根据文章分类、文章标签、文章创建日期进行分类统计,并且这些标签可以点击跳转,点击后就相当于执行了orm查询操作,同时日期要具体到月份。

文章展示可以模仿主页,返回所有文章查询结果的对象给前端即可

三处文章分类,需要在视图函数中返回对应的对象,这里需要用到分组。

在处理最后的根据创建日期的月份进行分组的时候,需要用到日期字段的切割处理。这里可以参考官网提供的一个orm语法

通过官网文档实现日期字段切割

官网提供了针对日期字段的切割处理

id  content      create_time     month
1   111        2020-11-11     2020-11
2   222        2020-11-12     2020-11
3   333        2020-11-13     2020-11
4   444        2020-11-14     2020-11
5   555        2020-11-15     2020-11
"""
django官网提供的一个orm语法
 from django.db.models.functions import TruncMonth
-官方提供
   from django.db.models.functions import TruncMonth
   Sales.objects
   .annotate(month=TruncMonth('timestamp'))  # Truncate to month and add to select list
   .values('month')  # Group By month
   .annotate(c=Count('id'))  # Select the count of the grouping
   .values('month', 'c')  # (might be redundant, haven't tested) select month and count
   
   
时区问题报错
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = False
"""

这里的基本意思就是点了objects后我们点annotate并不会执行分组,这时候参考官网提供的文档写就会对日期字段进行处理,产生一个month字段在表中,内部时间具体到月份,如果想修改具体到哪个位置,可以更换TruncMonth方法为别的,接下去我们再点values就是对数据进行查找,然后再点annotate就会进行分组,接下去就可以点values查找我们需要的数据了。

ps:使用的时候需要先用filter筛选数据,否则会查找所有的文章。

这时候就可以把前端需要对象返回给html文件,接下去就是类似标题和分类的渲染操作

图片防盗链

当我们随便在博客园输入一个肯定不存在的网址,他会有一个固定的错误网页展示给我们,我们也想弄一个,于是我们直接cv他的代码,但是在请求图片的时候我们并不能成功请求到,这就是使用了图片防盗链技术。

img

img

他的实现就是判断请求是否来自与站点内的网页,如果不是就不给你使用图片连接。

个人样式

模仿博客园,我们肯定会想到自定义样式,因此我们在母版中预留的地方进行设置即可,导入存在media文件夹中的css文件(这些文件都需要被前端使用,因此统一放在media文件夹中)

posted @ 2023-02-11 20:04  wwwxxx123  阅读(58)  评论(0编辑  收藏  举报