BBS项目

发布时间:2023年12月25日

功能简介:

登录,注册,验证码,上传头像,修改头像,修改密码,主站页面,个人主页,后台管理,新增文章,修改文章,删除文章,标签分类查询,点赞点踩,评论,子评论

后端:

目录

settings

from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

SECRET_KEY = 'django-insecure-x-8a2lb-5^hbovczun-b1s2kno94(a+vf^t0ai4b))&o&hp$^k'

DEBUG = True

ALLOWED_HOSTS = []

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'Blog.apps.BlogConfig',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'BBS_12_18.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates']
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'BBS_12_18.wsgi.application'

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'db4',
        'HOST': '127.0.0.1',
        'USER': 'root',
        'PASSWORD': '1227',
        'PORT': 3306,
    }
}

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False

import os

STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

MEDIA_URL = '/head/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'head')

AUTH_USER_MODEL = 'Blog.user'

models

from django.db import models
from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    phone = models.CharField(max_length=32)
    head = models.ImageField(upload_to='head', default='head/default.png')
    blog = models.OneToOneField(to='UserBlog', on_delete=models.CASCADE, null=True)

    class Meta:
        verbose_name_plural = '用户表'

    def __str__(self):
        return self.username


class UserBlog(models.Model):
    site_title = models.CharField(max_length=32)
    site_name = models.CharField(max_length=32)
    site_style = models.CharField(max_length=32)

    class Meta:
        verbose_name_plural = '博客表'

    def __str__(self):
        return self.site_title


class BlogTag(models.Model):
    name = models.CharField(max_length=32)
    blog = models.ForeignKey(to=UserBlog, on_delete=models.SET_NULL, null=True)

    class Meta:
        verbose_name_plural = '标签表'

    def __str__(self):
        return self.name


class BlogCategory(models.Model):
    name = models.CharField(max_length=32)
    blog = models.ForeignKey(to=UserBlog, on_delete=models.SET_NULL, null=True)

    class Meta:
        verbose_name_plural = '分类表'

    def __str__(self):
        return self.name


class BlogArticle(models.Model):
    title = models.CharField(max_length=128)
    description = models.CharField(max_length=256)
    content = models.TextField()
    create_time = models.DateTimeField(auto_now_add=True)
    # 加入点赞数,评论数字段 数字类型 ----优化字段
    up_number = models.IntegerField(default=0)
    down_number = models.IntegerField(default=0)
    commit_number = models.IntegerField(default=0)

    category = models.ForeignKey(to=BlogCategory, on_delete=models.SET_NULL, null=True)
    tag = models.ManyToManyField(to=BlogTag, through='ArticleToTag', through_fields=('BlogArticle', 'BlogTag'))
    blog = models.ForeignKey(to=UserBlog, on_delete=models.CASCADE)

    class Meta:
        verbose_name_plural = '文章表'

    def __str__(self):
        return self.title


class ArticleToTag(models.Model):
    BlogArticle = models.ForeignKey(to=BlogArticle, on_delete=models.CASCADE)
    BlogTag = models.ForeignKey(to=BlogTag, on_delete=models.CASCADE)

    class Meta:
        verbose_name_plural = '文章标签中间表'


class BlogUpAndDown(models.Model):
    user = models.ForeignKey(to=User, on_delete=models.CASCADE)
    article = models.ForeignKey(to=BlogArticle, on_delete=models.CASCADE)
    is_up = models.BooleanField(default=True)
    is_down = models.BooleanField(default=True)

    create_time = models.DateTimeField(auto_now_add=True, null=True)

    class Meta:
        verbose_name_plural = '点赞点踩'

    def __str__(self):
        return self.is_up


class BlogComment(models.Model):
    user = models.ForeignKey(to=User, on_delete=models.CASCADE)
    article = models.ForeignKey(to=BlogArticle, on_delete=models.CASCADE)
    content = models.CharField(max_length=256)
    create_time = models.DateTimeField(auto_now_add=True, null=True)
    parent_id = models.ForeignKey(to='self', on_delete=models.CASCADE, null=True)

    class Meta:
        verbose_name_plural = '评论表'

    def __str__(self):
        return self.content

urls

from django.contrib import admin
from django.urls import path, re_path
from Blog.views import *
from django.views.static import serve
from django.conf import settings

urlpatterns = [
    path('admin/', admin.site.urls),
    path('register/', register),
    path('check_username/', check_username),
    path('login/', login),
    path('get_valid_code/', get_valid_code),
    path('index/', index),
    path('', index),
    path('logout/', logout),
    path('change_password/', change_password),
    path('get_banner/', get_banner),
    path('diggit/', upanddown),
    path('commit/', commit),
    path('backend/', backend),
    path('delete/<int:pk>', delete_article),
    path('add_article/', add_article),
    path('upload_file/', upload_file),
    path('update_article/', update_article),
    path('update_header/',update_header),

    path('head/<path:path>', serve, {'document_root': settings.MEDIA_ROOT}),
    re_path('(?P<username>\w+)/(?P<choice>category|tag|archive)/(?P<condition>.*?).html', site),
    path('<str:username>/articles/<int:article_id>.html', article_detail),

    path('<str:username>', site),

]

admin

from django.contrib import admin

# Register your models here.
from .models import *

admin.site.register(User)
admin.site.register(UserBlog)
admin.site.register(BlogArticle)
admin.site.register(BlogCategory)
admin.site.register(BlogTag)
admin.site.register(BlogUpAndDown)
admin.site.register(BlogComment)
admin.site.register(ArticleToTag)

forms

from django.forms import widgets, ValidationError
from .models import User
from django import forms


class RegisterForm(forms.Form):
    username = forms.CharField(max_length=16, min_length=3, required=True,
                               label='用户名', error_messages={
            'max_length': '不能超过16个字符',
            'min_length': '不能少于3个字符',
            'required': '该字段必填'
        }, widget=widgets.TextInput(attrs={'class': 'form-control'}))

    password = forms.CharField(max_length=16, min_length=3, required=True,
                               label='密码', error_messages={
            'max_length': '不能超过16个字符',
            'min_length': '不能少于3个字符',
            'required': '该字段必填'
        }, widget=widgets.PasswordInput(attrs={'class': 'form-control'}))

    re_password = forms.CharField(max_length=16, min_length=3, required=True,
                                  label='确认密码', error_messages={
            'max_length': '不能超过16个字符',
            'min_length': '不能少于3个字符',
            'required': '该字段必填'
        }, widget=widgets.PasswordInput(attrs={'class': 'form-control'}))

    email = forms.EmailField(max_length=16, min_length=3, required=True,
                             label='邮箱', error_messages={
            'max_length': '不能超过16个字符',
            'min_length': '不能少于3个字符',
            'required': '该字段必填'
        }, widget=widgets.EmailInput(attrs={'class': 'form-control'}))

    phone = forms.CharField(max_length=11, min_length=11, required=True,
                            label='电话号码', error_messages={
            'max_length': '长度必须为11',
            'min_length': '长度必须为11',
            'required': '该字段必填'
        }, widget=widgets.TextInput(attrs={'class': 'form-control'}))



    def clean_username(self):
        username = self.cleaned_data.get('username')
        res = User.objects.filter(username=username).exists()
        if res:
            raise ValidationError('该用户已存在')
        return username

    def clean(self):
        password = self.cleaned_data.get('password')
        re_password = self.cleaned_data.get('re_password')
        if not password == re_password:
            raise ValidationError('两次密码不一致')
        return self.cleaned_data

