BBS论坛项目

文章目录

      • 1、项目开发流程
      • 2、博客项目需求
      • 3、bbs项目表设计
      • 4、数据库表创建及同步
      • 5、注册
        • 5.1、注册form组件
        • 5.2、注册功能页面
        • 5.3、头像实时显示
        • 5.4、注册路由配置
        • 5.5、注册功能视图
        • 5.6、注册功能错误渲染
        • 5.7、用户名变化校验
          • 5.7.1、视图函数
          • 5.7.2、用户名变化前端校验
      • 6、登录
        • 6.1、登录页面
        • 6.2、 图片验证码
        • 6.3、登录功能
          • 6.3.1、视图函数
          • 6.3.2、模板页面
      • 7、退出
        • 7.1、视图函数
      • 8、首页
        • 8.1、博客首页导航条
          • 8.1.1、视图函数
          • 8.1.2、模板页面
        • 8.2、首页页面搭建(轮播图)
          • 8.2.1、视图函数
          • 8.2.2、前端
        • 8.3、首页文章显示
          • 8.3.1、视图函数
          • 8.3.2、前端
      • 9、admin后台管理
      • 10、media及头像显示
      • 11、个人站点文章显示
        • 11.1、路由
        • 11.2、视图函数
        • 11.3、前端
      • 12、点赞点踩功能
        • 12.1、视图函数
        • 12.2、前端
      • 13、评论功能
        • 13.1、视图函数
        • 13.2、前端
      • 14、后台管理页面搭建
        • 14.1、base.html
        • 14.2、后台首页
        • 14.3、视图函数
      • 15、新增文章,处理xss攻击
        • 15.1、前端
        • 15.2、视图函数
      • 16、修改文章
        • 16.1、前端
        • 16.2、视图函数
      • 17、修改头像
        • 17.1、前端
        • 17.2、视图函数

1、项目开发流程

1、互联网项目(产品经理提需求),传统行业项目(客户提需求)
2、项目开发流程
   需求分析(组长,项目经理,产品经理)
   原型设计(产品经理)
   美工UI切图
   设计程序,数据库(上面两个可以跟设计数据库同步操作)
   分任务开发(张三写用户相关,李四写订单相关),多人协同开发(git,svn)
   假设过了三个月所有任务开发完了
   测试(专门的测试)
   上线
3、项目开发模式
   瀑布模式
   敏捷开发

2、博客项目需求

多人博客
博客首页
登录: 图片验证码
注册: 上传头像
自己有自己的个人站点(根据分类,标签,时间,过滤文章)
自己的后台管理
	发表博客(富文本编辑器,xss攻击处理)
	查看,删除
文章分类
随笔档案
文章标签
文章
文章详情
评论(根评论,子评论)
点赞点踩

3、bbs项目表设计

# 表设计(8张)
1、用户表: UserInfo表
2、博客表: Blog表
3、分类表: Category
4、标签表: Tag
5、文章: Article(文章和详情一个表)
6、评论: Comment
7、点赞点踩表: UpAndDown
8、文章标签中间表
    
# 表中字段
1、用户表: UserInfo表(通过继承auth_user扩展)
	phone
	avatar(头像)
    blog(关联字段)
    
2、博客表: Blog表
	title
    name
    style
    
3、分类表: Category
	name
    blog
    
4、标签表: Tag
	name
	blog
	
5、文章: Article(文章和详情一个表)
	title
    desc
    content
    create_time
    blog
    category
    tag(多对多)

6、评论: Comment
	user
    article
    create_time
    content
    
7、点赞点踩表: UpAndDown
	user
    article
    is_up
    create_time

4、数据库表创建及同步

models.py

from django.db import models

from django.contrib.auth.models import AbstractUser


class UserInfo(AbstractUser):
    phone = models.CharField(max_length=32, verbose_name='手机号')
    # upload_to文件上传以后存放的路径
    # FileField本质是varchar类型
    avator = models.FileField(upload_to='avator/', default='avator/default.png')
    blog = models.OneToOneField(to='Blog', on_delete=models.CASCADE, null=True)


class Blog(models.Model):
    title = models.CharField(max_length=32, verbose_name='博客标题')
    name = models.CharField(max_length=32, verbose_name='博客描述')
    style = models.CharField(max_length=32, verbose_name='博客样式')


class Tag(models.Model):
    name = models.CharField(max_length=32, verbose_name='标签名称')
    blog = models.ForeignKey(to='Blog', on_delete=models.CASCADE)


class Category(models.Model):
    name = models.CharField(max_length=32, verbose_name='分类名称')
    blog = models.ForeignKey(to='Blog', on_delete=models.CASCADE)


class Article(models.Model):
    title = models.CharField(max_length=32, verbose_name='文章标题')
    desc = models.CharField(max_length=128, verbose_name='文章描述')
    content = models.TextField(verbose_name='文章内容')
    create_time = models.DateTimeField(auto_now_add=True, verbose_name='文章创建时间')

    # 关联字段
    blog = models.ForeignKey(to='Blog', on_delete=models.CASCADE)
    category = models.ForeignKey(to='Category', on_delete=models.CASCADE)
    tag = models.ManyToManyField(to='Tag', through='TagToArticle', through_fields=('article', 'tag'))


class TagToArticle(models.Model):
    tag = models.ForeignKey(to='Tag', on_delete=models.CASCADE)
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE)


class UpAndDown(models.Model):
    user = models.ForeignKey(to='UserInfo', on_delete=models.CASCADE)
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE)
    is_up = models.BooleanField(verbose_name='是否是点赞')
    create_time = models.DateTimeField(auto_now_add=True, verbose_name='点赞点踩创建时间')


class Comment(models.Model):
    user = models.ForeignKey(to='UserInfo', on_delete=models.CASCADE)
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE)
    content = models.CharField(max_length=255, verbose_name='评论内容')
    create_time = models.DateTimeField(auto_now_add=True, verbose_name='评论创建时间')
    # 存父评论的id号
    comments = models.ForeignKey(to='self', on_delete=models.CASCADE)

执行迁移文件的两条命令

python manage.py makemigrations
python manage.py migrate

5、注册

5.1、注册form组件

blog_forms.py

from django import forms
from django.forms import widgets

from blog.models import UserInfo


