DjangoBBS项目功能拆分

目录

  • 1、随机验证码
  • 2、注册功能
  • 3、登录功能
  • 4、登录认证装饰器配置
  • 5、修改密码模态框方法
  • 6、修改头像
  • 7、修改签名模态框方法
  • 8、注销功能模态框
  • 9、用户上传静态文件配置
  • 10、图片防盗链
  • 11、暴露任意文件的配置
  • 12、分页器的使用
  • 13、每个用户拥有自己的css
  • 14、分组,按年月等。官方推荐
  • 15、侧边栏筛选(自定义过滤器方法)
  • 16、点赞点踩
  • 17、模板字符串
  • 18、KindEditor编辑器使用

1、随机验证码

url(r'^get_code/', views.get_code, name='get_code'),

# 获取随机3个0-255数
def get_random():
    """
    :return: 返回0-255三个随机数,元组
    """
    return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)


# 获取验证码
def get_code(request):
    # 1.产生一张随机颜色的图片
    img_obj = Image.new('RGB', (350, 35), get_random())
    # 2.产生一只在图片上的画笔
    img_draw = ImageDraw.Draw(img_obj)
    # 3.产生字体样式
    img_font = ImageFont.truetype(r'static\font\font.ttf', 35)
    io_obj = BytesIO()

    # 产生5个随机验证码
    code = ''
    for i in range(5):
        upper_str = chr(random.randint(65, 90))  # 大写字母
        lower_str = chr(random.randint(97, 122))  # 小写字母
        random_int = str(random.randint(0, 9))  # 数字
        # 随机取一个
        temp_str = random.choice([upper_str, lower_str, random_int])
        # 写在图片上,位置,内容,颜色,字体
        img_draw.text((45 + i * 60, -2), temp_str, get_random(), font=img_font)
        # 储存
        code += temp_str
    print(code)
    img_obj.save(io_obj, 'png')
    request.session['code'] = code
    return HttpResponse(io_obj.getvalue())

前端代码:

图片验证码

js代码:

原理:src改变,立马刷新。点击一次图片,给url添加一个?号

$('#id_img').click(function () {
        var oldSrc = $(this).attr('src');
        $(this).attr('src', oldSrc += '?')
    });

2、注册功能

DjangoBBS项目功能拆分_第1张图片

前端代码:




    
    注册
    
    
    
    
    {% load static %}
    
    


注册页面

{% csrf_token %} {% for form in form_obj %}
{{ form }}
{% endfor %}
  

后端urls.py:

# 注册
    url(r'^register/', views.register, name='register'),

views.py:

# 注册
def register(request):
    form_obj = MyRegForm()
    if request.method == 'POST':
        back_dic = {'code': 1000, 'msg': ""}
        # 校验用户名、密码
        form_obj = MyRegForm(request.POST)
        if form_obj.is_valid():
            # 用变量接收正确的结果 clean_data = {'username'   'password'  're_password' 'email'}
            clean_data = form_obj.cleaned_data
            # 将确认密码键值对删除,表中没有re_password
            clean_data.pop('re_password')

            # 把签名、用户CSS名字存进Blog表中
            sign = clean_data.get('sign')
            username = clean_data.get('username')
            site_theme = username + '.css'
            models.Blog.objects.create(site_name=username, site_title=sign, site_theme=site_theme)

            # 添加字段
            clean_data['blog'] = models.Blog.objects.filter(site_name=username).first()
            clean_data.pop('sign')

            # 额外做的事情:给每个新的注册用户添加3个默认的分类和3个默认的标签
            create_list = []
            blog = models.Blog.objects.filter(site_name=username).first()
            for i in ['一', '二', '三']:
                category_name = username+'的分类'+i
                create_list.append(models.Category(name=category_name, blog=blog))
            models.Category.objects.bulk_create(create_list)

            # 添加3个默认标签
            create_list = []
            for i in ['一', '二', '三']:
                tag_name = username + '的标签' + i
                create_list.append(models.Tag(name=tag_name, blog=blog))
            models.Tag.objects.bulk_create(create_list)
            # 获取用户头像文件
            avatar_obj = request.FILES.get('avatar')
            # 判断用户头像文件是否为空,用户没有上传
            if avatar_obj:
                # 用户上传了,添加到clean_data中
                clean_data['avatar'] = avatar_obj  # clean_data = {'username'  'password'  'email' 'avatar'}
            models.UserInfo.objects.create_user(**clean_data)  # 打散传入  ??=??的形式
            back_dic['msg'] = '注册成功'
            back_dic['url'] = '/login/'
        else:
            back_dic['code'] = 2000
            back_dic['msg'] = form_obj.errors
        return JsonResponse(back_dic)
    return render(request, 'register.html', locals())