utils

import random

def get_random_code():
    code = ''
    for i in range(4):
        upper_char = chr(random.randint(65, 90))
        low_char = chr(random.randint(97, 122))
        num_char = str(random.randint(0, 9))
        res = random.choice([upper_char, low_char, num_char])
        code += res
    return code


def get_random_rgb():
    return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))


if __name__ == '__main__':
    print(get_random_code())

views

from django.shortcuts import render, HttpResponse, redirect
from .models import *
from .forms import RegisterForm
from django.http import JsonResponse
from io import BytesIO
from .utils import get_random_code, get_random_rgb
from django.contrib.auth import authenticate, login as auth_login, logout as auth_logout
from PIL import Image, ImageDraw, ImageFont
from django.contrib.auth.decorators import login_required
from django.db.models import F
from django.db import transaction
import json, os
from django.conf import settings
from bs4 import BeautifulSoup


def register(request):
    if request.method == 'GET':
        form = RegisterForm()
        return render(request, 'register.html', locals())
    else:

        head = request.FILES.get('my_img')
        form = RegisterForm(request.POST)
        if form.is_valid():
            data = form.cleaned_data
            data.pop('re_password')
            if head:
                data['head'] = head

            obj = UserBlog.objects.create(site_name=data.get('username') + '的博客',
                                          site_title=data.get('username') + '的博客',
                                          site_style=data.get('username') + '的博客')
            data["blog"] = obj
            User.objects.create_user(**data)

            return JsonResponse({'code': 100, 'msg': '注册成功', 'url': '/login/'})
        else:
            return JsonResponse({'code': 101, 'msg': '注册失败', 'errors': form.errors})


def check_username(request):
    username = request.GET.get('username')
    res = User.objects.filter(username=username).exists()
    if res:
        return JsonResponse({'code': 101, 'msg': '已经被人抢注了'})
    else:
        return JsonResponse({'code': 100, 'msg': '史无前例的用户'})


def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        username = request.POST.get('username')
        password = request.POST.get('password')
        code = request.POST.get('code').lower()
        # 1 校验验证码,取出老验证码,忽略大小写

        old_code = request.session.get('code').lower()
        print(username, password, code, old_code)
        if code == old_code:
            # 2 验证用户
            user = authenticate(username=username, password=password)
            if user:

                print(request, user)
                auth_login(request, user)
                print('登录成功')
                return JsonResponse({'code': 100, 'msg': '登录成功', 'url': '/'})
            else:
                print('登录失败')
                return JsonResponse({'code': 101, 'msg': '用户名或密码错误'})
        else:
            print('验证码错误')
            return JsonResponse({'code': 102, 'msg': '验证码错误'})


def get_valid_code(request):
    image_tmp = Image.new('RGB', (300, 38), get_random_rgb())

    draw = ImageDraw.Draw(image_tmp)

    img_font = ImageFont.truetype('./static/font/ss.TTF', 23)

    code_str = get_random_code()

    request.session['code'] = code_str

    # 组合
    for i, item in enumerate(code_str):
        draw.text((20 + i * 45, 0), item, fill=get_random_rgb(), font=img_font)

    my_io = BytesIO()
    image_tmp.save(my_io, 'png')
    return HttpResponse(my_io.getvalue())


def index(request):
    # 取出所有文章
    article_list = BlogArticle.objects.all().order_by('create_time')
    return render(request, 'index.html', {'article_list': article_list})


def logout(request):
    auth_logout(request)
    return redirect('/')


@login_required(login_url='/login/')
def change_password(request):
    # 1 取出原密码  校验原密码是否正确
    old_password = request.POST.get('old_password')
    if request.user.check_password(old_password):
        new_password = request.POST.get('new_password')
        re_new_password = request.POST.get('re_new_password')
        # 2 取出新密码和确认新密码
        if new_password == re_new_password:
            request.user.set_password(new_password)
            request.user.save()
            return JsonResponse({'code': 100, 'msg': '修改成功'})
        else:
            return JsonResponse({'code': 101, 'msg': '两次密码不一致'})
    else:
        return JsonResponse({'code': 102, 'msg': '原密码错误'})


def get_banner(request):
    banner_list = [
        {'url': '/head/banner/2.png', 'msg': '默认图,点击登录', 'to': '/login/'},
        {'url': '/head/banner/1.png', 'msg': '跳转到注册', 'to': '/register/'}, ]
    return JsonResponse({'code': 100, 'results': banner_list})


def site(request, username, **kwargs):
    user = User.objects.filter(username=username).first()
    if user:
        article_list = BlogArticle.objects.filter(blog_id=user.blog.id).all()
        choice = kwargs.get('choice', None)
        condition = kwargs.get('condition', None)
        if choice and choice == 'category':
            category_name = BlogCategory.objects.filter(pk=condition).first().name
            article_list = article_list.filter(category_id=condition)
        elif choice and choice == 'tag':
            tag_name = BlogTag.objects.filter(pk=condition).first().name
            article_list = article_list.filter(tag__id=condition)
        elif choice and choice == 'archive':
            date_y_m = condition
            year, month = condition.split('-')
            article_list = article_list.filter(create_time__year=year, create_time__month=month)

        return render(request, 'site.html', locals())
    else:
        return render(request, '404.html')


def article_detail(request, username, article_id):
    article = BlogArticle.objects.filter(pk=article_id).first()
    commit_list = BlogComment.objects.filter(article_id=article.id)
    return render(request, 'article_detail.html', locals())