class BlogForm(forms.Form):
    username = forms.CharField(min_length=3, max_length=18, required=True, label='用户名',
                               error_messages={'min_length': '最小为3位', 'max_length': '最大为18位', 'required': '该字段必填'},
                               widget=widgets.TextInput(attrs={'class': 'form-control'}))

    password = forms.CharField(min_length=3, max_length=18, required=True, label='密码',
                               error_messages={'min_length': '最小为3位', 'max_length': '最大为18位', 'required': '该字段必填'},
                               widget=widgets.PasswordInput(attrs={'class': 'form-control'}))

    re_password = forms.CharField(min_length=3, max_length=18, required=True, label='确认密码',
                                  error_messages={'min_length': '最小为3位', 'max_length': '最大为18位', 'required': '该字段必填'},
                                  widget=widgets.PasswordInput(attrs={'class': 'form-control'}))

    email = forms.EmailField(required=True, label='邮箱',
                             error_messages={'min_length': '最小为3位', 'max_length': '最大为18位', 'required': '该字段必填',
                                             'invalid': '邮箱不合法'},
                             widget=widgets.TextInput(attrs={'class': 'form-control'}))

    def clean_username(self):
        username = self.cleaned_data.get('username')
        count = UserInfo.objects.filter(username=username).count()
        if count:
            self.add_error('username', '用户名已存在')
        else:
            return username

    def clean(self):
        pwd = self.cleaned_data.get('password')
        re_pwd = self.cleaned_data.get('re_password')
        if pwd == re_pwd:
            return self.cleaned_data
        else:
            self.add_error('re_password', '两次密码输入不一致')

setting中配置媒体资源

# 由于FileField会自动保存文件,所以默认以MEDIA_ROOT开始往下找,如果没有配,默认以根路径开始
# 以后上传的文件,都是从media路径往下找
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

5.2、注册功能页面

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    {% load static %}
    <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
    <script src="{% static 'jquery-3.3.1/jquery-3.3.1.min.js' %}"></script>
    <script src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script>
    <title>注册</title>
</head>
<body>

<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1 class="text-center">用户注册</h1>
            <form action="" id="id_form">
                {% csrf_token %}
                {% for form in forms %}
                    <div class="form-group">
                        <label for="{{ form.auto_id }}">{{ form.label }}</label>
                        {{ form }}
                        <span class="pull-right text-danger"></span>
                    </div>
                {% endfor %}
                <div class="form-group">
                    <label for="id_myfile">头像
                        <img src="/static/img/default.png" id="id_img" alt=""
                             style="width: 80px;height: 80px;margin-top: 10px;margin-left: 20px;">
                    </label>

                    <input type="file" id="id_myfile" name="avator" style="display: none;">
                </div>
                <div class="text-center"><input type="button" class="btn btn-danger" id='id_submit' value="提交"></div>
            </form>
        </div>
    </div>
</div>

<script>
    $(function () {
        // 获取头像信息
        $('#id_myfile').on('change', function () {
            var filereader = new FileReader();
            filereader.readAsDataURL($(this)[0].files[0]);
            filereader.onload = function () {
                $('#id_img').attr('src', filereader.result);
            }
        });

        // 检测用户名是否存在
        $('#id_username').on('blur', function () {
            var _this = $(this);
            $.ajax({
                url: '/blog/check_username/',
                method: 'post',
                data: {
                    'username': $('#id_username').val(),
                    'csrfmiddlewaretoken': $("[name='csrfmiddlewaretoken']").val()
                },
                success: function (data) {
                    if (data !== 100) {
                        _this.next('span').html(data.msg);
                    }
                }
            })
        });

        // 提交数据
        $('#id_submit').on('click', function () {
            var formdata = new FormData();
            formdata.append('avator', $('#id_myfile')[0].files[0]);
            res = $('#id_form').serializeArray();
            $.each(res, function (key, value) {
                formdata.append(value['name'], value['value'])
            });

            // console.log(formdata);

            $.ajax({
                url: '',
                method: 'post',
                data: formdata,
                processData: false,
                contentType: false,
                success: function (data) {
                    if (data.code == 100) {
                        console.log(data.msg);
                        location.href = data.url;
                    } else {
                        $.each(data.error, function (key, value) {
                            $('#id_' + key).next('span').text(value[0]).parent().addClass('has-error')
                        });

                        setTimeout(function () {
                            $('.text-danger').text('').parent().removeClass('has-error')
                        }, 3000);
                    }
                }
            })
        });
    })
</script>
</body>
</html>

5.3、头像实时显示

// 获取头像信息
$('#myfile').on('change', function () {
	// 借助于文件阅读器
    var filereader = new FileReader();
    // 把图片读到filereader对象中
    filereader.readAsDataURL($(this)[0].files[0]);
    // 文件完全读到文件阅读器以后再执行
    filereader.onload = function () {
        $('#img').attr('src', filereader.result);
    }
});

5.4、注册路由配置

总路由

path('blog/', include(('blog.urls','blog'), namespace='blog')),

blog路由

path('register/', views.Register.as_view(), name='register'),
path('login/', views.Login.as_view(), name='login'),

5.5、注册功能视图

from django.shortcuts import render, HttpResponse
from django.http import JsonResponse
from django.views import View
from blog.blog_forms import BlogForm
from blog.models import UserInfo

class Register(View):
    def get(self, request, *args, **kwargs):
        forms = BlogForm()
        return render(request, 'register.html', context={'forms': forms})

    def post(self, request, *args, **kwargs):
        res = {'code': 100, 'msg': 'success'}
        forms = BlogForm(request.POST)
        if forms.is_valid():
            data = forms.cleaned_data
            data.pop('re_password')
            files = request.FILES.get('avator')
            if files:
                data['avator'] = files
            UserInfo.objects.create_user(**data)
            res['url'] = '/blog/login/'
            return JsonResponse(res)
        else:
            res['code'] = 101
            res['msg'] = '数据验证失败'
            res['error'] = forms.errors
            return JsonResponse(res)

5.6、注册功能错误渲染

// 提交数据
$('#id_submit').on('click', function () {
    var formdata = new FormData();
    formdata.append('avator', $('#id_myfile')[0].files[0]);
    res = $('#id_form').serializeArray();
    $.each(res, function (key, value) {
        formdata.append(value['name'], value['value'])
    });

    $.ajax({
        url: '',
        method: 'post',
        data: formdata,
        processData: false,
        contentType: false,
        success: function (data) {
            if (data.code == 100) {
                console.log(data.msg);
                location.href = data.url;
            } else {
                $.each(data.error, function (key, value) {
                    $('#id_' + key).next('span').text(value[0]).parent().addClass('has-error')
                });
				// 过3秒钟,错误信息清除,在匿名函数中执行
                setTimeout(function () {
                    $('.text-danger').text('').parent().removeClass('has-error')
                }, 3000);
            }
        }
    })
});

5.7、用户名变化校验

5.7.1、视图函数
def check_username(request):
    res = {'code': 100, 'msg': None}
    if request.is_ajax():
        username = request.POST.get('username')
        count = UserInfo.objects.filter(username=username).first()
        if count:
            res['code'] = 102
            res['msg'] = '该用户已存在'
    return JsonResponse(res)