myforms.py:

from django import forms
from app01 import models

class MyRegForm(forms.Form):
    username = forms.CharField(min_length=3,max_length=8,label='用户名',
                               error_messages={
                                   "min_length":'用户名最短3位',
                                   "max_length":'用户名最长8位',
                                   "required":'用户名不能为空',
                               },widget=forms.widgets.TextInput(attrs={'class':'form-control'})
                               )

    password = forms.CharField(min_length=3, max_length=8, label='密码',
                               error_messages={
                                   "min_length": '密码最短3位',
                                   "max_length": '密码最长8位',
                                   "required": '密码不能为空',
                               }, widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'})
                               )

    re_password = forms.CharField(min_length=3, max_length=8, label='确认密码',
                               error_messages={
                                   "min_length": '确认密码最短3位',
                                   "max_length": '确认密码最长8位',
                                   "required": '确认密码不能为空',
                               }, widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'})
                               )

    email = forms.EmailField(label='邮箱',
                             error_messages={
                                 "required": '邮箱不能为空',
                                 "invalid":"邮箱格式不正确"
                             },
                             widget=forms.widgets.EmailInput(attrs={'class': 'form-control'})
                             )
    sign = forms.CharField(min_length=5, max_length=15, label='学习宣言',
                           error_messages={
                               "min_length": '学习宣言最短5位',
                               "max_length": '学习宣言最长15位',
                               "required": '学习宣言不能为空',}
                           , widget=forms.widgets.TextInput(attrs={'class': 'form-control'})
                           )

    # 钩子函数
    # 局部钩子校验用户名是否已存在
    def clean_username(self):
        username = self.cleaned_data.get('username')
        is_alive = models.UserInfo.objects.filter(username=username)
        if is_alive:
            self.add_error('username','用户名已存在')
        return username


    # 全局钩子校验密码与确认密码是否一致
    def clean(self):
        password = self.cleaned_data.get('password')
        re_password = self.cleaned_data.get('re_password')
        if not password == re_password:
            self.add_error('re_password','两次密码不一致')
        return self.cleaned_data

3、登录功能

DjangoBBS项目功能拆分_第2张图片

前端代码:




    
    Title
    
    
    
    
    {% load static %}
    
    



登录页面

图片验证码

后端代码:

urls.py:

# 登录
    url(r'^login/', views.login, name='login'),

views.py:

# 登录
def login(request):
    back_dic = {'code': None, 'msg': None}
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        code = request.POST.get('code')  # 从前端拿过来的验证码
        # 先对比验证码
        if request.session.get('code').lower() == code.lower():
            # 校验用户名和密码
            user_obj = auth.authenticate(username=username, password=password)
            if user_obj:
                # 记录登录状态
                auth.login(request, user_obj)
                back_dic['code'] = 1000
                back_dic['msg'] = '登录成功'
                back_dic['url'] = '/home/'
                # back_dic['url'] = '/%s/' % username
            else:
                back_dic['code'] = 2000
                back_dic['msg'] = '用户名或密码错误'
        else:
            back_dic['code'] = 3000
            back_dic['msg'] = '验证码错误'
        return JsonResponse(back_dic)
    return render(request, 'login.html')

4、登录认证装饰器配置

settings.py:

LOGIN_URL = '/login/'

5、修改密码模态框方法

DjangoBBS项目功能拆分_第3张图片