def upanddown(request):
    user = request.user
    if user.is_authenticated:
        article_id = request.POST.get('article_id')
        up_or_down = request.POST.get('up')
        up_or_down = json.loads(up_or_down)
        res = BlogUpAndDown.objects.filter(article_id=article_id, user=user).first()
        if res:
            return JsonResponse({'code': 102, 'msg': '您已经点过了'})
        else:
            with transaction.atomic():
                # 存点赞点踩表
                BlogUpAndDown.objects.create(user=user, article_id=article_id, is_up=up_or_down)

                if up_or_down:
                    BlogArticle.objects.filter(pk=article_id).update(up_number=F('up_number') + 1)
                    return JsonResponse({'code': 100, 'msg': '点赞成功'})
                else:
                    BlogArticle.objects.filter(pk=article_id).update(down_number=F('down_number') + 1)
                    return JsonResponse({'code': 100, 'msg': '点踩成功'})

    else:
        return JsonResponse({'code': 101, 'msg': '您没有登录,请先 <a href="/login/" style="color: red">登录</a>'})


def commit(request):
    user = request.user
    if user.is_authenticated:
        article_id = request.POST.get('article_id')
        content = request.POST.get('content')
        parent_id = request.POST.get('parent_id') or None
        with transaction.atomic():
            commit = BlogComment.objects.create(user=user, article_id=article_id, content=content,
                                                parent_id_id=parent_id)
            BlogArticle.objects.filter(pk=article_id).update(commit_number=F('commit_number') + 1)
            if commit.parent_id:
                return JsonResponse(
                    {'code': '103', 'msg': '评论成功', 'content': commit.content, 'username': user.username,
                     'parent_name': commit.parent_id.user.username})
            else:
                return JsonResponse(
                    {'code': '100', 'msg': '评论成功', 'content': commit.content, 'username': user.username})
    else:
        return JsonResponse({'code': '101', 'msg': '没有登录'})


@login_required(login_url='/login/')
def backend(request):
    article_list = BlogArticle.objects.filter(blog=request.user.blog)
    return render(request, 'backend/backend_index.html', locals())


@login_required(login_url='/login/')
def delete_article(request, pk):
    BlogArticle.objects.filter(pk=pk).delete()
    return redirect('/backend/')


@login_required(login_url='/login/')
def add_article(request):
    if request.method == 'GET':
        # 当前作者所有分类
        category_list = BlogCategory.objects.filter(blog=request.user.blog).all()
        tag_list = BlogTag.objects.filter(blog=request.user.blog).all()
        return render(request, 'backend/add_article.html', locals())
    else:
        title = request.POST.get('title')
        content = request.POST.get('content')
        category = request.POST.get('category')
        desc = content[0:70]
        tag = request.POST.getlist('tag')
        article = BlogArticle.objects.create(title=title, description=desc, content=content, category_id=category,
                                             blog=request.user.blog)
        article.tag.add(*tag)

        return redirect('/backend/')


def upload_file(request):
    file = request.FILES.get('myfile')
    path = os.path.join(settings.MEDIA_ROOT, 'img')
    with open('%s/%s' % (path, file.name), 'wb') as f:
        for line in file:
            f.write(line)

    return JsonResponse({
        "error": 0,
        "url": "/head/img/%s" % file.name
    })


# 修改文章
@login_required(login_url='/login/')
def update_article(request):
    if request.method == 'GET':
        article_id = request.GET.get('id')
        article = BlogArticle.objects.get(pk=article_id)
        article_category = article.category  # 当前文章分类
        tags = article.tag.all()  # 当前文章所有标签
        # 拿出当前作者所有分类
        category_list = BlogCategory.objects.filter(blog=request.user.blog).all()
        # 拿出当前作者所有标签
        tag_list = BlogTag.objects.filter(blog=request.user.blog).all()
        return render(request, 'backend/update_article.html', locals())
    else:
        article_id = request.GET.get('id')

        title = request.POST.get('title')
        content = request.POST.get('content')
        category = request.POST.get('category')
        tag = request.POST.getlist('tag')
        # 处理xss攻击
        soup = BeautifulSoup(content)
        list_script = soup.find_all('script')
        for l in list_script:
            l.decompose()
        content = str(soup)
        desc = soup.text[0:70]
        BlogArticle.objects.filter(pk=article_id).update(title=title, content=content, description=desc,
                                                         category_id=category)
        ArticleToTag.objects.filter(article_id=article_id).delete()
        for i in tag:
            ArticleToTag.objects.create(article_id=article_id, tag_id=i)

        return redirect('/backend/')


@login_required(login_url='/login/')
def update_header(request):
    if request.method == 'GET':
        return render(request, 'backend/update_head.html')
    else:
        avatar = request.FILES.get('new_head')

        request.user.head = avatar
        print(request.user.head)
        request.user.save()

        return redirect('/backend/')

templatetags - commen_left

from django import template
from Blog.models import User, BlogArticle
from django.db.models import Count
from django.db.models.functions import TruncMonth

register = template.Library()


# 把返回的数据----渲染在left.html中----》html片段
# 把这个html片段放在你想放在的位置
@register.inclusion_tag('left.html', name='left')
def left(username):
    user = User.objects.filter(username=username).first( )
    res_category = BlogArticle.objects.filter(blog=user.blog).values('category_id').annotate(
        article_count=Count('id')).values('category__id', 'category__name', 'article_count')
    res_tag = BlogArticle.objects.filter(blog=user.blog).values('tag__id').annotate(article_count=Count('id')).values(
        'tag__id', 'tag__name', 'article_count')
    res_date = BlogArticle.objects.filter(blog=user.blog).annotate(create_date=TruncMonth('create_time')).values(
        'create_date').annotate(article_count=Count('id')).values('create_date', 'article_count')

    return {'user': user, 'res_category': res_category, 'res_tag': res_tag, 'res_date': res_date}

前端:

目录

site

{% extends 'base.html' %}
{% block title %}
    {{ user.blog.site_title }}
{% endblock %}

{% block site_name %}
    {{ user.blog.site_name }}
{% endblock %}


{% block header %}
    <link rel="stylesheet" href="/static/font-awesome/css/font-awesome.min.css">
{% endblock %}




{% block article_top %}
    {% if category_name %}
        <h2>随笔分类--{{ category_name }}</h2>
        <hr>
    {% elif tag_name %}
        <h2>标签--{{ tag_name }}</h2>
        <hr>
    {% elif date_y_m %}
        <h2>随笔档案---{{ date_y_m }}</h2>
        <hr>
    {% endif %}
{% endblock %}
{% block content %}
    {% for article in article_list %}
        <div class="media">
            <h3 class="media-heading"><a href="/{{ username }}/articles/{{ article.id }}.html">{{ article.title }}</a>
            </h3>
            <div class="clearfix">
                <div class="media-left pull-right">

                    <a href="#">
                        <img alt="64x64" class="media-object" style="width: 64px; height: 64px;"
                             src="/head/{{ article.blog.user.head }}">
                    </a>
                </div>
                <div class="media-body">
                    {{ article.description }}
                </div>
            </div>
            <div class="bottom-article pull-right" style="margin-top: 8px">
                <span>posted @</span>
                <span>{{ article.create_time|date:'Y-m-d H:i:s' }}</span>
                <span class="glyphicon glyphicon-thumbs-up">{{ article.up_number }}</span>
                <span><i class="fa fa-address-card-o"
                         style="margin-right: 10px"></i><span>{{ article.commit_number }}</span></span>
                <span><a href="">编辑</a></span>
            </div>
        </div>
        <hr>
    {% endfor %}