5.7.2、用户名变化前端校验
// 检测用户名是否存在
// 当光标不再username控件上就发送ajax请求,去后台查询
$('#id_username').on('blur', function () {
    var _this = $(this);
    $.ajax({
        url: '/blog/check_username/',
        method: 'post',
        data: {
            'username': $('#id_username').val(),
            'csrfmiddlewaretoken': $("[name='csrfmiddlewaretoken']").val()
        },
        success: function (data) {
            if (data !== 100) {
                _this.next('span').html(data.msg);
            }
        }
    })
});

6、登录

6.1、登录页面


<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    {% load static %}
    <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
    <script src="{% static 'jquery-3.3.1/jquery-3.3.1.min.js' %}">script>
    <script src="{% static 'bootstrap/js/bootstrap.min.js' %}">script>
    <title>登录title>
head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1 class="text-center">用户登录h1>
            <form action="" id="form">
                {% csrf_token %}
                <div class="form-group">
                    <label for="id_username">用户名label>
                    <input type="text" name="id_username" id="id_username" class="form-control">
                div>

                <div class="form-group">
                    <label for="id_password">密码label>
                    <input type="password" name="id_password" id="id_password" class="form-control">
                div>

                <div class="form-group">
                    <label for="id_code">验证码label>
                    <div class="row">
                        <div class="col-md-6">
                            <input type="text" name="id_code" id="id_code" class="form-control">
                        div>
                        <div class="col-md-6">
                            <img src="/blog/get_code/" alt="" width="450px" height="30px" id="id_img">
                        div>
                    div>
                div>

                <div class="form-group text-center">
                    <input type="button" id="id_submit" class="btn btn-danger" value="提交">
                div>
            form>
        div>
    div>
div>
<script>
    $(function () {
        // 点击切换图形验证码
        $('#id_img').on('click', function () {
            $(this).attr('src', $(this).attr('src') + '?' + Math.random())
        });

        $('#id_submit').on('click', function () {
            formdata = new FormData();
            $.each($('#form').serializeArray(), function (key, value) {
                formdata.append(value.name, value.value)
            });

            $.ajax({
                url: '/blog/login/',
                method: 'post',
                data: formdata,
                processData: false,
                contentType: false,
                success: function (data) {
                    if (data.code !== 100) {
                        alert(data.msg)
                    } else {
                        location.href = data['url']
                    }
                }
            })
        });
    })
script>
body>
html>

6.2、 图片验证码

def get_code(request):
	# 生成一张图片(Image模块下的new函数,返回一个Image对象)
    img = Image.new('RGB', (450, 30), generate_rgb())
    # 把图片放到画板上
    img_draw = ImageDraw.Draw(img)
    img_font = ImageFont.truetype('./static/font/ss.TTF', 25)

    code_str = ''
    for i in range(5):
        lower_char = chr(random.randint(97, 122))
        upper_char = chr(random.randint(65, 90))
        num_char = str(random.randint(0, 9))
        res = random.choice([lower_char, upper_char, num_char])
        code_str += res
        img_draw.text((i * 70 + 40, i), str(res), generate_rgb(), font=img_font)

        for i in range(170):
            img_draw.point([random.randint(0, 450), random.randint(0, 30)], fill=generate_rgb())
    # 把验证码存到session中
    request.session['code'] = code_str
    print(code_str)
    # 图片保存(写到内存中)
    fp = BytesIO()
    img.save(fp, 'png')
    # 把内容全取出来
    data = fp.getvalue()
    return HttpResponse(data)

6.3、登录功能

6.3.1、视图函数
class Login(View):
    def get(self, request, *args, **kwargs):
        return render(request, 'login.html')

    def post(self, request, *args, **kwargs):
        res = {'code': 100, 'msg': None}
        if request.is_ajax():
            username = request.POST.get('id_username')
            password = request.POST.get('id_password')
            code = request.POST.get('id_code')
            if request.session['code'].lower() == code.lower():
                user = auth.authenticate(username=username, password=password)
                if user:
                    auth.login(request, user)
                    res['msg'] = '登录成功'
                    res['url'] = '/blog/index/'
                else:
                    res['code'] = 103
                    res['msg'] = '账号或密码有误'
            else:
                res['code'] = 101
                res['msg'] = '验证码错误'
            return JsonResponse(res)
        else:
            res['code'] = 102
            res['msg'] = '请求有误'
            return JsonResponse(res)
6.3.2、模板页面

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    {% load static %}
    <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
    <script src="{% static 'jquery-3.3.1/jquery-3.3.1.min.js' %}">script>
    <script src="{% static 'bootstrap/js/bootstrap.min.js' %}">script>
    <title>登录title>
head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1 class="text-center">用户登录h1>
            <form action="" id="form">
                {% csrf_token %}
                <div class="form-group">
                    <label for="id_username">用户名label>
                    <input type="text" name="id_username" id="id_username" class="form-control">
                div>

                <div class="form-group">
                    <label for="id_password">密码label>
                    <input type="password" name="id_password" id="id_password" class="form-control">
                div>

                <div class="form-group">
                    <label for="id_code">验证码label>
                    <div class="row">
                        <div class="col-md-6">
                            <input type="text" name="id_code" id="id_code" class="form-control">
                        div>
                        <div class="col-md-6">
                            <img src="/blog/get_code/" alt="" width="450px" height="30px" id="id_img">
                        div>
                    div>
                div>

                <div class="form-group text-center">
                    <input type="button" id="id_submit" class="btn btn-danger" value="提交">
                div>
            form>
        div>
    div>
div>
<script>
    $(function () {
        // 点击切换图形验证码
        $('#id_img').on('click', function () {
            $(this).attr('src', $(this).attr('src') + '?' + Math.random())
        });

        $('#id_submit').on('click', function () {
            formdata = new FormData();
            $.each($('#form').serializeArray(), function (key, value) {
                formdata.append(value.name, value.value)
            });

            $.ajax({
                url: '/blog/login/',
                method: 'post',
                data: formdata,
                processData: false,
                contentType: false,
                success: function (data) {
                    if (data.code !== 100) {
                        alert(data.msg)
                    } else {
                        location.href = data['url']
                    }
                }
            })
        });
    })
script>
body>
html>

7、退出

7.1、视图函数

def logout(request):
    auth.logout(request)
    return redirect(reverse('blog:index'))

8、首页

8.1、博客首页导航条

8.1.1、视图函数
class Index(View):
    def get(self, request, *args, **kwargs):
        return render(request, 'index.html')