前端代码:

  • 修改密码
  • {#修改密码模态框#}

    js代码:

    
    

    后端代码:

    urls.py:

    # 修改密码
        url(r'^set_password', views.set_password, name='set_password'),
    

    views.py:

    # 修改密码
    @login_required
    def set_password(request):
        if request.is_ajax():
            back_dic = {'code': 1000, 'msg': ''}
            old_password = request.POST.get('old_password')
            new_password = request.POST.get('new_password')
            confirm_password = request.POST.get('confirm_password')
            if new_password == confirm_password:
                is_right = request.user.check_password(old_password)
                if is_right:
                    request.user.set_password(new_password)
                    request.user.save()
                    back_dic['msg'] = '修改成功'
                    back_dic['url'] = reverse('login')
                else:
                    back_dic['code'] = 2000
                    back_dic['msg'] = '原密码错误'
            else:
                back_dic['code'] = 3000
                back_dic['msg'] = '两次密码不一致'
            return JsonResponse(back_dic)
    

    6、修改头像

    DjangoBBS项目功能拆分_第4张图片

    前端代码:

  • 修改头像
  • stt_avatar.html:

    {% extends 'base.html' %}
    
    {% block content %}
        

    返回


    {% csrf_token %}

    {% endblock %}

    后端代码:

    urls.py:

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

    views.py:

    # 修改头像
    @ login_required
    def set_avatar(request):
        if request.method == 'POST':
            avatar_obj = request.FILES.get('myfile')
            # models.UserInfo.objects.filter(pk=request.user.pk).update(avatar=avatar_obj)   # 不会帮你自动添加前缀
            # 用自己的save方法,自动帮你添加前缀
            request.user.avatar = avatar_obj
            request.user.save()
        return render(request, 'set_avatar.html')
    

    7、修改签名模态框方法

    DjangoBBS项目功能拆分_第5张图片

    前端代码:

  • 编辑签名
  • {#编辑签名模态框#}

    js代码:

    {#修改签名#}
        $("#set_sign").click(function () {
            $.ajax({
                url: '{% url 'set_sign' %}',
                type: 'post',
                data: {
                    new_sign: $('#id_new_sign').val(),
                    csrfmiddlewaretoken: '{{ csrf_token }}'
                },
                success: function (data) {
                    if (data.code === 1000) {
                        location.href = data.url;
                        $('#error_sign').text(data.msg)
                    } else {
                        $('#error_sign').text(data.msg)
                    }
                }
            })
    
        });
    

    后端代码:

    urls.py:

    # 编辑签名
        url(r'^set_sign', views.set_sign, name='set_sign'),
    

    views.py:

    # 编辑签名
    @login_required
    def set_sign(request):
        if request.is_ajax():
            back_dic = {'code': 1000, 'msg': ''}
            site_name = request.user.username
            site_title = request.POST.get('new_sign')
            if len(site_title) < 5:
                back_dic['code'] = 2000
                back_dic['msg'] = '(你的学习宣言必须大于5位)'
            elif len(site_title) > 15:
                back_dic['code'] = 3000
                back_dic['msg'] = '(你的学习宣言必须小于于15位)'
            else:
                back_dic['code'] = 1000
                back_dic['url'] = reverse('home')
                back_dic['msg'] = '修改成功'
                models.Blog.objects.filter(site_name=site_name).update(site_title=site_title)
            return JsonResponse(back_dic)
    

    8、注销功能模态框

    DjangoBBS项目功能拆分_第6张图片

    前端代码:

  • 注销
  • {# 退出确认模态框#}

    js代码:

    {#注销#}
        $('#exit').click(function () {
            location.href = "/logout/"
        });
    

    后端代码:

    urls.py:

    # 注销
        url(r'^logout', views.logout, name='logout'),
    

    views.py:

    # 注销
    @login_required
    def logout(request):
        auth.logout(request)  # 原理删除了对应的session值
        return redirect(reverse('home'))
    

    9、用户上传静态文件配置

    setting.py文件配置:

    配置好之后,文件夹自动创建

    # media配置
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')   # 用户上传的文件全部保存该文件下
    

    10、图片防盗链

    请求头里面有一个referer请求头,用来标识你上一次是从哪一个网址过来的

    判断上一次这个网址是否有权限

    自己的项目:把图片所在的文件夹暴露,那么只能访问图片。

    别人的图片怎么解决防盗链?:

    1.用爬虫将所有的图片资源下载到本地    这是爬虫的价值所在
    2.修改请求头参数  百度搜吧
    

    referer属性:

    DjangoBBS项目功能拆分_第7张图片

    11、暴露任意文件的配置

    urls.py:

    注意:千万不要暴露重要文件资源,否则拍屁股走人

    MEDIA_ROOT,一定不要暴露关键文件

    from django.views.static import serve
    # 暴露任意后端资源配置
        url(r'^media/(?P.*)', serve, {"document_root": settings.MEDIA_ROOT}),
    

    DjangoBBS项目功能拆分_第8张图片

    图片文件地址:

    这是你的头像
    

    DjangoBBS项目功能拆分_第9张图片

    12、分页器的使用

    分页器:新建py文件,把代码复制过来

    DjangoBBS项目功能拆分_第10张图片

    代码:

    class Pagination(object):
        def __init__(self,current_page,all_count,per_page_num=2,pager_count=11):
            """
            封装分页相关数据
            :param current_page: 当前页
            :param all_count:    数据库中的数据总条数
            :param per_page_num: 每页显示的数据条数
            :param pager_count:  最多显示的页码个数
            
            用法:
            queryset = model.objects.all()
            page_obj = Pagination(current_page,all_count)
            page_data = queryset[page_obj.start:page_obj.end]
            获取数据用page_data而不再使用原始的queryset
            获取前端分页样式用page_obj.page_html
            """
            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('''
                        
                                               
                                           ''')
            return ''.join(page_html_list)
    

    使用方法:

    后端代码:

    from app01.utils.mypagenation import Pagination  # 分页器导
    
    # 首页
    def home(request):
        # 将网站的所有文章展示到前端
        article_list = models.Article.objects.all()
        # 分页处理
        page_obj = Pagination(current_page=request.GET.get('page',1),all_count=article_list.count())
        article_list = article_list[page_obj.start:page_obj.end]
        return render(request, 'home.html', locals())
    

    前端代码:

    {#分页器 #}
    
    {{ page_obj.page_html|safe }}

    13、每个用户拥有自己的css

    1.在注册的时候把用户的css文件的名字固定写好,写进数据库

    DjangoBBS项目功能拆分_第11张图片

    2.在用户编辑CSS的时候,再通过文件操作,创建用户固定的CSS文件

    DjangoBBS项目功能拆分_第12张图片

    3.再应用导入自己的CSS文件

    {#引用自己的css#}
    
    

    前端代码:

    个人站点CSS设置
    

    urls.py:

    # 个人站点CSS设置
        url(r'^blog_css/', views.blog_css, name='blog_css'),
    

    views.py:

    # 个人站点CSS设置
    @login_required
    def blog_css(request):
        username = request.user.username
        site_theme = models.Blog.objects.filter(site_name=username).first().site_theme
        css_dir = f'media/css/{site_theme}'
        if request.method == 'POST':
            new_css = request.POST.get('new_css')
            with open(css_dir, 'w', encoding='utf-8')as f:
                for line in new_css:
                    res = line.replace('\n', '')
                    f.write(res)
                f.close()
            return redirect('/blog_css/')
        if request.method == 'GET':
            # 先判断用户css文件是否存在,不存在就创建。存在就读取
            isfile = os.path.exists(css_dir)
            # 不存在创建空的css文件
            if not isfile:
                with open(css_dir, 'w')as f:
                    f.close()
            # 存在就读取
            with open(css_dir, 'r')as f:
                old_css = f.read()
            return render(request, 'backend/blog_css.html', locals())
    

    backend/blog_css.html:

    {% extends 'backend/backend_base.html' %}
    {% block article %}
        
    {% csrf_token %}

    页面定制 CSS 代码

    推荐客户端: Open Live Writer

    MetaWeblog访问地址: http://127.0.0.1:8000/home/

    {% endblock %}

    14、分组,按年月等。官方推荐

    -官方提供
    from django.db.models.functions import TruncMonth
    Article.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

    DjangoBBS项目功能拆分_第13张图片

    DjangoBBS项目功能拆分_第14张图片

    # 3.按照文章的年月分组
        date_list = models.Article.objects.filter(blog=blog).\
            annotate(month=TruncMonth('create_time')).values(
            'month').annotate(c=Count('pk')).values('c', 'month')
    
    {% for date in date_list %}
                

    {{ date.month|date:'Y年m月' }}({{ date.c }})

    {% endfor %}

    15、侧边栏筛选(自定义过滤器方法)

    新建文件夹和py文件:

    DjangoBBS项目功能拆分_第15张图片

    mytag.py代码:

    from django.template import Library
    from app01 import models
    from django.db.models import Count
    from django.db.models.functions import TruncMonth
    register = Library()
    
    
    # 侧边栏渲染,自定义过滤器方法
    @register.inclusion_tag('left_menu.html', name='my_left')
    def index(username):
        # 提供left_menu所需要的所有数据
        user_obj = models.UserInfo.objects.filter(username=username).first()
        blog = user_obj.blog
        # 1.查询当前用户的分类及每个分类下的文章数
        category_list = models.Category.objects.all().filter(blog=blog).annotate(article_sum=Count('article__pk')).values(
            'article_sum', 'name', 'pk')
    
        # 2.查询当前用户的标签,及每个标签下的文章数
        tag_list = models.Tag.objects.all().filter(blog=blog).annotate(tag_sum=Count('article__pk')).values('tag_sum',
                                                                                                            'name', 'pk')
    
        # 3.按照文章的年月分组
        date_list = models.Article.objects.filter(blog=blog).\
            annotate(month=TruncMonth('create_time')).values(
            'month').annotate(c=Count('pk')).values('c', 'month')
        return locals()
    

    left_menu.html:

    文章分类

    {% for category in category_list %}

    {{ category.name }}({{ category.article_sum }})

    {% endfor %}

    文章标签

    {% for tag in tag_list %}

    {{ tag.name }}({{ tag.tag_sum }})

    {% endfor %}

    日期归档

    {% for date in date_list %}

    {{ date.month|date:'Y年m月' }}({{ date.c }})

    {% endfor %}

    16、点赞点踩

    前端样式:可以直接去别人网站拷贝html代码,改改自己用

    {#    点赞点踩前端样式#}
            
    {{ article_obj.up_num }}
    {{ article_obj.down_num }}

    js代码:

    DjangoBBS项目功能拆分_第16张图片

    
    

    后端:

    urls.py:

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

    views.py:

    # 点赞点踩
    import json
    from django.contrib import auth
    from django.db.models import F
    def up_or_down(request):
        bank_dic = {'code': 1000, 'msg': ''}
        if request.is_ajax():
            article_id = request.POST.get('article_id')
            # 注意:前端返回来的bool值是str形式。拿到是点赞还是点踩   赞True 踩false
            is_up = request.POST.get('is_up')
            is_up = json.loads(is_up)  # 转成python形式的bool值
            '''
            1.必须是登录的用户才能点赞点踩,判断用户是否登录
            2.判断当前文章是否是用户自己写的,自己不能给自己点赞点踩
            3.当前用户是否已经给文章点过赞或踩了
            4.操作数据库---操作两张表,优化表字段
            '''
            # 1.判断用户是否已登录
            if request.user.is_authenticated():
                # 2.拿到当前文章,从文章里拿到当前用户,和登录的用户比较。如果用户一样,则证明是自己写的文章,不能点赞踩
                article_obj = models.Article.objects.filter(pk=article_id).first()
                if not article_obj.blog.userinfo.pk == request.user.pk:
                    # 3.判断当前用户是否已经给当前文章点过赞或踩了。到点赞点踩表中查询是否有当前用户的记录,如果有,则证明当前用户已经点过了
                    is_click = models.UpAndDown.objects.filter(user=request.user.pk, article=article_id)
                    if not is_click:
                        # 用户没电点过,操作表数据.第一张表
                        # 点赞给点赞字段+1
                        if is_up:
                            models.Article.objects.filter(pk=article_id).update(up_num=F('up_num') + 1)
                            bank_dic['msg'] = '点赞成功'
                        # 点踩给点踩字段+1
                        else:
                            models.Article.objects.filter(pk=article_id).update(down_num=F('down_num') + 1)
                            bank_dic['msg'] = '点踩成功'
                        # 操作表数据,第二张表
                        models.UpAndDown.objects.create(user=request.user, article=article_obj, is_up=is_up)
                    else:
                        bank_dic['code'] = 2000
                        bank_dic['msg'] = '你已经点过了'
                else:
                    bank_dic['code'] = 3000
                    bank_dic['msg'] = '不能给自己点'
            else:
                bank_dic['code'] = 4000
                bank_dic['msg'] = '请先登录'
            return JsonResponse(bank_dic)
    

    17、模板字符串

    文章评论零时渲染:

    //定义全局变量
            var parentId = null;
            // 文章评论js代码
            $('#id_comment').click(function () {
                var conTent = $('#id_content').val();
                // 如果是根评论不处理,如果是子评论需要处理,将@jeff 切割
                // @jeff 萨尔
                if (parentId) {
                    //切割方式  获取第一个\n对应的索引
                    var indexN = conTent.indexOf('\n') + 1  //顾头不顾尾
                    // 按照获取的索引切割
                    conTent = conTent.slice(indexN)  //将indexN之前的全部切除,中保留之后的
                }
                $.ajax({
                    url: '{% url "comment" %}',
                    type: 'post',
                    data: {
                        "article_id":{{ article_obj.pk }},
                        "content": conTent,
                        "csrfmiddlewaretoken": '{{ csrf_token }}',
                        "parent_id": parentId
                    },
                    success: function (data) {
                        if (data.code === 1000) {
    
                            // 临时渲染评论内容
                            var UserName = '{{ request.user.username }}';
                            var conTent = $('#id_content').val();
                            // 将内容临时渲染到ul标签内
                            var temp = `
                            
  • ${UserName}
    ${conTent}
  • `; $('.list-group').append(temp); // 将获取用户输入评论的内容框清空 $('#id_content').val(''); // 将全局的parentId清空,否则parentId后续一直有值,就一直是子评论 parentId = null } } }) });

    DjangoBBS项目功能拆分_第17张图片

    18、KindEditor编辑器使用

    看官方文档

    DjangoBBS项目功能拆分_第18张图片

    DjangoBBS项目功能拆分_第19张图片

    前端内容:

    {% extends 'backend/backend_base.html' %}
    {% block article %}
        

    添加文章

    {% csrf_token %}

    标题

    内容(使用kindeditor编辑器)

    文章标签

    {% for tag in tag_list %} {{ tag.name }} {% endfor %}

    文章分类

    {% for category in category_list %} {{ category.name }} {% endfor %}

    {% endblock %}

    后端代码:

    urls.py:

    # 添加文章
    url(r'^add_article/', views.add_article, name='add_article'),
    

    views.py:

    # 添加随笔
    from bs4 import BeautifulSoup
    @login_required
    def add_article(request):
        if request.method == 'POST':
            # 获取从前端页面传来的文章数据
            title = request.POST.get('title')
            content = request.POST.get('content')
            tag_list = request.POST.get('tag')
            category_id = request.POST.get('category')
            # 先生成一个该模块beautifulsoup4的对象
            soup = BeautifulSoup(content, 'html.parser')
            for tag in soup.find_all():
                # 筛选除script标签直接删除,避免XSS攻击
                if tag.name == 'script':
                    tag.decompose()  # 删除该标签
    
            # desc = content[0:150]   # 截取文章简介,错误示范。会从html代码截取
            desc = soup.text[0:150]  # 通过模块处理,直接从内容截取
            # 写入数据
            article_obj = models.Article.objects.create(title=title, desc=desc, content=str(soup), category_id=category_id, blog=request.user.blog)
            # 手动操作文章与标签的第三张表
            # 用批量插入数据 bulk_create
            b_list = []
            for tag_id in tag_list:
                b_list.append(models.Article2Tag(article=article_obj, tag_id=tag_id))
            models.Article2Tag.objects.bulk_create(b_list)
            return redirect(reverse('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())
    

    你可能感兴趣的:(DjangoBBS项目功能拆分)