{% endblock %}

text

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/jquery-3.7.1.js"></script>
    <script src="/static/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="/static/bootstrap-3.4.1-dist/css/bootstrap.css">
</head>
<body>
<h1>个人站点 --- {{ username }}</h1>
</body>
</html>

register

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/jquery-3.7.1.js"></script>
    <script src="/static/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="/static/bootstrap-3.4.1-dist/css/bootstrap.css">
</head>
<body>
<div class="row">
    <div class="col-md6 container">
        <h1 class="text-center">注册功能</h1>
        <form id="register_form">
            {% csrf_token %}
            {% for foo in form %}
                <div class="form-group container">
                    <label for="{{ foo.auto_id }}">{{ foo.label }}</label>
                    {{ foo }} <span class="pull-right error" style="color: red"></span>
                </div>
            {% endfor %}
            <div class="form-group">
                <label for="id_avatar">头像
                    <img src="/head/img/default.png" alt="" height="80px" width="80px" id="id_img">
                </label>
                <input type="file" id="id_avatar" class="form-control" accept="image/*" style="display: none">

            </div>

        </form>
        <div class="text-center">
            <input type="submit" value="注册" class=" btn btn-success" id="id_submit">
            <span class="error" style="color:darkred;margin-left:10px " id="id_error"></span>

        </div>
    </div>
</div>

</body>

<script>
    //1 监控文件变化
    $('#id_avatar').change(function () {
        var fileReader = new FileReader();
        fileReader.readAsDataURL($('#id_avatar')[0].files[0])
        fileReader.onload = function () {
            $('#id_img').attr('src', fileReader.result)
        }

    })

    //2 按钮提交
    $('#id_submit').click(function () {
        var formdata = new FormData()
        //放入文件
        formdata.append('my_img', $('#id_avatar')[0].files[0])


        //放数据 2
        var register_form = $('#register_form').serializeArray()  //会把当前form表单中的数据放到列表套字典的形式
        $.each(register_form, function (i, v) {
            formdata.append(v['name'], v['value'])
        })

        $.ajax({
            url: '/register/',
            method: 'post',
            processData: false,
            contentType: false,
            data: formdata,
            success: function (data) {
                if (data.code == 100) {
                    location.href = data.url
                } else {
                    //两次密码不一致,把错误写在注册按钮的后面
                    //input 自己的错误写在自己的后面
                    console.log(data.errors)
                    $.each(data.errors, function (key, value) {
                        if (key == '__all__') {
                            $('#id_error').html(value[0])
                        }
                        $('#id_' + key).next().html(value[0]).parent().addClass('has-error')

                        //三秒后清空错误和红框
                        setTimeout(function () {
                            $('.error').html('').parent().removeClass('has-error')
                        }, 3000)

                    })
                }
            }

        })

    })

    //3 输入框失去焦点,去后端校验
    $('#id_username').blur(function () {

        var username = $(this).val()

        $.ajax({
            url: '/check_username/?username=' + username,
            method: 'get',
            success: function (data) {
                console.log(data)
                if (data.code != 100) {
                    //1 清空输入框
                    //2 错误提示
                    $('#id_username').val('').next().html(data.msg).parent().addClass('has-error')
                }
                setTimeout(function () {
                            $('.error').html('').parent().removeClass('has-error')
                        }, 3000)
            }
        })
    })
</script>
</html>

login

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/jquery-3.7.1.js"></script>
    <script src="/static/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="/static/bootstrap-3.4.1-dist/css/bootstrap.css">

</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md6 container">
            <h1 class="text-center">登录功能</h1>
            <form id="login_form" method="post">
                {% csrf_token %}
                <div class="form-group">
                    <label for="">用户名</label>
                    <input type="text" name="username" class="form-control">
                </div>

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

                <div class="form-group">
                    <label for="">验证码</label>
                    <div class="row">
                        <div class="col-md-6">
                            <input type="text" name="code" class="form-control">
                        </div>

                        <img src="/get_valid_code/" alt="" class="col-md-6" height="35" id="id_img">
                    </div>
                    <div class="text-center" style="margin-top: 50px">
                        <input type="button" value="登录" class=" btn btn-success" id="id_submit">
                        <span class="error" style="color:darkred;margin-left:10px " id="id_error"></span>
                    </div>


                </div>
            </form>
        </div>
    </div>
</div>
<script>

    $('#id_img').click(function () {
        //img标签有个特性,只要src变了,就会重新向src地址请求数据
        var times = new Date().getTime()
        var url = '/get_valid_code/?time=' + times
        $(this).attr('src', url)
    })
    $.ajaxSetup({

        data: {csrfmiddlewaretoken: '{{ csrf_token }}'}

    })

    //ajax登录
    $('#id_submit').click(function () {
        var serialize_data = $('#login_form').serializeArray()
        var data = {}
        $.each(serialize_data, function (i, v) {
            data[v['name']] = v['value']
        })
        console.log(data)
        $.ajax({
            url: '/login/',
            method: 'post',
            data: data,
            success: function (data) {
                console.log(data)
                if (data.code==100){
                    location.href=data.url
                }else {
                    $('#id_error').html(data.msg)
                }
            }
        })
    })
</script>

</body>

</html>

left

<div>
    <div class="panel panel-primary">
        <div class="panel-heading"><h3 class="panel-title">我的标签</h3></div>
        <div class="panel-body">
            {% for tag in res_tag %}
                <p><a href="/{{ user.username }}/tag/{{ tag.tag__id }}.html">{{ tag.tag__name }}({{ tag.article_count }})</a>
                </p>
                {% if not forloop.last %}

                    <hr>
                {% endif %}

            {% endfor %}


        </div>
    </div>
    <div class="panel panel-info">
        <div class="panel-heading"><h3 class="panel-title">随笔分类</h3></div>
        <div class="panel-body">

            {% for category in res_category %}
                <p>
                    <a href="/{{ user.username }}/category/{{ category.category__id }}.html">{{ category.category__name }}({{ category.article_count }})</a>
                </p>
                {% if not forloop.last %}

                    <hr>
                {% endif %}

            {% endfor %}


        </div>
    </div>
    <div class="panel panel-danger">
        <div class="panel-heading"><h3 class="panel-title">随笔档案</h3></div>
        <div class="panel-body">
            {% for item in res_date %}
                <p>
                    <a href="/{{ user.username }}/archive/{{ item.create_date|date:'Y-m' }}.html">{{ item.create_date|date:'Y年m月' }}({{ item.article_count }})</a>
                </p>
                {% if not forloop.last %}

                    <hr>
                {% endif %}

            {% endfor %}</div>
    </div>