8.1.2、模板页面

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    {% load static %}
    <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
    <script src="{% static 'jquery-3.3.1/jquery-3.3.1.min.js' %}">script>
    <script src="{% static 'bootstrap/js/bootstrap.min.js' %}">script>
    <title>首页title>
head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="header">
            <nav class="navbar navbar-default">
                <div class="container-fluid">
                    
                    <div class="navbar-header">
                        <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                                data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                            <span class="sr-only">Toggle navigationspan>
                            <span class="icon-bar">span>
                            <span class="icon-bar">span>
                            <span class="icon-bar">span>
                        button>
                        <a class="navbar-brand" href="{% url 'blog:index' %}">博客公园a>
                    div>

                    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                        <ul class="nav navbar-nav">
                            <li><a href="{% url 'blog:index' %}">首页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="Search">
                            div>
                            <button type="submit" class="btn btn-default">Submitbutton>
                        form>
                        {% if request.user.is_authenticated %}
                            <ul class="nav navbar-nav navbar-right">
                                <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="#" id="change_pwd" data-toggle="modal"
                                               data-target=".bs-example-modal-lg">修改密码a>li>
                                        <li><a href="#">修改头像a>li>
                                        <li><a href="{% url 'blog:logout' %}">退出a>li>
                                    ul>
                                li>
                            ul>
                        {% else %}
                            <ul class="nav navbar-nav navbar-right">
                                <li><a href="{% url 'blog:register' %}">注册a>li>
                                <li><a href="{% url 'blog:login' %}">登录a>li>
                            ul>
                        {% endif %}

                    div>
                div>
            nav>
        div>
        <div class="body">
            <div class="content_left col-md-2">111div>
            <div class="content_middle col-md-7">222div>
            <div class="content_right col-md-3">333div>
        div>
    div>
div>
body>
html>

8.2、首页页面搭建(轮播图)

8.2.1、视图函数
def get_banner(request):
    banner_info = [
        {'img_url': '/static/img/glasses-banner1.jpg', 'desc': '广告位出租', 'target_url': 'http://www.baidu.com'},
        {'img_url': '/static/img/glasses-banner2.jpg', 'desc': '点我买苹果手机12', 'target_url': 'http://www.sina.com'},
        {'img_url': '/static/img/glasses-banner3.jpg', 'desc': '北京到上海特价飞机票', 'target_url': 'http://www.bilibi.com'},
    ]
    return JsonResponse(banner_info, safe=False)
8.2.2、前端
<div class="content_middle col-md-7">
    <div class="lbt">
        <div id="carousel-example-generic" class="carousel slide" data-ride="carousel">
            
            <ol class="carousel-indicators">
                <li data-target="#carousel-example-generic" data-slide-to="0" class="active">li>
                <li data-target="#carousel-example-generic" data-slide-to="1">li>
                <li data-target="#carousel-example-generic" data-slide-to="2">li>
            ol>

            
            <div class="carousel-inner" role="listbox">
                <div class="item active">
                    <img class="banner-img" src="xxx" alt="...">
                    <div class="carousel-caption">
                        <a href="" class="link_a">xxxa>
                    div>
                div>
                <div class="item">
                    <img class="banner-img" src="xxx" alt="...">
                    <div class="carousel-caption">
                        <a href="" class="link_a">xxxa>
                    div>
                div>
                <div class="item">
                    <img class="banner-img" src="xxx" alt="...">
                    <div class="carousel-caption">
                        <a href="" class="link_a">xxxa>
                    div>
                div>
            div>

            
            <a class="left carousel-control" href="#carousel-example-generic" role="button"
               data-slide="prev">
                <span class="glyphicon glyphicon-chevron-left" aria-hidden="true">span>
                <span class="sr-only">Previousspan>
            a>
            <a class="right carousel-control" href="#carousel-example-generic" role="button"
               data-slide="next">
                <span class="glyphicon glyphicon-chevron-right" aria-hidden="true">span>
                <span class="sr-only">Nextspan>
            a>
        div>
    div>
    <div class="article">
        {% for article in article_list %}
            <div class="ever" style="margin-top: 15px">
                <h4 class="media-heading">{{article.title}}h4>
                <div class="media">
                    <div class="media-left">
                        <a href="#">
                            <img class="media-object" src="media/{{ article.blog.userinfo.avator }}" alt="" height="60px;" width="60px;">
                        a>
                    div>
                    <div class="media-body">
                        {{ article.desc }}
                    div>
                    <div class="article_bottom">
                        <span><a href="{% url 'blog:my_site' article.blog.userinfo.username %}">{{ article.blog.userinfo.username }}a>span>
                        <span>{{ article.create_time|date:'Y-m-d H:i:s' }}span>
                        <span class="glyphicon glyphicon-thumbs-up"> 0span>
                        <span class="glyphicon glyphicon-briefcase"> 10span>
                    div>
                div>
                <hr>
            div>
        {% endfor %}
    div>
div>

<script>
$.ajax({
    url: '/blog/get_banner/',
    method: 'post',
    data: {'csrfmiddlewaretoken': $('[name="csrfmiddlewaretoken"]').val()},
    success: function (data) {
        $.each(data, function (key, value) {
            $('.banner-img')[key].src = value.img_url;
            $('.link_a')[key].href = value.target_url;
            $($('.link_a')[key]).html(value['desc']);
        })
    }
})
script>

8.3、首页文章显示

8.3.1、视图函数
class Index(View):
    def get(self, request, *args, **kwargs):
        banner = ['/static/img/glasses-banner1.jpg', '/static/img/glasses-banner2.jpg',
                  '/static/img/glasses-banner3.jpg']

        article_list = models.Article.objects.all()
        return render(request, 'index.html', locals())

    def post(self, request, *args, **kwargs):
        pass
8.3.2、前端

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    {% load static %}
    <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
    <style>
        .article_bottom span {
            margin-right: 10px;
        }
    style>
    <script src="{% static 'jquery-3.3.1/jquery-3.3.1.min.js' %}">script>
    <script src="{% static 'bootstrap/js/bootstrap.min.js' %}">script>
    <title>首页title>
