功能简介:
登录,注册,验证码,上传头像,修改头像,修改密码,主站页面,个人主页,后台管理,新增文章,修改文章,删除文章,标签分类查询,点赞点踩,评论,子评论
后端:
目录
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">×</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 %}