</div>

index

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/jquery-3.7.1.js"></script>
    <script src="/static/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="/static/bootstrap-3.4.1-dist/css/bootstrap.css">
    <style>
        span {
            margin-right: 15px;
        }
    </style>
</head>
<body>
<div class="header">
    <nav class="navbar navbar-default">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                        data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="#">BBS项目</a>
            </div>

            <!-- Collect the nav links, forms, and other content for toggling -->
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav">
                    <li class="active"><a href="#">文章 <span class="sr-only">(current)</span></a></li>
                    <li><a href="#">新闻</a></li>
                </ul>
                {% if request.user.is_authenticated %}
                    <ul class="nav navbar-nav navbar-right">
                        <li><a href="/{{ request.user }}">{{ 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="#">后台管理</a></li>
                                <li><a href="#"><span data-target="#gridSystemModal" data-toggle="modal">修改密码</span></a>
                                </li>
                                <li><a href="#">修改头像</a></li>
                                <li role="separator" class="divider"></li>
                                <li><a href="/logout/">退出</a></li>
                            </ul>
                        </li>
                    </ul>
                {% else %}
                    <ul class="nav navbar-nav navbar-right">
                        <li><a href="/login/">登录</a></li>
                        <li><a href="/register">注册</a></li>


                    </ul>
                {% endif %}

            </div><!-- /.navbar-collapse -->
        </div><!-- /.container-fluid -->
    </nav>
</div>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-2">
            <div class="panel panel-primary">
                <div class="panel-heading">
                    <h3 class="panel-title">文章栏1</h3>
                </div>
                <div class="panel-body">
                    <p><a href="">11111</a></p>
                    <p><a href="">22222</a></p>
                    <p><a href="">33333</a></p>
                    <p><a href="">44444</a></p>
                </div>
            </div>
            <div class="panel panel-success">
                <div class="panel-heading">
                    <h3 class="panel-title">文章栏2</h3>
                </div>
                <div class="panel-body">
                    <p><a href="">11111</a></p>
                    <p><a href="">22222</a></p>
                </div>
            </div>
            <div class="panel panel-info">
                <div class="panel-heading">
                    <h3 class="panel-title">文章栏3</h3>
                </div>
                <div class="panel-body">
                    广告位招租
                </div>
            </div>
        </div>


        <div class="col-md-7">

            <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>
                </ol>
                <div class="carousel-inner" role="listbox">


                    <div class="item active">

                        <a href="">
                            <img src="" alt="..." class="img">
                            <div class="carousel-caption">

                            </div>
                        </a>
                    </div>
                    <div class="item ">

                        <a href="">
                            <img src="" alt="..." class="img">
                            <div class="carousel-caption">

                            </div>
                        </a>
                    </div>


                    <!-- Controls -->
                    <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">Previous</span>
                    </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">Next</span>
                    </a>
                </div>

            </div>


            {% for article in article_list %}
                <div class="media">
                    <h3  class="media-heading"><a href="/{{ article.blog.user.username }}/articles/{{ article.id }}.html">{{ article.title }}</a></h3>
                    <div class="media-left">

                        <a href=/{{ article.blog.user.username }}>
                            <img class="media-object" data-src="holder.js/64x64" alt="64x64"
                                 src="/head/{{ article.blog.user.head }}"

                            >
                        </a>

                    </div>
                    <div class="media-body">
                        {{ article.description }}
                    </div>
                    <div class="bottom-article" style="margin-top: 8px">
                        <span><a href="/{{ article.blog.user.username }}">{{ article.blog.user.username }}</a></span>
                        <span>{{ article.create_time|date:'Y-m-d H:i:s' }}</span>
                        <span class="glyphicon glyphicon-thumbs-up">{{ article.up_number }}</span>
                        <span><i class="fa fa-address-card-o"
                                 style="margin-right: 10px"></i><span>{{ article.commit_number }}</span></span>

                    </div>
                </div>
                <hr>
            {% endfor %}


            <div>
                <div class="modal fade" tabindex="-1" role="dialog" aria-labelledby="gridSystemModalLabel"
                     id="gridSystemModal">
                    <div class="modal-dialog" role="document">
                        <div class="modal-content">
                            <div class="modal-header">
                                <button type="button" class="close" data-dismiss="modal"
                                        aria-label="Close"><span
                                        aria-hidden="true">&times;</span></button>
                                <h4 class="modal-title" id="gridSystemModalLabel">修改密码</h4>
                            </div>
                            <div class="modal-body">
                                <div class="alert alert-warning alert-dismissible fade in hidden" role="alert"
                                     id="alert_error">
                                    <button type="button" class="close" data-dismiss="alert"
                                            aria-label="Close"><span
                                            aria-hidden="true">×</span></button>
                                    <strong>密码修改成功</strong>
                                </div>
                                <form>
                                    {% csrf_token %}
                                    <div class="form-group">
                                        <label for="">原密码</label>
                                        <input type="password" name="old_password" class="form-control">
                                    </div>
                                    <div class="form-group">
                                        <label for="">新密码</label>
                                        <input type="password" name="new_password" class="form-control">
                                    </div>
                                    <div class="form-group">
                                        <label for="">确认新密码</label>
                                        <input type="password" name="re_new_password" class="form-control">
                                    </div>
                                </form>
                            </div>
                            <div class="modal-footer">
                                <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
                                <button type="button" class="btn btn-primary" id="id_submit">确定</button>
                            </div>
                        </div><!-- /.modal-content -->
                    </div><!-- /.modal-dialog -->
                </div><!-- /.modal -->


            </div>


        </div>
        <div class="col-md-3">
            <div class="panel panel-success">
                <div class="panel-heading"><h3 class="panel-title">48小时排行榜</h3></div>
                <div class="panel-body"> Panel content</div>
            </div>
            <div class="panel panel-info">
                <div class="panel-heading"><h3 class="panel-title">72小时排行榜</h3></div>
                <div class="panel-body"> Panel content</div>
            </div>
            <div class="panel panel-warning">
                <div class="panel-heading"><h3 class="panel-title">Panel title</h3></div>
                <div class="panel-body"> Panel content</div>
            </div>
        </div>
    </div>
</div>

</body>
<script>
    $('#id_submit').click(function () {
        // 让按钮不可点击
        //$(this).addClass('disabled')
        // jquery 都是给标签修改属性--attr-自定义属性和prop->自有属性的区别
        $(this).prop('disabled', true)
        $.ajax({
            url: '/change_password/',
            method: 'post',
            data: {
                old_password: $('[name="old_password"]').val(),
                new_password: $('[name="new_password"]').val(),
                re_new_password: $('[name="re_new_password"]').val(),
                csrfmiddlewaretoken: $('[name="csrfmiddlewaretoken"]').val()

            },
            success: function (data) {
                console.log(data)
                // 显示错误信息,并显示
                $('#alert_error').removeClass('hidden').children('strong').html(data.msg)
                // 让按钮可以点击
                $('#id_submit').prop('disabled', false)
                if (data.code == 100) {
                    $('#alert_error').addClass('hidden')
                    // 模态框消失
                    $('#gridSystemModal').modal('hide')
                    // 跳到登录,重新登录
                    location.href = '/login/'
                } else {
                    // 3s 后,错误提示消失
                    setTimeout(function () {
                        $('#alert_error').addClass('hidden')
                    }, 3000)
                }

            }
        })


    })
    $(function () {
            // 发送ajax请求
            $.ajax({
                url: '/get_banner/',
                method: 'get',
                success: function (data) {
                    console.log(data)

                    $('.img').each(function (i, v) {
                        console.log(i)
                        console.log(v)

                        $(v).attr('src', data.results[i].url).parent().attr('href', data.results[i].to).children('div').html(data.results[i].msg)

                    })

                }
            })
        })

</script>
</html>

base

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>
        {% block title %}
        {% endblock %}
    </title>
    <script src="/static/jquery-3.7.1.js"></script>
    <script src="/static/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="/static/bootstrap-3.4.1-dist/css/bootstrap.css">
    {% block header %}

    {% endblock %}

</head>
<body>
<nav class="navbar navbar-default">
    <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                    data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="/">{% block site_name %}{% endblock %}</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav">
                <li class="active"><a href="#">文章 <span class="sr-only">(current)</span></a></li>
                <li><a href="#">新闻</a></li>
            </ul>
            {% if  request.user.is_authenticated %}
                <ul class="nav navbar-nav navbar-right">
                    <li><a href="#">{{ request.user.username }}</a></li>
                    <li><a href="/backend/">后台管理</a></li>

                </ul>
            {% else %}
                <ul class="nav navbar-nav navbar-right">
                    <li><a href="/login/">登录</a></li>
                    <li><a href="/register/">注册</a></li>

                </ul>
            {% endif %}


      </div><!-- /.navbar-collapse -->
    </div><!-- /.container-fluid -->
</nav>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-2">
            {% load common_left %}
            {% left username %}
        </div>
        <div class="col-md-9">
            {% block article_top %}{% endblock %}
            {% block content %}

            {% endblock %}


        </div>
    </div>
</div>


</body>
{% block script %}

{% endblock %}
</html>

article_detail

{% extends 'base.html' %}

{% block title %}
    {{ article.title }}
{% endblock %}

{% block header %}
    <link rel="stylesheet" href="/static/css/common_css.css">
    <link rel="stylesheet" href="/static/font-awesome/css/font-awesome.min.css">
{% endblock %}

{% block site_name %}
    {{ article.blog.site_name }}
{% endblock %}

{% block content %}
    <h2 class="text-center" article_id="{{ article.id }}">{{ article.title }}</h2>
    <hr>
    {{ article.content|safe }}
    <hr>
    <div class="clearfix">
        <div id="div_digg">
            <div class="diggit digg">
                <span class="diggnum" id="digg_count">{{ article.up_number }}</span>
            </div>
            <div class="buryit digg">
                <span class="burynum" id="bury_count">{{ article.down_number }}</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips">

            </div>
        </div>
    </div>

    <div>
        <span id="id_commit_text">评论列表</span>
        <ul class="list-group">
            {% for commit in commit_list %}
                <li class="list-group-item">

                    <div>
                        <span><a href=""># {{ forloop.counter }} 楼</a></span>
                        <span>{{ commit.create_time|date:"Y-m-d H:i" }}</span>
                        <span><a href="/{{ commit.user.username }}">{{ commit.user.username }}</a></span>
                        <div class="pull-right">
                            <span style="margin-left: 10px;margin-right: 10px" class="replay"
                                  username="{{ commit.user.username }}" commit_id="{{ commit.id }}"><a>回复</a></span>
                            <span>删除</span>
                        </div>
                    </div>
                    <div style="padding: 10px">
                        {# 判断 parent_id是否为空,如果为空,就是下面,如果不为空,找到父评论人名 @#}
                        {% if commit.parent_id %}
                            <a href="/{{ commit.parent_id.user.username }}">@ {{ commit.parent_id.user.username }}</a>

                        {% endif %}
                        <br>
                        {{ commit.content }}
                    </div>

                </li>

            {% endfor %}


        </ul>

    </div>

    {% if request.user.is_authenticated %}
        <div>
            <p style="margin-left: 15px"><h4>发表评论</h4></p>
            <p><textarea name="" id="id_text" cols="200" rows="10" style="margin-left: 10px"></textarea></p>
            <div class="alert alert-warning alert-dismissible fade in text-center hidden" role="alert" id="alert_error">
                <button type="button" class="close" data-dismiss="alert"
                        aria-label="Close"><span
                        aria-hidden="true">×</span></button>
                <strong>必须输入内容</strong>
            </div>
            <button class="btn btn-success pull-right" id="btn_commit">评论</button>
        </div>

    {% endif %}

{% endblock %}



{% block script %}
    <script>

        // 全局变量 parent_id
        var parent_id = null;
        // 笨办法,给点赞绑定一个事件,给点踩绑定一个事件---》分别提交
        $('.digg').click(function () {
            // 判断,如果点的div 有diggit类就是点赞
            var up = $(this).hasClass('diggit')
            console.log($('h2').attr('article_id'))
            let _this = this
            //发送ajax请求
            $.ajax({
                url: '/diggit/',
                method: 'post',
                data: {
                    article_id: '{{ article.id }}',
                    up: up,
                    csrfmiddlewaretoken: '{{ csrf_token }}'
                },
                success: function (data) {
                    console.log(data)
                    $('#digg_tips').html(data.msg)
                    if (data.code == 100) {
                        // 让数字加1  赞+1  踩 +1
                        var num = Number($(_this).children('span').html())
                        $(_this).children('span').html(num + 1)

                    }
                }
            })
        })

        // 评论功能
        $("#btn_commit").click(function () {

            var content = $("#id_text").val()
            if (parent_id) {
                var i = content.indexOf('\n')
                content = content.substring(i + 1)
                console.log(content);
            }

            if (content) {
                $.ajax({
                    url: '/commit/',
                    method: 'post',
                    data: {
                        parent_id: parent_id,
                        article_id: '{{ article.id }}',
                        content: content,
                        csrfmiddlewaretoken: '{{ csrf_token }}'

                    },
                    success: function (data) {
                        console.log(data)
                        if (data.code == 100) {
                            $("#id_text").val('')
                            var html_content = `
                            <li class="list-group-item">
                                <p><span class="fa fa-address-card-o">${data.username}:</span></p>
                                <p>${data.content}</p>
                            </ul>
                            `
                            $('.list-group').append(html_content)
                        } else if (data.code == 103) {
                            $("#id_text").val('')
                            var html_content = `
                            <li class="list-group-item">
                                <p><span class="fa fa-address-card-o">${data.username}:</span></p>
                                <p>@${data.parent_name}</p>
                                <p>${data.content}</p>
                            </ul>
                            `
                            $('.list-group').append(html_content)

                        }
                    }
                })
            } else {
                $('#alert_error').removeClass('hidden')
            }

        })

        // 子评论
        $('.replay').click(function () {
            var username = "@" + $(this).attr('username') + '\n'
            $("#id_text").val(username)

            parent_id = $(this).attr('commit_id')
        })

    </script>
{% endblock %}

404

<!DOCTYPE html>
<html><head>
    <meta charset="utf-8">
    <link rel="icon" href="" type="image/x-icon">
    <title>404 页面不存在</title>
    <style type="text/css">
        body {
            margin: 8% auto 0;
            max-width: 400px;
            min-height: 200px;
            padding: 10px;
            font-family: 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
            font-size: 14px;
            padding-right: 200px;
            position: relative;
        }
        p { color: #555;margin: 15px 0px; }
        img { border: 0px; }
        .d { color: #404040; }
        .robot img { max-width: 192px; }
        .robot { position: absolute; top: 0; right: 0; }
    </style>
</head>
<body>
    <div style="margin-top:20px">
        <p>404 - 抱歉,您访问的资源不存在!</p>
        <p style="color:#777;">可能是网址有误,或者对应的内容被删除,或者处于私有状态</p>
        <p><a href="/index/">返回主页</a></p>
    </div>
    <div class="robot"><a href=""><img src="//common.cnblogs.com/images/404-robot.png" alt="404 robot"></a></div>


</body></html>

add_article

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

{% block articles %}

    <div>
        <h3 class="text-center">添加文章</h3>
        <form method="post">
            {% csrf_token %}
            <div class="form-group">
                <label for="">标题</label>
                <input type="text" class="form-control" name="title">
            </div>
            <div class="form-group">
                <label for="">内容</label>
                <textarea name="content" id="editor_id" cols="200" rows="10" class="form-control"></textarea>
            </div>
            <div class="form-group">
                <label for="">分类</label>
                <select class="form-control" name="category">
                    {% for category in category_list %}
                        <option value="{{ category.id }}">{{ category.name }}</option>
                    {% endfor %}


                </select>

            </div>
            <div class="form-group">
                <label for="">标签</label>
                <select class="form-control" multiple name="tag">
                    {% for tag in tag_list %}
                        <option value="{{ tag.id }}">{{ tag.name }}</option>
                    {% endfor %}


                </select>

            </div>

            <div class="text-center">
                <button class="btn btn-danger">新增文章</button>
            </div>
        </form>

    </div>
{% endblock %}


{% block script %}
    <script charset="utf-8" src="/static/kindeditor/kindeditor-all.js"></script>
    <script charset="utf-8" src="/static/kindeditor/lang/zh-CN.js"></script>
    <script>
        KindEditor.ready(function (K) {
            window.editor = K.create('#editor_id', {
                width: '100%',
                height: '600px',
                items: [
                    'source', '|', 'undo', 'redo', '|', 'preview', 'print', 'template', 'code', 'cut', 'copy', 'paste',
                    'plainpaste', 'wordpaste', '|', 'justifyleft', 'justifycenter', 'justifyright',
                    'justifyfull', 'insertorderedlist', 'insertunorderedlist', 'indent', 'outdent', 'subscript',
                    'superscript', 'clearhtml', 'quickformat', 'selectall', '|', 'fullscreen', '/',
                    'formatblock', 'fontname', 'fontsize', '|', 'forecolor', 'hilitecolor', 'bold',
                    'italic', 'underline', 'strikethrough', 'lineheight', 'removeformat', '|', 'image', 'multiimage',
                    'flash', 'media', 'insertfile', 'table', 'hr', 'emoticons', 'baidumap', 'pagebreak',
                    'anchor', 'link', 'unlink', '|', 'about'
                ],
                resizeType: 0,
                uploadJson: '/upload_file/',
                filePostName: 'myfile',
                extraFileUploadParams: {
                    csrfmiddlewaretoken: '{{ csrf_token }}'
                }
            });
        });
    </script>
{% endblock %}

backend_base

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>后台管理</title>
    <script src="/static/jquery-3.7.1.js"></script>
    <script src="/static/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="/static/bootstrap-3.4.1-dist/css/bootstrap.css">

</head>
<body>
<nav class="navbar navbar-default">
    <div class="container-fluid navbar-inverse">
        <div class="navbar-header ">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                    data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="/">后台管理</a>
        </div>
    </div><!-- /.container-fluid -->
</nav>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-2">
            <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 nav-pills nav-stacked nav-pills-stacked-example">
                                <li role="presentation"><a href="/add_article/">新建文章</a></li>
                                <hr>
                                <li role="presentation"><a href="/update_header/">修改头像</a></li>
                                <hr>
                                <li role="presentation"><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">
                            <ul class="nav nav-pills nav-stacked nav-pills-stacked-example">
                                <li role="presentation"><a href="#">分类一</a></li>
                                <hr>
                                <li role="presentation"><a href="#">分类二</a></li>

                            </ul>
                        </div>
                    </div>
                </div>
            </div>

        </div>
        <div class="col-md-9">
            <div class="bs-example bs-example-tabs" data-example-id="togglable-tabs">
                <ul id="myTabs" class="nav nav-tabs" role="tablist">
                    <li role="presentation" class="active"><a href="#home" id="home-tab" role="tab" data-toggle="tab"
                                                              aria-controls="home" aria-expanded="true">文章</a></li>
                    <li role="presentation" class=""><a href="#profile" role="tab" id="profile-tab" data-toggle="tab"
                                                        aria-controls="profile" aria-expanded="false">随笔</a></li>
                    <li role="presentation" class="dropdown">
                        <a href="#" id="myTabDrop1" class="dropdown-toggle" data-toggle="dropdown"
                           aria-controls="myTabDrop1-contents" aria-expanded="false">更多 <span
                                class="caret"></span></a>
                        <ul class="dropdown-menu" aria-labelledby="myTabDrop1" id="myTabDrop1-contents">
                            <li><a href="#dropdown1" role="tab" id="dropdown1-tab" data-toggle="tab"
                                   aria-controls="dropdown1">相册</a></li>
                            <li><a href="#dropdown2" role="tab" id="dropdown2-tab" data-toggle="tab"
                                   aria-controls="dropdown2">测试</a></li>
                        </ul>
                    </li>
                </ul>
                <div id="myTabContent" class="tab-content">
                    <div role="tabpanel" class="tab-pane fade active in" id="home" aria-labelledby="home-tab">
                        {% block articles %}

                        {% endblock %}
                    </div>
                    <div role="tabpanel" class="tab-pane fade" id="profile" aria-labelledby="profile-tab">
                        随笔
                    </div>
                    <div role="tabpanel" class="tab-pane fade" id="dropdown1" aria-labelledby="dropdown1-tab">
                        相册
                    </div>
                    <div role="tabpanel" class="tab-pane fade" id="dropdown2" aria-labelledby="dropdown2-tab">
                        更多
                    </div>
                </div>
            </div>


        </div>
    </div>
</div>


</body>
{% block script %}

{% endblock %}
</html>

backend_index

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

{% block articles %}

    <div>
        <table class="table table-striped">
            <thead>
            <tr>
                <th>文章id</th>
                <th>文章名字</th>
                <th>创建时间</th>
                <th>操作</th>
            </tr>
            </thead>
            <tbody>
            {% for article in article_list %}
                <tr>
                    <th scope="row">{{ article.id }}</th>
                    <td><a href="/{{ request.user.username }}/articles/{{ article.id }}.html">{{ article.title }}</a>
                    </td>
                    <td>{{ article.create_time|date:"Y年m月d日" }}</td>
                    <td><a href="">编辑</a> | <a href="/delete/{{ article.id }}">删除</a></td>
                </tr>
            {% endfor %}


            </tbody>
        </table>
    </div>

{% endblock %}

update_article

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

{% block articles %}

    <div>
        <h3 class="text-center">添加文章</h3>
        <form method="post" action="/update_article/?id={{ article.id }}">
            {% csrf_token %}
            <input type="hidden" name="id" value="{{ article.id }}">
            <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>
                <textarea name="content" id="editor_id" cols="200" rows="10" class="form-control">
                     {{ article.content }}
                </textarea>
            </div>
            <div class="form-group">
                <label for="">分类</label>
                <select class="form-control" name="category">
                    {% for category in category_list %}
                        {% if article_category.id == category.id %}
                            <option value="{{ category.id }}" selected>{{ category.name }}</option>
                        {% else %}
                            <option value="{{ category.id }}">{{ category.name }}</option>
                        {% endif %}

                    {% endfor %}


                </select>

            </div>
            <div class="form-group">
                <label for="">标签</label>
                <select class="form-control" multiple name="tag">
                    {% for tag in tag_list %}
                        {% if tag in tags %}
                            <option value="{{ tag.id }}" selected>{{ tag.name }}</option>
                        {% else %}
                            <option value="{{ tag.id }}">{{ tag.name }}</option>
                        {% endif %}
                    {% endfor %}


                </select>

            </div>

            <div class="text-center">
                <button class="btn btn-danger">新增文章</button>
            </div>
        </form>

    </div>
{% endblock %}


{% block script %}
    <script charset="utf-8" src="/static/kindeditor/kindeditor-all.js"></script>
    <script charset="utf-8" src="/static/kindeditor/lang/zh-CN.js"></script>
    <script>
        KindEditor.ready(function (K) {
            window.editor = K.create('#editor_id', {
                width: '100%',
                height: '600px',
                items: [
                    'source', '|', 'undo', 'redo', '|', 'preview', 'print', 'template', 'code', 'cut', 'copy', 'paste',
                    'plainpaste', 'wordpaste', '|', 'justifyleft', 'justifycenter', 'justifyright',
                    'justifyfull', 'insertorderedlist', 'insertunorderedlist', 'indent', 'outdent', 'subscript',
                    'superscript', 'clearhtml', 'quickformat', 'selectall', '|', 'fullscreen', '/',
                    'formatblock', 'fontname', 'fontsize', '|', 'forecolor', 'hilitecolor', 'bold',
                    'italic', 'underline', 'strikethrough', 'lineheight', 'removeformat', '|', 'image', 'multiimage',
                    'flash', 'media', 'insertfile', 'table', 'hr', 'emoticons', 'baidumap', 'pagebreak',
                    'anchor', 'link', 'unlink', '|', 'about'
                ],
                resizeType: 0,
                uploadJson: '/upload_file/',
                filePostName: 'myfile',
                extraFileUploadParams: {
                    csrfmiddlewaretoken: '{{ csrf_token }}'
                }
            });
        });
    </script>
{% endblock %}

update_head

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

{% block articles %}
    <div>
        <form method="post" enctype="multipart/form-data">
            {% csrf_token %}
            <div class="form-group">
                <label>原头像
                    <img src="/head/{{ request.user.head }}" alt="" height="80px" width="80px"
                         style="margin-left: 20px">
                </label>
            </div>
            <div class="form-group">
                <label for="id_avatar">新头像
                    <img src="/static/img/default.png" alt="" height="80px" width="80px" id="id_img"
                         style="margin-left: 20px">
                </label>
                <input type="file" id="id_avatar" class="form-control" accept="image/*" style="display: none"
                       name="new_avatar">
            </div>
            <input type="submit" class="btn btn-danger" value="提交">
        </form>
    </div>

{% endblock %}


{% block script %}
    <script>
        // 1 监控文件变化
       $('#id_avatar').change(function () {
        var fileReader = new FileReader();
        fileReader.readAsDataURL($('#id_avatar')[0].files[0])
        fileReader.onload = function () {
            $('#id_img').attr('src', fileReader.result)
        }

    })
    </script>
{% endblock %}

文章来源:https://blog.csdn.net/safvirtu/article/details/135206411
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。