head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="header">
            <nav class="navbar navbar-default">
                <div class="container-fluid">
                    
                    <div class="navbar-header">
                        <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                                data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                            <span class="sr-only">Toggle navigationspan>
                            <span class="icon-bar">span>
                            <span class="icon-bar">span>
                            <span class="icon-bar">span>
                        button>
                        <a class="navbar-brand" href="/">博客公园a>
                    div>

                    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                        <ul class="nav navbar-nav">
                            <li><a href="/">首页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="Search">
                            div>
                            <button type="submit" class="btn btn-default">Submitbutton>
                        form>
                        {% if request.user.is_authenticated %}
                            <ul class="nav navbar-nav navbar-right">
                                <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="#" id="change_pwd" data-toggle="modal"
                                               data-target=".bs-example-modal-lg">修改密码a>li>
                                        <li><a href="#">修改头像a>li>
                                        <li><a href="{% url 'blog:logout' %}">退出a>li>
                                    ul>
                                li>
                            ul>
                        {% else %}
                            <ul class="nav navbar-nav navbar-right">
                                <li><a href="{% url 'blog:register' %}">注册a>li>
                                <li><a href="{% url 'blog:login' %}">登录a>li>
                            ul>
                        {% endif %}

                    div>
                div>
            nav>
        div>
        <div class="body">
            <div class="content_left col-md-2">
                <div class="panel panel-primary">
                    <div class="panel-heading">
                        <h3 class="panel-title">招商广告h3>
                    div>
                    <div class="panel-body">
                        招商广告栏位出租
                    div>
                div>

                <div class="panel panel-danger">
                    <div class="panel-heading">
                        <h3 class="panel-title">北京时间h3>
                    div>
                    <div class="panel-body">
                        北京时间x点x分
                    div>
                div>

                <div class="panel panel-primary">
                    <div class="panel-heading">
                        <h3 class="panel-title">招商广告h3>
                    div>
                    <div class="panel-body">
                        招商广告栏位出租
                    div>
                div>

                <div class="panel panel-danger">
                    <div class="panel-heading">
                        <h3 class="panel-title">北京时间h3>
                    div>
                    <div class="panel-body">
                        北京时间x点x分
                    div>
                div>

            div>
            <div class="content_middle col-md-7">
                <div class="lbt">
                    <div id="carousel-example-generic" class="carousel slide" data-ride="carousel">
                        
                        <ol class="carousel-indicators">
                            <li data-target="#carousel-example-generic" data-slide-to="0" class="active">li>
                            <li data-target="#carousel-example-generic" data-slide-to="1">li>
                            <li data-target="#carousel-example-generic" data-slide-to="2">li>
                        ol>

                        
                        <div class="carousel-inner" role="listbox">
                            <div class="item active">
                                <img class="banner-img" src="xxx" alt="...">
                                <div class="carousel-caption">
                                    <a href="" class="link_a">xxxa>
                                div>
                            div>
                            <div class="item">
                                <img class="banner-img" src="xxx" alt="...">
                                <div class="carousel-caption">
                                    <a href="" class="link_a">xxxa>
                                div>
                            div>
                            <div class="item">
                                <img class="banner-img" src="xxx" alt="...">
                                <div class="carousel-caption">
                                    <a href="" class="link_a">xxxa>
                                div>
                            div>
                        div>

                        
                        <a class="left carousel-control" href="#carousel-example-generic" role="button"
                           data-slide="prev">
                            <span class="glyphicon glyphicon-chevron-left" aria-hidden="true">span>
                            <span class="sr-only">Previousspan>
                        a>
                        <a class="right carousel-control" href="#carousel-example-generic" role="button"
                           data-slide="next">
                            <span class="glyphicon glyphicon-chevron-right" aria-hidden="true">span>
                            <span class="sr-only">Nextspan>
                        a>
                    div>
                div>
                <div class="article">
                    {% for article in article_list %}
                        <div class="ever" style="margin-top: 15px">
                            <h4 class="media-heading">{{article.title}}h4>
                            <div class="media">
                                <div class="media-left">
                                    <a href="#">
                                        <img class="media-object" src="media/{{ article.blog.userinfo.avator }}" alt="" height="60px;" width="60px;">
                                    a>
                                div>
                                <div class="media-body">
                                    {{ article.desc }}
                                div>
                                <div class="article_bottom">
                                    <span><a href="{% url 'blog:my_site' article.blog.userinfo.username %}">{{ article.blog.userinfo.username }}a>span>
                                    <span>{{ article.create_time|date:'Y-m-d H:i:s' }}span>
                                    <span class="glyphicon glyphicon-thumbs-up"> 0span>
                                    <span class="glyphicon glyphicon-briefcase"> 10span>
                                div>
                            div>
                            <hr>
                        div>
                    {% endfor %}
                div>
            div>
            <div class="content_right col-md-3">
                <div class="panel panel-primary">
                    <div class="panel-heading">
                        <h3 class="panel-title">招商广告h3>
                    div>
                    <div class="panel-body">
                        招商广告栏位出租
                    div>
                div>

                <div class="panel panel-danger">
                    <div class="panel-heading">
                        <h3 class="panel-title">北京时间h3>
                    div>
                    <div class="panel-body">
                        北京时间x点x分
                    div>
                div>
            div>
            <div class="content_right col-md-3">
                <div class="panel panel-primary">
                    <div class="panel-heading">
                        <h3 class="panel-title">招商广告h3>
                    div>
                    <div class="panel-body">
                        招商广告栏位出租
                    div>
                div>

                <div class="panel panel-danger">
                    <div class="panel-heading">
                        <h3 class="panel-title">北京时间h3>
                    div>
                    <div class="panel-body">
                        北京时间x点x分
                    div>
                div>
            div>
        div>
    div>
div>

{% include 'change_pwd.html' %}
<script>
    $(function () {
        // 校验密码是否正确
        $('#old_password').blur(function (data) {
            var _this = $(this);
            $.ajax({
                url: '/blog/check_old_password/',
                method: 'post',
                data: {'old_password': $(this).val(), 'csrfmiddlewaretoken': $('[name="csrfmiddlewaretoken"]').val()},
                success: function (data) {
                    if (data.code !== 100) {
                        _this.next('span').html(data.msg).parent().addClass('has-error')
                    } else {
                        _this.next('span').html('').parent().removeClass('has-error')
                    }
                }
            })
        });

        // 修改密码
        $('#btn').on('click', function () {
            if ($('div').hasClass('has-error')) {
                return;
            }
            $.ajax({
                url: '/blog/update_password/',
                method: 'post',
                data: {
                    'new_password': $('#new_password').val(),
                    're_new_password': $('#re_new_password').val(),
                    'csrfmiddlewaretoken': $('[name="csrfmiddlewaretoken"]').val(),
                },
                success: function (data) {
                    if (data.code == 100) {
                        alert(data.msg);
                        location.reload();
                    } else {
                        $('#re_new_password').next().html(data.msg).parent().addClass('has-error')
                    }
                }
            })
        });

        // 获取banner图
        $.ajax({
            url: '/blog/get_banner/',
            method: 'post',
            data: {'csrfmiddlewaretoken': $('[name="csrfmiddlewaretoken"]').val()},
            success: function (data) {
                $.each(data, function (key, value) {
                    $('.banner-img')[key].src = value.img_url;
                    $('.link_a')[key].href = value.target_url;
                    $($('.link_a')[key]).html(value['desc']);
                })
            }
        })
    })
script>
body>
html>

9、admin后台管理

# 路由
path('admin/', admin.site.urls),

# admin.py中注册
from django.contrib import admin
from blog import models
admin.site.register(models.UserInfo)
admin.site.register(models.Article)
admin.site.register(models.Blog)
admin.site.register(models.Category)
admin.site.register(models.UpAndDown)
admin.site.register(models.Comment)
admin.site.register(models.Tag)
admin.site.register(models.TagToArticle)

# setting中配置国际化
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False

# 表名显示中文
class Meta:
    verbose_name_plural='标签表'
# 数据显示中文
def __str__(self):
   return self.name

10、media及头像显示

1、用户上传的头像能够在浏览器中访问到
2、默认情况下,static下的都能访问
3、media文件夹下的图片不能访问,需要手动打开
4、使用方式
	项目根路径下新建media文件夹
    setting中配置MEDIA_ROOT=os.path.join(BASE_DIR,'media')
    路由中配置:
    from django.urls import re_path
	from django.views.static import serve
	from django.conf import settings
    re_path('^media/(?P.*)$', serve,{'document_root': settings.MEDIA_ROOT}),

11、个人站点文章显示

11.1、路由

# 个人站点 访问tag/category/achive
re_path(r'^my_site/(?P\w+)/(?Ptag|category|achive)/(?P.*).html$', views.my_site,name='my_site'),
re_path(r'^my_site/(?P\w+)/', views.my_site, name='my_site'),

11.2、视图函数

def my_site(request, name, **kwargs):
    obj = UserInfo.objects.filter(username=name).first()
    if obj:

        article_list = models.Article.objects.filter(blog__userinfo__username=name)

        query = kwargs.get('query', None)
        if query == 'category':
            condition = kwargs.get('condition')
            article_list = article_list.filter(category_id=condition)
        elif query == 'tag':
            condition = kwargs.get('condition')
            article_list = article_list.filter(tagtoarticle__tag_id=condition)
        elif query == 'achive':
            year, month = kwargs.get('condition').split('/')
            article_list = article_list.filter(create_time__year=year, create_time__month=month)

        return render(request, 'my_site.html', locals())
    else:
        return render(request, 'error.html')

11.3、前端


{% extends 'base.html' %}

{% block content %}
    <div class="article">
        {% for article in article_list %}
            <div class="ever" style="margin-top: 15px">
                <h4 class="media-heading"><a href="{% url 'blog:article_detail' name article.id %}">{{ article.title }}a>h4>
                <div class="media">
                    <div class="media-body">
                        {{ article.desc }}
                    div>
                    <div class="article_bottom pull-right">
                        @posted by  
                        <span>{{ article.blog.userinfo.username }}span>
                        <span>{{ article.create_time|date:'Y-m-d H:i:s' }}span>
                        <span><i class="fa fa-hand-pointer-o fa-lg">i> {{ article.up_num }}span>
                        <span><i class="fa fa-comment fa-lg">i> {{ article.comment_num }}span>
                        <span><a href="">编辑a>span>
                    div>
                div>
                <hr>
            div>
        {% endfor %}
    div>
{% endblock %}



<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    {% load static %}
    <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
    <title>Documenttitle>
head>
<body>
<div class="error-panel server-error error-404 text-center">
    <img src="//static.hdslb.com/error/very_sorry.png">
    <div style="text-align: center; padding: 0 0 40px 0;">
        <a class="rollback-btn" style="padding: 0 20px; float: none;">返回上一页a>
    div>
div>
body>
html>

12、点赞点踩功能

12.1、视图函数

def up_and_down(request):
    res = {'code': 100, 'msg': None}
    if request.is_ajax():
        if request.user.is_authenticated:
            article_id = request.POST.get('article_id')
            obj = models.UpAndDown.objects.filter(user_id=request.user.id, article_id=article_id).first()
            if obj:
                res['code'] = 103
                res['msg'] = '您已经提交过'
            else:
                # 写数据库
                is_up = request.POST.get('is_up')
                is_up = json.loads(is_up)
                # 点赞点踩表增加数据
                models.UpAndDown.objects.create(is_up=is_up, user_id=request.user.id, article_id=article_id)
                # 文章表点赞点踩数量加1
                try:
                    with transaction.atomic():
                        if is_up:
                            models.Article.objects.filter(id=article_id).update(up_num=F('up_num') + 1)
                            res['msg'] = '点赞成功'
                        else:
                            models.Article.objects.filter(id=article_id).update(down_num=F('down_num') + 1)
                            res['msg'] = '点踩成功'
                        res['code'] = 100
                except Exception as e:
                    res['code'] = 104
                    res['msg'] = '数据异常'
        else:
            res['code'] = 102
            res['msg'] = '登录后再操作'
    else:
        res['code'] = 101
        res['msg'] = '非法操作'
    return JsonResponse(res)

12.2、前端

{% extends 'base.html' %}

{% block ext_css %}
    {% load static %}
    <link rel="stylesheet" href="{% static 'css/my_css.css' %}">
{% endblock %}

{% block content %}
    {% csrf_token %}
    <h2>{{ article.title }}h2>
    <hr>
    <div class="content" style="text-indent:2em;">{{ article.content }}div>
    {# 点赞点踩 #}
    <div class="upanddown clearfix">
        <div id="div_digg">
            <div class="diggit up_or_down">
                <span class="diggnum" id="digg_count">{{ article.up_num }}span>
            div>
            <div class="buryit up_or_down">
                <span class="burynum" id="bury_count">{{ article.down_num }}span>
            div>
            <div class="clear">div>
            <div class="diggword" id="digg_tips" style="color: red;">
            div>
        div>
    div>
    <hr>
    {# 评论 #}
    <div class="comment_list">
        评论列表
        <ul class="list-group">
            {% for comment in comment_list %}
                <li class="list-group-item">
                    <div>
                        <span>#{{ forloop.counter }}楼span>
                        <span>{{ comment.create_time|date:'Y-m-d H:i:s' }}span>
                        <span><a
                                href="{% url 'blog:my_site' comment.user.username %}">{{ comment.user.username }}a>span>
                        <span class="replay pull-right" parent="{{ comment.pk }}"
                              username="{{ comment.user.username }}"><a>回复a>span>

                        {% if comment.comments_id %}
                            <p>@{{ comment.article.blog.userinfo.username }}p>
                            <p><span>{{ comment.content }}span>p>
                        {% else %}
                            <p><span>{{ comment.content }}span>p>
                        {% endif %}

                    div>
                li>
            {% endfor %}
        ul>
    div>
    <hr>
    {% if request.user.is_authenticated %}
        <div class="comment" style="margin-top: 15px;">
            <div class="form-group">
                <div>发表评论div>
                <textarea name="content" id="content_textarea" cols="100" rows="15">textarea>
            div>
            <div class="form-group">
                <button class="btn btn-info" id="id_btn">提交button>
            div>
        div>
    {% else %}
        <div>登录后才能发表评论,立即 <a href="{% url 'blog:login' %}">登录a><a href="{% url 'blog:register' %}">注册a>, 访问 <a
                href="/">网站首页a>div>
    {% endif %}

{% endblock %}

{% block ext_js %}
    <script>
        $(function () {
            parent_id = '';
            $('.up_or_down').on('click', function () {
                var _this = $(this);
                is_up = true;
                if ($(this).children().hasClass('burynum')) {
                    is_up = false;
                }
                $.ajax({
                    url: '/blog/up_and_down/',
                    method: 'post',
                    data: {
                        article_id: '{{ article.id }}',
                        is_up: is_up,
                        csrfmiddlewaretoken: $('input[name="csrfmiddlewaretoken"]').val(),
                    },
                    success: function (data) {
                        if (data.code == 100) {
                            num = Number(_this.children('span').html()) + 1;
                            _this.children('span').html(num);
                        }
                        $('#digg_tips').html(data.msg);
                    }
                })
            });

            $('#id_btn').on('click', function () {
                let content = $('#content_textarea').val();
                if (parent_id) {
                    // 子评论
                    str = content.indexOf('\n') + 1;
                    content = content.slice(str);
                }

                $.ajax({
                    url: '/blog/commit_comment/',
                    method: 'post',
                    data: {
                        article_id: '{{ article.id }}',
                        content: content,
                        parent_id: parent_id,
                        csrfmiddlewaretoken: $('input[name="csrfmiddlewaretoken"]').val(),
                    },
                    success: function (data) {
                        content_text = '';
                        var username = data.username;
                        var content = $('#content_textarea').val();
                        if (data.code == 100) {

                            if (parent_id) {
                                var parent_name = data.parent_name;
                                content_text = `
  • ${username}

    ${parent_name}

    ${content}
  • `
    ; } else { content_text = `
  • ${username}
    ${content}
  • `
    ; } $('#content_textarea').val(''); $('.list-group').append(content_text); parent_id = ''; } } }) }); // replay功能 $('.replay').on('click', function () { username = $(this).attr('username'); parent_id = $(this).attr('parent'); replay = `@${username}\n`; $('#content_textarea').html(replay).focus(); }); });
    script> {% endblock %}

    13、评论功能

    13.1、视图函数

    def commit_comment(request):
        res = {'code': 100, 'msg': '评论成功'}
        if request.is_ajax():
            if request.user.is_authenticated:
                parent_id = request.POST.get('parent_id')
                user_id = request.user.id
                article_id = request.POST.get('article_id')
                content = request.POST.get('content')
    
                try:
                    with transaction.atomic():
                        models.Comment.objects.create(user_id=user_id, content=content, article_id=article_id,
                                                      comments_id=parent_id)
                        article = models.Article.objects.filter(id=article_id).update(comment_num=F('comment_num') + 1)
                        res['username'] = request.user.username
                        if parent_id:
                            old_user_id = models.Comment.objects.filter(pk=parent_id).first().user_id
                            res['parent_name'] = models.UserInfo.objects.filter(pk=old_user_id).first().username
    
                        # from django.core.mail import send_mail
                        #
                        # send_mail('您的文章{}被评论了'.format(article.title), '{}评论了{}'.format(settings.EMAIL_HOST_USER,
                        #                                                                content), settings.EMAIL_HOST_USER,
                        #           models.UserInfo.objects.filter(pk=old_user_id).first().email)
    
                except Exception as e:
                    print(e)
                    res['code'] = 103
                    res['msg'] = '内部错误'
            else:
                res['code'] = 102
                res['msg'] = '用户未登录'
        else:
            res['code'] = 101
            res['msg'] = '非法操作'
        return JsonResponse(res)
    

    13.2、前端

    同12.2章节

    14、后台管理页面搭建

    14.1、base.html

    
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>后台页面title>
        {% block ext_css %}
    
        {% endblock %}
        {% load static %}
        <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
        <link rel="stylesheet" href="{% static 'font-awesome-4.7.0/css/font-awesome.min.css' %}">
        <script src="{% static 'jquery-3.3.1/jquery-3.3.1.min.js' %}">script>
        <link rel="stylesheet" href="{% static 'bootstrap/js/bootstrap.min.js' %}">
    head>
    <body>
    <div class="header">
        <nav class="navbar navbar-default navbar-inverse">
            <div class="container-fluid">
                <div class="navbar-header">
                    <a class="navbar-brand" href="#">后台管理a>
                div>
    
                <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 class=""><a href="{% url 'blog:update_avator' %}">修改头像 <span class="sr-only">(current)span>a>li>
                    ul>
                div>
            div>
        nav>
    div>
    div>
    <div class="container-fluid">
        <div class="row">
            <div class="left_content col-md-3">
                <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
                    <div class="panel panel-default">
                        <div class="panel-heading" role="tab" id="headingOne">
                            <h4 class="panel-title">
                                <a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne"
                                   aria-expanded="true" aria-controls="collapseOne">
                                    操作
                                a>
                            h4>
                        div>
                        <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel"
                             aria-labelledby="headingOne">
                            <div class="panel-body">
                                <ul class="nav">
                                    <li><a href="{% url 'blog:create_article' %}">新增文章a>li>
                                    <li><a href="">新增随笔a>li>
                                ul>
    
                            div>
                        div>
                    div>
                    <div class="panel panel-default">
                        <div class="panel-heading" role="tab" id="headingTwo">
                            <h4 class="panel-title">
                                <a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion"
                                   href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
                                    分类
                                a>
                            h4>
                        div>
                        <div id="collapseTwo" class="panel-collapse collapse" role="tabpanel"
                             aria-labelledby="headingTwo">
                            <div class="panel-body">
                            div>
                        div>
                    div>
                div>
            div>
            <div class="col-md-9">
                <div>
    
                    
                    <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>
    
                    
                    <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 ext_js %}
    
    {% endblock %}
    body>
    html>
    

    14.2、后台首页

    {% extends 'backend/base.html' %}
    {% block content %}
        <table class="table table-hover table-striped">
            <thead>
            <tr>
                <th>标题th>
                <th>评论数th>
                <th>点赞数th>
                <th>操作th>
                <th>操作th>
            tr>
            thead>
            <tbody>
            {% for article in article_list %}
                <tr>
                    <td>
                        <a href="/{{ article.blog.userinfo.username }}/article/{{ article.pk }}.html">{{ article.title }}a>
                    td>
                    <td>{{ article.comment_num }}td>
                    <td>{{ article.up_num }}td>
                    <td><a href="{% url 'blog:update_article' article.id %}">编辑a>td>
                    <td><a href="">删除a>td>
                tr>
            {% endfor %}
            tbody>
        table>
    {% endblock %}
    

    14.3、视图函数

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

    15、新增文章,处理xss攻击

    15.1、前端

    {% extends 'backend/base.html' %}
    
    {% block content %}
        <form action="" method="post">
            {% csrf_token %}
            <div class="form-group">
                <label for="">文章标题label>
                <input type="text" class="form-control" name="title" value="{{ article.title }}">
            div>
    
            <div class="form-group">
                <label for="">文章内容label>
                <p><textarea name="content" id="editor_id" cols="30" rows="10">{{ article.content }}textarea>p>
            div>
    
            <div class="form-group">
                <label for="">分类label>
                {% for category in category_list %}
                    <input type="radio" name="category_id" value="{{ category.id }}">{{ category.name }}
                {% endfor %}
            div>
    
            <div class="form-group">
                <label for="">标签label>
                {% for tag in tag_list %}
                    <input type="checkbox" name="tag_id" value="{{ tag.id }}">{{ tag.name }}
                {% endfor %}
            div>
    
            <div class="text-center">
                <button class="btn btn-primary">创建文章button>
            div>
        form>
    {% endblock %}
    

    15.2、视图函数

    def create_article(request):
        if request.method == 'GET':
            category_list = models.Category.objects.filter(blog=request.user.blog).all()
            tag_list = models.Tag.objects.filter(blog=request.user.blog).all()
            return render(request, 'backend/create_article.html', locals())
        else:
            title = request.POST.get('title')
            content = request.POST.get('content')
            category_id = request.POST.get('category_id')
            tag_id = request.POST.getlist('tag_id')
    
            soup = BeautifulSoup(content, 'html.parser')
            desc = soup.text[0:90]
            res_script = soup.find_all('script')
            for script in res_script:
                script.decompose()
    
            article = models.Article.objects.create(title=title, content=str(soup), description=desc, blog=request.user.blog,
                                                    category_id=category_id)
    
            ll = []
            for tag in tag_id:
                ll.append(models.TagToArticle(tag_id=tag, article_id=article.pk))
            models.TagToArticle.objects.bulk_create(ll)
            return redirect(reverse('blog:admin_index'))
    

    16、修改文章

    16.1、前端

    {% extends 'backend/base.html' %}
    
    {% block content %}
        <form action="{% url 'blog:update_article' article.id %}" method="post" enctype="multipart/form-data">
            {% csrf_token %}
            <div class="form-group">
                <label for="">文章标题label>
                <input type="text" class="form-control" name="title" value="{{ article.title }}">
            div>
    
            <div class="form-group">
                <label for="">文章描述label>
                <input type="text" class="form-control" name="desc" value="{{ article.desc }}">
            div>
    
            <div class="form-group">
                <label for="">文章内容label>
                <textarea name="content" id="editor_id" cols="30" rows="10">{{ article.content }}textarea>
            div>
            <div class="text-center">
                <button class="btn btn-primary">修改button>
            div>
        form>
    {% endblock %}
    
    {% block ext_js %}
        {% load static %}
        <script charset="utf-8" src="{% static 'kindeditor/kindeditor-all.js' %}">script>
        <script>
            KindEditor.ready(function (K) {
                window.editor = K.create('#editor_id', {
                    width: '100%',
                    height: '500px',
                    uploadJson: '/blog/upload_img/',
                    filePostName: 'myfile',
                    extraFileUploadParams: {
                        csrfmiddlewaretoken: '{{ csrf_token }}',
                    }
                });
            });
        script>
    {% endblock %}
    

    16.2、视图函数

    def update_article(request, id):
        if request.method == 'GET':
            article = models.Article.objects.get(pk=id)
            return render(request, 'backend/update_article.html', locals())
        else:
            title = request.POST.get('title')
            desc = request.POST.get('desc')
            content = request.POST.get('content')
            models.Article.objects.filter(pk=id).update(title=title, description=desc, content=content)
            return redirect(reverse('blog:admin_index'))
    

    17、修改头像

    17.1、前端

    {% extends 'backend/base.html' %}
    
    {% block content %}
        {% csrf_token %}
        <div class="form-group">
            <label for="">原始头像label>
            <img src="/media/{{ user.avator }}" alt="" width="80" height="80">
        div>
    
        <div class="form-group">
            <label for="upload_avator">上传新头像
                <img src="/media/{{ user.avator }}" alt="" width="80" height="80" id="id_img">
            label>
            <input type="file" name="myfile" value="上传头像" id="upload_avator" style="display: none">
        div>
    
        <div class="text-center">
            <button class="btn btn-primary" id="id_btn">修改头像button>
        div>
    
    {% endblock %}
    
    {% block ext_js %}
        {% load static %}
        <script src="{% static 'jquery-3.3.1/jquery-3.3.1.min.js' %}">script>
    
        <script>
            $(function () {
                $('#upload_avator').on('change', function () {
                    filereader = new FileReader();
                    formdata = new FormData();
                    filereader.readAsDataURL($(this)[0].files[0]);
                    filereader.onload = function () {
                        $('#id_img').attr('src', filereader.result);
                        formdata.append('avator', $('#id_img').attr('src'));
                    }
                });
    
                $('#id_btn').on('click', function () {
                    formdata.append('myfile', $('#upload_avator')[0].files[0]);
                    formdata.append('csrfmiddlewaretoken', $('input[name="csrfmiddlewaretoken"]').val());
    
                    $.ajax({
                        url: '',
                        method: 'post',
                        data: formdata,
                        contentType: false,
                        processData: false,
                        success: function (data) {
                            if (data.code == 100) {
                                window.location.href = data.url
                            } else {
                                alert(data.msg)
                            }
                        }
                    })
                });
            })
        script>
    {% endblock %}
    

    17.2、视图函数

    @login_required(login_url='/blog/login/')
    def update_avator(request):
        res = {'code': 100, 'msg': 'success'}
        if request.is_ajax():
            try:
                request.user.avator = request.FILES.get('myfile')
                request.user.save()
                res['url'] = reverse('blog:update_avator')
            except Exception as e:
                res['code'] = 101
                res['msg'] = 'error'
            return JsonResponse(res)
        else:
            return render(request, 'update_avator.html')
    

    你可能感兴趣的:(python,django)