Python开发【项目】:博客后台
概述
通过自己写的博客后台代码、思路,来与武sir的代码进行一个差异化的比较,记录之间的差距,改善以后写代码的思路
博客后台这个项目,对之前Django学习的各个知识点都有涉及到,非常重要
用户登录验证
数据库表:
from django.db import models # 除了主键其他默认可以为空
# max_length在CharField中必填 class UserInfo(models.Model):
"""
用户表
"""
nid = models.BigAutoField(primary_key=True) # 主键自增 8位数
username = models.CharField(verbose_name='用户名', max_length=32, unique=True) # unique 唯一性
password = models.CharField(verbose_name='密码', max_length=64) # verbose_name ModeForm验证时显示名,
nickname = models.CharField(verbose_name='昵称', max_length=32) # 等同于Form类里面的label
email = models.EmailField(verbose_name='邮箱', unique=True) # unique 唯一性
avatar = models.ImageField(verbose_name='头像') create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) # auto_now_add 更新时,自动更新为当前时间 fans = models.ManyToManyField(verbose_name='粉丝们', to='UserInfo', through='UserFans',
through_fields=('user', 'follower')) class Blog(models.Model):
"""
博客信息
"""
nid = models.BigAutoField(primary_key=True)
title = models.CharField(verbose_name='个人博客标题', max_length=64)
site = models.CharField(verbose_name='个人博客前缀', max_length=32, unique=True)
theme = models.CharField(verbose_name='博客主题', max_length=32) user = models.OneToOneField(to='UserInfo', to_field='nid') class UserFans(models.Model):
"""
互粉关系表
"""
user = models.ForeignKey(verbose_name='博主', to='UserInfo', to_field='nid', related_name='users')
follower = models.ForeignKey(verbose_name='粉丝', to='UserInfo', to_field='nid', related_name='followers') # 联合唯一索引 索引本身目的就是加快查询速度
class Meta:
unique_together = [
('user', 'follower'),
] class Category(models.Model):
"""
博主个人文章分类表,自创建
"""
nid = models.AutoField(primary_key=True)
title = models.CharField(verbose_name='分类标题', max_length=32) blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid') class ArticleDetail(models.Model):
"""
文章详细表
"""
content = models.TextField(verbose_name='文章内容', ) article = models.OneToOneField(verbose_name='所属文章', to='Article', to_field='nid') class UpDown(models.Model):
"""
文章顶或踩
"""
article = models.ForeignKey(verbose_name='文章', to='Article', to_field='nid')
user = models.ForeignKey(verbose_name='赞或踩用户', to='UserInfo', to_field='nid')
up = models.BooleanField(verbose_name='是否赞') class Meta:
unique_together = [
('article', 'user'),
] class Comment(models.Model):
"""
评论表
"""
nid = models.BigAutoField(primary_key=True)
content = models.CharField(verbose_name='评论内容', max_length=255)
create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) reply = models.ForeignKey(verbose_name='回复评论', to='self', related_name='back', null=True)
article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid')
user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid') class Tag(models.Model):
nid = models.AutoField(primary_key=True)
title = models.CharField(verbose_name='标签名称', max_length=32)
blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid') class Article(models.Model):
'''
文章简介表
'''
nid = models.BigAutoField(primary_key=True)
title = models.CharField(verbose_name='文章标题', max_length=128)
summary = models.CharField(verbose_name='文章简介', max_length=255)
read_count = models.IntegerField(default=0)
comment_count = models.IntegerField(default=0)
up_count = models.IntegerField(default=0)
down_count = models.IntegerField(default=0)
create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid')
category = models.ForeignKey(verbose_name='文章类型', to='Category', to_field='nid', null=True) type_choices = [
(1, "Python"),
(2, "Linux"),
(3, "OpenStack"),
(4, "GoLang"),
] article_type_id = models.IntegerField(choices=type_choices, default=None) tags = models.ManyToManyField(
to="Tag",
through='Article2Tag',
through_fields=('article', 'tag'),
) class Article2Tag(models.Model):
'''
文章跟标签对应关系
'''
article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid')
tag = models.ForeignKey(verbose_name='标签', to="Tag", to_field='nid') class Meta:
unique_together = [
('article', 'tag'),
]
models.py
我的代码:
from django import forms
from django.forms import fields
from django.forms import widgets from repository import models
from django.core.exceptions import ValidationError class LoginForm(forms.Form):
username = fields.CharField(
max_length=12,
widget=widgets.Input(attrs={'class':'form-control','placeholder':"请输入用户名"}),
error_messages={'required': '用户名不能为空',
'max_length': '密码长度不能大于12位'}
)
password = fields.CharField(
max_length=12,
min_length=6,
widget=widgets.PasswordInput(attrs={'class':'form-control','placeholder':"请输入密码"}),
error_messages={'required': '密码不能为空', 'min_length': '密码长度不能小于6位',
'max_length': '密码长度不能大于12位'}
)
check_code = fields.CharField(
error_messages={'required': '验证码不能为空', }
) def clean(self):
user_obj = models.UserInfo.objects.filter(
username=self.cleaned_data.get('username'),password=self.cleaned_data.get('password')
).first()
if user_obj:
return self.cleaned_data['username']
else:
raise ValidationError(message='用户名或密码错误') def clean_check_code(self):
code = self.request.POST.get('check_code')
if code.upper() == self.request.session['CheckCode'].upper():
return self.cleaned_data['check_code']
else:
raise ValidationError(message='验证码错误') def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
if args:
self.request = args[1]
from.py
from io import BytesIO
from django.shortcuts import HttpResponse
from django.shortcuts import render
from django.shortcuts import redirect
from utils.check_code import create_validate_code
from web.views.form import LoginForm
from web.views.form import RegisterForm
from repository import models import json def check_code(request):
"""
验证码
:param request:
:return:
"""
# 1. 创建一张图片 pip3 install Pillow
# 2. 在图片中写入随机字符串
# obj = object()
# 3. 将图片写入到制定文件
# 4. 打开制定目录文件,读取内容
# 5. HttpResponse(data) stream = BytesIO() #在内存中生成一个文件对象
img, code = create_validate_code() #生成图片img和字符串code
img.save(stream,'PNG') #把验证图片存放到内存中以PNG名存放
request.session['CheckCode'] = code #把生成的字符串code存放到session中
# print('验证码',code)
return HttpResponse(stream.getvalue()) #stream.getvalue()返回图片的内容 def login(request):
"""
登陆
"""
if request.method == 'GET':
if request.session.get('is_login',None):
return redirect('/index/')
else:
obj = LoginForm()
return render(request, 'login.html',{'obj':obj})
elif request.method == 'POST':
data = {'status': True, 'error': None,}
obj = LoginForm(request.POST,request)
result = obj.is_valid()
if result:
print('验证通过')
username = request.POST.get('username')
user_dict = models.UserInfo.objects.filter(username=username).values('nid','username','nickname','email','blog__site',
'blog__title','blog__theme')[0]
for key in user_dict:
request.session[key] = user_dict[key] request.session['is_login']=True
if request.POST.get('rmb') == '':
request.session.set_expiry(60*60*24*30)
else:
print(obj.errors.as_json)
data['status'] = False
data['error'] = obj.errors return HttpResponse(json.dumps(data))
views.py
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="/static/plugins/bootstrap/css/bootstrap.css"/>
<link rel="stylesheet" href="/static/plugins/font-awesome/css/font-awesome.css"/>
<link rel="stylesheet" href="/static/css/edmure.css"/>
<link rel="stylesheet" href="/static/css/commons.css"/>
<link rel="stylesheet" href="/static/css/account.css"/>
<style>
</style>
</head>
<body>
<div class="login">
<div style="font-size: 25px; font-weight: bold;text-align: center;">
用户登陆
</div>
<form id='login_form' role="form" onsubmit = "return false" > <!--不进行跳转-->
{% csrf_token %}
<div class="form-group">
<label for="username">用户名</label>
{# <input type="text" class="form-control" placeholder="请输入用户名">#}
{{ obj.username }}
</div>
<div class="form-group">
<label for="password">密码</label>
{# <input type="password" class="form-control" placeholder="请输入密码">#}
{{ obj.password }}
</div>
<div class="form-group">
<label for="password">验证码</label> <div class="row">
<div class="col-xs-7">
<input type="text" class="form-control" placeholder="请输入验证码" name="check_code">
</div>
<div class="col-xs-5">
<img id='changecheckcode' src="/check_code.html" onclick="changeCheckCode();"> <!--点击更换验证码-->
</div>
</div> </div>
<div class="checkbox">
<label>
<input type="checkbox" name="rmb" value=""> 一个月内自动登陆
</label>
<div class="right">
<a href="/register.html" style="margin-right: 10px">注册</a>
<a href="#">忘记密码?</a>
</div>
</div>
<button type="submit" id="loginsubmit" class="btn btn-default">登 陆</button>
<div class="error_message"></div>
</form>
</div>
<script src="/static/js/jquery-1.12.4.js"></script>
<script src="/static/js/account.js"></script>
<script src="/static/js/account_login.js"></script>
</body>
</html> function changeCheckCode(){
var img = $('#changecheckcode')[0];
img.src = img.src + '?'; //刷新验证码
} function ShowError(error) {
$('.error_message').text(error)
} function EmptyError() {
$('.error_message').text('')
} /**
* Created by L on 2017/2/17.
*/ $('#loginsubmit').click(function () {
EmptyError() ; //清空错误
$.ajax({
url:'/login.html',
type:'POST',
data:$('#login_form').serialize(),
dataType:'JSON',
success:function (data) {
if(data['status']){
location.href = '/'; //成功跳转 }else {
var error_message = DateHandel(data);
console.log(error_message)
var error_message = '**' + error_message; ShowError(error_message)
}
},error:function () { }
})
}); function DateHandel(data) {
var error = data['error'];
if(error['username']){ //用户名格式输入错误
var error_message = error['username'][0]
}else {
if(error['password']){ //密码格式输入错误
var error_message = error['password'][0]
}else {
if(error['check_code']){ //验证码为空
changeCheckCode();
var error_message = error['check_code'][0]
}else {
if(error['__all__']){
var error_message = error['__all__'][0]
}
}
}
}
return error_message
}
login.html
武sir的代码:
from django.core.exceptions import ValidationError
from django import forms as django_forms
from django.forms import fields as django_fields
from django.forms import widgets as django_widgets from repository import models from .base import BaseForm class LoginForm(BaseForm, django_forms.Form):
username = django_fields.CharField(
min_length=6,
max_length=20,
error_messages={'required': '用户名不能为空.', 'min_length': "用户名长度不能小于6个字符", 'max_length': "用户名长度不能大于32个字符"}
)
password = django_fields.RegexField(
'^(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[!@#$\%\^\&\*\(\)])[0-9a-zA-Z!@#$\%\^\&\*\(\)]{8,32}$',
min_length=12,
max_length=32,
error_messages={'required': '密码不能为空.',
'invalid': '密码必须包含数字,字母、特殊字符',
'min_length': "密码长度不能小于8个字符",
'max_length': "密码长度不能大于32个字符"}
)
rmb = django_fields.IntegerField(required=False) check_code = django_fields.CharField(
error_messages={'required': '验证码不能为空.'}
) def clean_check_code(self):
if self.request.session.get('CheckCode').upper() != self.request.POST.get('check_code').upper():
raise ValidationError(message='验证码错误', code='invalid')
from.py
from io import BytesIO
from django.shortcuts import HttpResponse
from django.shortcuts import render
from django.shortcuts import redirect
from utils.check_code import create_validate_code
from repository import models
from ..form.accout import LoginForm def check_code(request):
"""
验证码
:param request:
:return:
"""
stream = BytesIO()
img, code = create_validate_code()
img.save(stream, 'PNG')
request.session['CheckCode'] = code
return HttpResponse(stream.getvalue()) def login(request):
"""
登陆
:param request:
:return:
"""
if request.method == 'GET':
return render(request, 'login.html')
elif request.method == 'POST':
result = {'status': False, 'message': None, 'data': None}
form = LoginForm(request=request, data=request.POST)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user_info = models.UserInfo.objects. \
filter(username=username, password=password). \
values('nid', 'nickname',
'username', 'email',
'avatar',
'blog__nid',
'blog__site').first() if not user_info:
# result['message'] = {'__all__': '用户名或密码错误'}
result['message'] = '用户名或密码错误'
else:
result['status'] = True
request.session['user_info'] = user_info
if form.cleaned_data.get('rmb'):
request.session.set_expiry(60 * 60 * 24 * 7)
else:
print(form.errors)
if 'check_code' in form.errors:
result['message'] = '验证码错误或者过期'
else:
result['message'] = '用户名或密码错误'
return HttpResponse(json.dumps(result))
views.py
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="/static/plugins/bootstrap/css/bootstrap.css"/>
<link rel="stylesheet" href="/static/plugins/font-awesome/css/font-awesome.css"/>
<link rel="stylesheet" href="/static/css/edmure.css"/>
<link rel="stylesheet" href="/static/css/commons.css"/>
<link rel="stylesheet" href="/static/css/account.css"/>
</head>
<body>
<div class="login">
<div style="font-size: 25px; font-weight: bold;text-align: center;">
用户登陆
</div>
<form id="fm" method="POST" action="/login.html">
{% csrf_token %}
<div class="form-group">
<label for="username">用户名</label>
<input type="text" class="form-control" name="username" id="username" placeholder="请输入用户名">
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" class="form-control" name="password" id="password" placeholder="请输入密码">
</div>
<div class="form-group">
<label for="password">验证码</label> <div class="row">
<div class="col-xs-7">
<input type="text" class="form-control" name="check_code" id="check_code" placeholder="请输入验证码">
</div>
<div class="col-xs-5">
<img id="check_code_img" src="/check_code.html">
</div>
</div> </div>
<div class="checkbox">
<label>
<input type="checkbox" value="" name="rmb"> 一个月内自动登陆
</label> <div class="right">
<a href="#">忘记密码?</a>
</div>
</div>
<div class="row">
<div class="col-xs-3">
<a id="submit" class="btn btn-default">登 陆</a>
</div>
<div class="col-xs-9" style="padding-left: 0;">
<div class="alert alert-danger hide">
<span style="padding: 0 5px 0 5px;display: inline-block;font-size: 14px">
<i class="fa fa-minus-circle" aria-hidden="true"></i>
</span>
<span id="error_msg" style="font-size: 12px;"></span>
</div>
</div>
</div> </form>
<script src="/static/js/jquery-1.12.4.js"></script>
<script type="text/javascript">
$(function () {
bindLogin();
});
function bindLogin() {
$('#submit').click(function () {
var $msg = $('#error_msg');
$msg.parent().addClass('hide');
$.ajax({
url: '/login.html',
type: 'POST',
data: $('#fm').serialize(),
dataType: 'JSON',
success: function (arg) {
if(arg.status){
location.href = '/'
}else{
$msg.parent().removeClass('hide');
$msg.text(arg.message);
var img = $('#check_code_img')[0];
img.src = img.src + '?';
$('#password,#check_code').val('');
} }
}) })
}
</script>
</div>
</body>
</html>
login.html
区别:
①、前后端交互
前后端交互这块,自己没有活学活用,上课时老师讲课时讲到直接把obj.errors传到前端,然后就固执的这么用了,导致前端js要写很多的if进行判断(我的错误提示很完善,从不能为空,到用户错误,武sir这方面写的比较简单);武sir是直接向前端传输一个错误的字符串(result['message'] = '用户名或密码错误'),前端直接打印即可,js无需多的判断,如果想加多点的错误提示,可以在后台上多做写判断亦可
②、form验证
我from验证这一块,直接受到后台与前端传输数据为obj.errors影响,由于所有错误信息必须包含到obj.errors里面,导致用户密码验证必须写到form里面进行验证,获取用户名密码必须要把request传送到form,这个当时困住了好长时间,好算最后解决了;用户密码验证,武sir放到了处理函数里面,很机智
③、session信息
把要存的信息,key、value分别对应进行存储;武sir直接存了一个‘use_info’字段,包含了所有的信息
用户注册
下面的代码都是整合后的代码了:
class RegisterForm(forms.Form):
username = fields.CharField(
max_length=12,
widget=widgets.Input(attrs={'class': 'form-control', 'placeholder': "请输入用户名"}),
error_messages={'required': '用户名不能为空',
'max_length': '密码长度不能大于12位'}
)
email = fields.EmailField(
widget=widgets.Input(attrs={'class': 'form-control', 'placeholder': "请输入邮箱"}),
error_messages={'required': '邮箱不能为空', 'invalid':'邮箱格式不正确'}
)
password = fields.CharField(
max_length=12,
min_length=6,
widget=widgets.PasswordInput(attrs={'class': 'form-control', 'placeholder': "请输入密码"}),
error_messages={'required': '密码不能为空', 'min_length': '密码长度不能小于6位',
'max_length': '密码长度不能大于12位'}
)
confirm_password = fields.CharField(
max_length=12,
min_length=6,
widget=widgets.PasswordInput(attrs={'class': 'form-control', 'placeholder': "请重新输入密码"}),
error_messages={'required': '确认密码不能为空', 'min_length': '确认密码长度不能小于6位',
'max_length': '确认密码长度不能大于12位'}
)
check_code = fields.CharField(
error_messages={'required': '验证码不能为空',}
) def clean_check_code(self):
print(self.request.POST.get('check_code').upper())
print(self.request.session['CheckCode'].upper())
if self.request.POST.get('check_code').upper() == self.request.session['CheckCode'].upper():
return self.cleaned_data['check_code']
else:
raise ValidationError(message='验证码错误') def clean(self):
confirm_password = self.cleaned_data.get('confirm_password')
password = self.cleaned_data.get('password')
if confirm_password == password:
return self.cleaned_data
else:
raise ValidationError(message='两次密码输入的不一致') def __init__(self, request, *args, **kwargs):
self.request = request
super().__init__(*args, **kwargs)
form.py
from django.db.models import Q
def register(request):
"""
注册
"""
if request.method == 'GET':
return render(request, 'register.html')
elif request.method == 'POST':
message = {'status': False, 'error': None}
form = RegisterForm(request,request.POST)
result = form.is_valid()
if result: # 验证通过
# 开始用户名、邮箱是否唯一
username = request.POST.get('username')
email = request.POST.get('email') user_obj = models.UserInfo.objects.filter(Q(username=username)|Q(email=email)).first()
if user_obj:
message['error'] = '用户名或邮箱已经注册过'
else:
print(form.cleaned_data)
form.cleaned_data.pop('confirm_password')
form.cleaned_data.pop('check_code')
# print(form.cleaned_data)
models.UserInfo.objects.create(**form.cleaned_data)
user_dict = models.UserInfo.objects.filter(username=username).values('nid','username','email').first()
# print(type(user_dict))
request.session['user_info'] = user_dict
message['status'] = True else:
print(form.errors)
if 'username' in form.errors: # 按照顺序进行验证,先用户名、邮箱、密码、确实密码、密码一致、验证码,用户名邮箱唯一
message['error'] = form.errors['username']
else:
if 'email' in form.errors:
message['error'] = form.errors['email']
else:
if 'password' in form.errors:
message['error'] = form.errors['password']
else:
if 'confirm_password' in form.errors:
message['error'] = form.errors['confirm_password']
else:
if '__all__' in form.errors:
message['error'] = form.errors['__all__']
else:
if 'check_code' in form.errors:
message['error'] = form.errors['check_code']
# print(message)
return HttpResponse(json.dumps(message))
views.py
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="/static/plugins/bootstrap/css/bootstrap.css"/>
<link rel="stylesheet" href="/static/plugins/font-awesome/css/font-awesome.css"/>
<link rel="stylesheet" href="/static/css/edmure.css"/>
<link rel="stylesheet" href="/static/css/commons.css"/>
<link rel="stylesheet" href="/static/css/account.css"/>
<style> </style>
</head>
<body>
<div class="register">
<div style="font-size: 25px; font-weight: bold;text-align: center;">
用户注册
</div>
<form role="form" id="reg_form">
{% csrf_token %}
<div class="form-group">
<label for="username">用户名</label>
<input name="username" class="form-control" placeholder="请输入用户名">
</div>
<div class="form-group">
<label for="email">邮箱</label>
<input name="email" class="form-control" placeholder="请输入邮箱">
</div>
<div class="form-group">
<label for="password">密码</label>
<input name="password" class="form-control" type="password" placeholder="请输入密码">
</div>
<div class="form-group">
<label for="confirm_password">确认密码</label>
<input name="confirm_password" class="form-control" type="password" placeholder="请重新输入密码">
</div> <div class="form-group">
<label for="password">验证码</label> <div class="row">
<div class="col-xs-7">
<input type="text" class="form-control" id="password" placeholder="请输入验证码" name="check_code">
</div>
<div class="col-xs-5">
<img id='changecheckcode' src="/check_code.html" onclick="changeCheckCode();"> <!--点击更换验证码-->
</div>
</div>
</div>
<div class="row">
<div class="col-xs-3">
<a id="submit" class="btn btn-default">下一步</a>
</div> <div class="col-xs-9" style="padding-left: 0;">
<div class="alert alert-danger hide">
<span style="padding: 0 5px 0 5px;display: inline-block;font-size: 14px">
<i class="fa fa-minus-circle" aria-hidden="true"></i>
</span>
<span id="error_msg" style="font-size: 12px;"></span>
</div>
</div>
</div>
{# <input type="button" id="regsubmit" class="btn btn-default" value="下一步"/>#}
{# <div class="error_message"></div>#}
</form>
</div>
<script src="/static/js/jquery-1.12.4.js"></script>
<script src="/static/js/account.js"></script>
<script src="/static/js/account_register.js"></script> </body>
</html>
register.html
代码有意思的地方:
form验证中__init__传入request参数
class RegisterForm(forms.Form):
def __init__(self, request, *args, **kwargs):
self.request = request
super().__init__(*args, **kwargs) def register(request):
"""
注册
"""
elif request.method == 'POST':
message = {'status': False, 'error': None}
form = RegisterForm(request,request.POST) # 传参
model检索Q或请求:
from django.db.models import Q
def register(request):
user_obj = models.UserInfo.objects.filter(Q(username=username)|Q(email=email)).first()
所有的验证错误提示信息在后端处理:
def register(request):
else:
print(form.errors)
if 'username' in form.errors: # 按照顺序进行验证,先用户名、邮箱、密码、确实密码、密码一致、验证码,用户名邮箱唯一
message['error'] = form.errors['username']
else:
if 'email' in form.errors:
message['error'] = form.errors['email']
else:
if 'password' in form.errors:
message['error'] = form.errors['password']
else:
if 'confirm_password' in form.errors:
message['error'] = form.errors['confirm_password']
else:
if '__all__' in form.errors:
message['error'] = form.errors['__all__']
else:
if 'check_code' in form.errors:
message['error'] = form.errors['check_code']
# print(message)
return HttpResponse(json.dumps(message))
首页文章展示:
def index(request,*args,**kwargs):
print('innnnidex')
"""
博客首页,展示全部博文
:param request:
:return:
""" if kwargs:
article_type_id = int(kwargs.get('article_type_id'))
kwargs['article_type_id'] = article_type_id
article_list = models.Article.objects.filter(article_type_id=article_type_id).prefetch_related('blog').order_by('-nid')
else:
article_list = models.Article.objects.all().prefetch_related('blog').order_by('-nid') type_choices = models.Article.type_choices return render(request, 'index.html', {'article_list': article_list,
'type_choices':type_choices,
'kwargs':kwargs})
views.py
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="/static/plugins/bootstrap/css/bootstrap.css"/>
<link rel="stylesheet" href="/static/plugins/font-awesome/css/font-awesome.css"/>
<link rel="stylesheet" href="/static/css/edmure.css"/>
<link rel="stylesheet" href="/static/css/commons.css"/>
<link rel="stylesheet" href="/static/css/row-avatar.css"/> <script type="text/javascript" src="/static/js/jquery-1.12.4.js"></script>
<script type="text/javascript" src="/static/plugins/bootstrap/js/bootstrap.js"></script>
</head>
<body>
{% include 'include/header.html' %} <div class="container">
<div>
<div class="col-md-8">
<div class="article-list">
{% for item in article_list %}
<div class="article-item clearfix">
<h3><a href="/{{ item.blog.site }}/{{ item.nid }}.html">{{ item.title }}</a>
{# <small>{{ item.type_choices }}</small>#}
<small>{{ item.article_type_id}}</small>
</h3>
<div class="clearfix">
<a class="avatar left" href="#">
<img src="/{{ item.blog.user.avatar }}">
</a>
{{ item.summary}}
</div>
<div class="footers">
<a href="/{{ item.blog.site }}/index.html">
<i class="fa fa-user-o" aria-hidden="true"></i>
<span>{{ item.blog.user.username }}</span>
</a>
<span>发布于 {{ item.create_time|date:"Y-m-d H:i:s" }}</span>
<a href="/{{ item.blog.site }}/{{ item.nid }}.html" class="ele">
<i class="fa fa-commenting-o" aria-hidden="true"></i>
<span>{{ item.read_count }}</span>
</a>
<a href="#" class="ele">
<i class="fa fa-thumbs-o-up" aria-hidden="true"></i>
<span>{{ item.comment_count }}</span>
</a>
</div> </div>
{% endfor %} </div> <div class="clearfix"> <ul class="pagination">
<li><a href="#">«</a></li>
<li><a href="#">1</a></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">4</a></li>
<li><a href="#">5</a></li>
<li><a href="#">»</a></li>
</ul> </div> </div>
<div class="col-md-4">
<div class="panel panel-default hot-recommend">
<div class="panel-heading">吐血推荐</div>
<div class="panel-body">
<ul class="list-unstyled">
<li>Lorem ipsum dolor sit amet</li>
<li>Consectetur adipiscing elit</li>
<li>Integer molestie lorem at massa</li>
<li>Facilisis in pretium nisl aliquet</li>
<li>Nulla volutpat aliquam velit
</li>
<li>Faucibus porta lacus fringilla vel</li>
<li>Aenean sit amet erat nunc</li>
<li>Eget porttitor lorem</li>
</ul>
</div>
</div>
<div class="panel panel-default hot-comment">
<div class="panel-heading">评论最多</div>
<div class="panel-body">
<ul class="list-unstyled">
<li>Lorem ipsum dolor sit amet</li>
<li>Consectetur adipiscing elit</li>
<li>Integer molestie lorem at massa</li>
<li>Facilisis in pretium nisl aliquet</li>
<li>Nulla volutpat aliquam velit
</li>
<li>Faucibus porta lacus fringilla vel</li>
<li>Aenean sit amet erat nunc</li>
<li>Eget porttitor lorem</li>
</ul>
</div>
</div> </div>
</div>
</div> </body>
</html>
index.html
关键的几段代码:
前端对时间进行处理:
<span>发布于 {{ item.create_time|date:"Y-m-d H:i:s" }}</span>

后台个人信息修改
@check_login
def base_info(request):
"""博主个人信息"""
if request.method == 'GET':
blogTheme = [
{'id': '', 'name': '默认主题'},
{'id': '', 'name': '黑不溜秋'},
{'id': '', 'name': '乌七八啦'},
{'id': '', 'name': '红色火焰'},
{'id': '', 'name': '哈哈哈嘿哈'},
]
print(request.session['user_info'])
return render(request, 'backend_base_info.html',{'blogTheme':blogTheme}) elif request.method == 'POST':
message = {'status':False,'error':None} site = request.session['user_info'].get('blog__site') # session中查找博客地址 if site: # 博客地址之前已经创建,需要更新内容
# print('------------',request.POST)
nickname = request.POST.get('nickname')
theme = request.POST.get('theme')
title = request.POST.get('title') models.Blog.objects.filter(site=site).update(theme=theme,title=title)
models.UserInfo.objects.filter(nid=request.session['user_info']['nid']).update(nickname=nickname) user_info_dict = request.session['user_info']
user_info_dict['nickname']=nickname
user_info_dict['blog__theme']=theme
user_info_dict['blog__title']=title
request.session['user_info']=request.session['user_info']
print(request.session['user_info']) message['status'] = True else:
site = request.POST.get('site')
if site: # 博客地址提交
blog_obj = models.Blog.objects.filter(site=site).first()
if blog_obj:
message['error'] = '博客地址已被占用'
else:
nickname = request.POST.get('nickname')
theme = request.POST.get('theme')
title = request.POST.get('title') models.Blog.objects.create(site=site,
theme=theme,
title=title,
user_id=request.session['user_info']['nid'])
models.UserInfo.objects.filter(nid=request.session['user_info']['nid']).update(nickname=nickname)
user_info_dict = request.session['user_info']
user_info_dict['nickname'] = nickname
user_info_dict['blog__theme'] = theme
user_info_dict['blog__title'] = title
user_info_dict['blog__site'] = site
request.session['user_info'] = request.session['user_info']
message['status'] = True
else:
message['error'] = '博客地址不能为空' return HttpResponse(json.dumps(message)) @check_login
def upload_avatar(request): # 更新头像
message = {'status': False, 'data': None}
if request.method == 'POST':
files = request.FILES.get('avatar_img')
if files: # 省略判断文件格式的过程
img_dir = 'static/imgs/savatar/%s'%(request.session['user_info']['username'])
if not os.path.exists(img_dir):
os.makedirs(img_dir)
img_path = os.path.join(img_dir,'avatar.png')
print(img_path) with open(img_path,'wb') as f:
for item in files.chunks():
f.write(item) models.UserInfo.objects.filter(nid=request.session['user_info']['nid']).update(avatar=img_path)
user_info_dict = request.session['user_info']
user_info_dict['avatar'] = img_path
request.session['user_info'] = user_info_dict
print(request.session['user_info'])
message['data'] = img_path
message['status'] = True
print(message)
return HttpResponse(json.dumps(message))
view.py
{% extends 'backend_layout.html' %}
{% block css %}
<style>
.form-horizontal .control-label {
padding-top: 7px;
margin-bottom: 0;
text-align: right;
}
.avatar-container{
height: 200px;
width: 200px;
padding: 2px;
border: 1px solid #dddddd;
position: relative;
}
.avatar-container img{
height: 198px;
width: 198px;
border: 0;
overflow: hidden;
}
.avatar-container .text{
text-align: center;
}
.avatar-container .img-file{
top:0;
left: 0;
right: 0;
bottom: 0;
opacity: 0;
position: absolute;
z-index: 102;
}
</style>
{% endblock %}
{% block conent %}
<ol class="breadcrumb">
<li><a href="#">用户管理</a></li>
<li class="active">用户信息</li>
</ol>
<div>
<div class="row" style="position: relative;">
<form class="form-horizontal" id="baseinfo_form">
{% csrf_token %}
<div class="col-xs-12">
<div class="form-group">
<label class="col-xs-2 control-label">用户名</label>
<div class="col-xs-5">
<p class="form-control-static">{{ request.session.user_info.username }}</p>
</div>
</div>
<div class="form-group">
<label class="col-xs-2 control-label">邮箱</label>
<div class="col-xs-5">
<p class="form-control-static">{{ request.session.user_info.email }}</p>
</div>
</div>
<div class="form-group">
<label for="nickname" class="col-xs-2 control-label">昵称</label>
<div class="col-xs-5">
{% if request.session.user_info.nickname %}
<input type="text" class="form-control" name="nickname" value="{{ request.session.user_info.nickname }}">
{% else %}
<input type="text" class="form-control" name="nickname" placeholder="请输入昵称">
{% endif %}
</div>
</div>
<div class="form-group">
<label for="blogUrl" class="col-xs-2 control-label">博客地址</label>
<div class="col-xs-5">
{% if request.session.user_info.blog__site %}
<p class="form-control-static">{{ request.session.user_info.blog__site }}</p>
{# <p class="form-control" readonly>{{ request.session.user_info.blog__site }}</p>#}
{% else %}
<input type="text" class="form-control" id="blogUrl" name="site"
placeholder="如:wupeiqi,则个人博客为http://www.xxx.com/wupeiqi.html">
{% endif %}
</div>
</div>
<div class="form-group">
<label for="blogTheme" class="col-xs-2 control-label">博客主题</label>
<div class="col-xs-5">
<select id="blogTheme" class="form-control" name="theme" >
{% for item in blogTheme %}
{% if item.id == request.session.user_info.blog__theme %}
<option value="{{ item.id }}" selected="selected">{{ item.name }}</option>
{% else %}
<option value="{{ item.id }}">{{ item.name }}</option>
{% endif%}
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label for="blogTitle" class="col-xs-2 control-label">博客标题内容</label>
<div class="col-xs-8">
{% if request.session.user_info.blog__title %}
<textarea id="blogTitle" style="min-height: 100px" class="form-control"
name="title">{{ request.session.user_info.blog__title }}</textarea>
{% else %}
<textarea id="blogTitle" style="min-height: 100px" class="form-control"
placeholder="来一杯鸡汤..." name="title"></textarea>
{% endif %}
</div>
</div>
<div class="form-group">
<div class="col-xs-offset-2 col-xs-10">
<button type="button" id='baseinfo_save' class="btn btn-primary">保 存</button>
<div class="error_message"></div>
</div>
</div>
</div>
</form>
<div style="position: absolute;" class="col-xs-offset-7 col-xs-5">
<div class="avatar-container">
<iframe style="display: none;" id="upload_iframe" name="upload_iframe"></iframe>
<form method="POST" action="/backend/upload-avatar.html" enctype="multipart/form-data"
target="upload_iframe" id="form1">
{% csrf_token %}
{% if request.session.user_info.avatar %}
<img id='previewImg' origin="/static/imgs/avatar/default.png" src='/{{ request.session.user_info.avatar }}'
style="border-radius: 50%;" >
{% else %}
<img id='previewImg' origin="/static/imgs/avatar/default.png" src="/static/imgs/avatar/default.png"
style="border-radius: 50%;width: 198px;">
{% endif %}
<div class="text">点击图片更换(<a href="#">撤销</a>)</div>
<input id="avatarImg" name="avatar_img" type="file" class="img-file" onchange="ChangeAvatar();"/>
</form>
</div>
</div>
</div>
</div>
<script src="/static/js/jquery-1.12.4.js"></script>
<script>
$('#baseinfo_save').click(function () {
$.ajax({
url:'/backend/base-info.html',
type:'POST',
data:$('#baseinfo_form').serialize(),
dataType:'JSON',
success:function (data) {
if(data['status']){
console.log('ok');
location.reload()
}else {
var error_message ='**' + data['error'];
ShowError(error_message)
}
},
error:function () {
}
})
});
function ShowError(error) {
$('.error_message').text(error)
}
function ChangeAvatar() {
{# $('#avatarImg').change(function () {#}
{# $(this).submit();#}
$('#form1').submit();
$('#upload_iframe').load(function () {
var iframeContents = this.contentWindow.document.body.innerText;
iframeContents = JSON.parse(iframeContents);
console.log(iframeContents);
if (iframeContents.status) {
$('#previewImg').attr('src', '/' + iframeContents.data +'?');
}
})
}
</script>
{% endblock %}
{% block js %}
{% endblock %}
backend_base_info.html
里面包含了图片的点击上传,更新 非常重要

文章列表+组合检索
@check_login
def article(request,*args, **kwargs):
"""
博主个人文章管理
"""
blog_id = request.session['user_info']['blog__nid']
condition = {}
for k, v in kwargs.items():
if v == '':
pass
else:
condition[k] = v
condition['blog_id'] = blog_id
data_count = models.Article.objects.filter(**condition).count()
page = Pagination(request.GET.get('p', 1), data_count)
result = models.Article.objects.filter(**condition).order_by('-nid').only('nid', 'title','blog').select_related('blog')[page.start:page.end]
page_str = page.page_str(reverse('article', kwargs=kwargs))
category_list = models.Category.objects.filter(blog_id=blog_id).values('nid', 'title')
type_list = map(lambda item: {'nid': item[0], 'title': item[1]}, models.Article.type_choices)
kwargs['p'] = page.current_page
return render(request,
'backend_article.html',
{'result': result,
'page_str': page_str,
'category_list': category_list,
'type_list': type_list,
'arg_dict': kwargs,
'data_count': data_count
}
)
views.py
{% extends 'backend_layout.html' %}
{% load search %}
{% block css %}
<style>
.conditions a{
display: inline-block;
padding: 2px 5px;
margin-left: 5px;
}
.conditions a.active{
background-color: #b35215;
color: #ffffff;
}
</style>
{% endblock %}
{% block conent %}
<ol class="breadcrumb" style="margin-bottom: 0;">
<li><a href="#">文章管理</a></li>
<li class="active">文章列表</li>
</ol>
<div>
<div style="border: 1px dashed #dddddd;padding: 8px;border-left: 3px solid #337ab7;">
<i class="fa fa-search" aria-hidden="true"></i> 搜索条件
</div>
<div style="padding: 10px">
<div class="conditions row clearfix" style="margin: 0;padding: 8px 0;">
<div class="col-xs-1" style="text-align: right">
{% category_all arg_dict %}
</div>
<div class="col-xs-11">
{% category_combine category_list arg_dict %}
</div>
</div>
<div class="conditions row clearfix" style="margin: 0;padding: 8px 0;">
<div class="col-xs-1" style="text-align: right">
{% article_type_all arg_dict %}
</div>
<div class="col-xs-11">
{% article_type_combine type_list arg_dict %}
</div>
</div>
</div>
<div class="clearfix"
style="height: 36px;line-height: 35px;padding: 0 15px;border-top: 1px solid #dddddd;background-color: #f1f0f0">
<i class="fa fa-table" aria-hidden="true"></i>
搜索文章({{ data_count }}篇)
<a target="_blank" href="/backend/add-article.html" class="right"
style="display: inline-block;padding:0 10px;background-color: #428bca;color: #ffffff;">
<i class="fa fa-plus-circle" aria-hidden="true"></i>
创建新文章
</a>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th>文章标题</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for row in result %}
<tr nid="{{ row.nid }}">
<td><a href="/{{ row.blog.site }}/{{ row.nid }}.html">{{ row.title }}</a></td>
<td>
<a class="btn btn-danger btn-xs" href="/backend/del-article-{{ row.nid }}.html">
<i class="fa fa-times" aria-hidden="true"></i>
删除
</a>
|
<a class="btn btn-primary btn-xs" href="/backend/edit-article-{{ row.nid }}.html">
<i class="fa fa-pencil-square-o" aria-hidden="true"></i>
编辑
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="clearfix">
<ul class="pagination right" style="margin-top: 0">
{{ page_str }}
</ul>
</div>
</div>
{% endblock %}
{% block js %}
{% endblock %}
backend_article.html
组合搜索-跳转

套路之新建文章和编辑文章
@check_login
def add_article(request):
"""
添加文章
"""
if request.method == 'GET':
form = ArticleForm(request=request)
return render(request, 'backend_add_article.html', {'form': form})
elif request.method == 'POST':
form = ArticleForm(request=request, data=request.POST) # 传参request 当然也可以用之前自己的方式进行上传
if form.is_valid():
with transaction.atomic(): # 固定用法,下面操作多表进行添加数据,如果有一个数据添加数据,那么其他刚刚添加的数据进行回滚
#dict格式cleaned_data 包含的字段{'tags' 'article_type' 'category_id' 'summary' 'title' 'content'}
print(form.cleaned_data)
tags = form.cleaned_data.pop('tags') # 移出 tags需要在第三张Article2Tag表上手动添加
content = form.cleaned_data.pop('content') # 移出 content需要在表ArticleDetail上手动添加、关联Article
content = XSSFilter().process(content) # 对content进行数据过滤 过滤到script等标签
form.cleaned_data['blog_id'] = request.session['user_info']['blog__nid'] # 把博客id添加到cleande_data中 # dict格式cleaned_data 包含的字段{ 'blog_id' 'article_type' 'category_id' 'summary' 'title' }
obj = models.Article.objects.create(**form.cleaned_data) # Article表添加cleaned_data里的内容
# ****直接关联obj
models.ArticleDetail.objects.create(content=content, article=obj) # ArticleDetail添加数据、关联 # 第一种写法
# tag_list = [] # 准备添加第三张Article2Tag表
# for tag_id in tags: # tags ['1'] 是个列表
# tag_id = int(tag_id)
# tag_list.append(models.Article2Tag(article_id=obj.nid, tag_id=tag_id))
# # models.Article2Tag(article_id=obj.nid, tag_id=tag_id) 创建Article2Tag object对象
# print(tag_list)
# models.Article2Tag.objects.bulk_create(tag_list) # 直接添加对象 类型[obj1,obj2] # 第二种写法
for tag_id in tags: # tags ['1'] 是个列表
tag_id = int(tag_id)
models.Article2Tag.objects.create(article_id=obj.nid,tag_id=tag_id) return redirect('/backend/article-0-0.html')
else:
return render(request, 'backend_add_article.html', {'form': form})
else:
return redirect('/') def edit_article(request,nid):
"""
编辑文章
:param request:
:return:
"""
print('编辑')
blog_id = request.session['user_info']['blog__nid']
if request.method == 'GET':
obj = models.Article.objects.filter(nid=nid, blog_id=blog_id).first()
if not obj:
return render(request, 'backend_no_article.html')
tags = obj.tags.values_list('nid')
if tags:
tags = list(zip(*tags))[0]
init_dict = {
'nid': obj.nid,
'title': obj.title,
'summary': obj.summary,
'category_id': obj.category_id,
'article_type_id': obj.article_type_id,
'content': obj.articledetail.content,
'tags': tags
}
form = ArticleForm(request=request, data=init_dict)
return render(request, 'backend_edit_article.html', {'form': form, 'nid': nid})
elif request.method == 'POST':
form = ArticleForm(request=request, data=request.POST)
if form.is_valid():
obj = models.Article.objects.filter(nid=nid, blog_id=blog_id).first()
if not obj:
return render(request, 'backend_no_article.html')
with transaction.atomic():
content = form.cleaned_data.pop('content')
content = XSSFilter().process(content)
tags = form.cleaned_data.pop('tags')
models.Article.objects.filter(nid=obj.nid).update(**form.cleaned_data)
models.ArticleDetail.objects.filter(article=obj).update(content=content)
models.Article2Tag.objects.filter(article=obj).delete()
tag_list = []
for tag_id in tags:
tag_id = int(tag_id)
tag_list.append(models.Article2Tag(article_id=obj.nid, tag_id=tag_id))
models.Article2Tag.objects.bulk_create(tag_list)
return redirect('/backend/article-0-0.html')
else:
return render(request, 'backend_edit_article.html', {'form': form, 'nid': nid})
views.py
{% extends 'backend_layout.html' %}
{% block css %}
<link rel="stylesheet" href="/static/plugins/kindeditor/themes/default/default.css"/>
<style>
.kind-content {
width: 100%;
min-height: 500px;
}
</style>
{% endblock %}
{% block conent %}
<ol class="breadcrumb" style="margin-bottom: 0;">
<li><a href="#">文章管理</a></li>
<li class="active">添加文章</li>
</ol>
<div style="padding: 5px 8px;">
<form method="POST" action="/backend/add-article.html" novalidate>
{% csrf_token %}
<div class="form-group">
<label for="{{ form.title.id_for_label }}">标题 <span>{{ form.title.errors.0 }}</span></label>
{{ form.title }}
</div>
<div class="form-group">
<label for="summary">简介 <span>{{ form.summary.errors.0 }}</span></label>
{{ form.summary }}
</div>
<div class="form-group">
<label for="content">内容 <span>{{ form.content.errors.0 }}</span></label>
{{ form.content }}
</div>
<div class="form-group">
<label>类型 <span>{{ form.article_type.errors.0 }}</span></label>
<div>
{{ form.article_type_id }}
</div>
</div>
<div class="form-group">
<label>分类 <span>{{ form.category_id.errors.0 }}</span></label>
<div>
{{ form.category_id }}
</div>
</div>
<div class="form-group">
<label>标签 <span>{{ form.tags.errors.0 }}</span></label>
<div>
{{ form.tags }}
</div>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" value="保 存">
</div>
</form>
</div>
{% endblock %}
{% block js %}
<script charset="utf-8" src="/static/plugins/kindeditor/kindeditor-min.js"></script>
<script charset="utf-8" src="/static/plugins/kindeditor/lang/zh_CN.js"></script>
<script>
KindEditor.ready(function (K) {
var editor = K.create('textarea[name="content"]', {
resizeType: 1
});
});
</script>
{% endblock %}
backend_article.html
{% extends 'backend_layout.html' %}
{% block css %}
<link rel="stylesheet" href="/static/plugins/kindeditor/themes/default/default.css"/>
<style>
.kind-content {
width: 100%;
min-height: 500px;
}
</style>
{% endblock %}
{% block conent %}
<ol class="breadcrumb" style="margin-bottom: 0;">
<li><a href="#">文章管理</a></li>
<li class="active">修改文章</li>
</ol>
<div style="padding: 5px 8px;">
<form method="POST" action="/backend/edit-article-{{ nid }}.html">
{% csrf_token %}
<div class="form-group">
<label for="{{ form.title.id_for_label }}">标题 <span>{{ form.title.errors.0 }}</span></label>
{{ form.title }}
</div>
<div class="form-group">
<label for="summary">简介 <span>{{ form.summary.errors.0 }}</span></label>
{{ form.summary }}
</div>
<div class="form-group">
<label for="content">内容 <span>{{ form.content.errors.0 }}</span></label>
{{ form.content }}
</div>
<div class="form-group">
<label>类型 <span>{{ form.article_type.errors.0 }}</span></label>
<div>
{{ form.article_type_id }}
</div>
</div>
<div class="form-group">
<label>分类 <span>{{ form.category_id.errors.0 }}</span></label>
<div>
{{ form.category_id }}
</div>
</div>
<div class="form-group">
<label>标签 <span>{{ form.tags.errors.0 }}</span></label>
<div>
{{ form.tags }}
</div>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" value="保 存">
</div>
</form>
</div>
{% endblock %}
{% block js %}
<script charset="utf-8" src="/static/plugins/kindeditor/kindeditor-min.js"></script>
<script charset="utf-8" src="/static/plugins/kindeditor/lang/zh_CN.js"></script>
<script>
KindEditor.ready(function (K) {
var editor = K.create('textarea[name="content"]', {
resizeType: 1
});
});
</script>
{% endblock %}
backend_edit_article.html
最重要的是生成编辑框:
{% block js %}
<script charset="utf-8" src="/static/plugins/kindeditor/kindeditor-min.js"></script>
<script charset="utf-8" src="/static/plugins/kindeditor/lang/zh_CN.js"></script>
<script>
KindEditor.ready(function (K) {
var editor = K.create('textarea[name="content"]', {
resizeType: 1
});
});
</script>
{% endblock %}

显示具体的文章页
from django.utils.safestring import mark_safe
def detail(request, site, nid):
"""
博文详细页
:param request: http://127.0.0.1:8000/lzl/6.html
:param site: 博客地址名
:param nid: 文章ID
:return:
"""
# print(site,nid)
blog_obj = models.Blog.objects.filter(site = site).prefetch_related('user').first() # 当前用户对应的博客对象
user_obj = blog_obj.user
# print(user_obj)
article_obj = models.Article.objects.filter(nid=nid,blog=blog_obj).first() # 当前访问的文章对象
content_obj = models.Comment.objects.filter(article=article_obj) if article_obj:
detail_obj = models.ArticleDetail.objects.filter(article=article_obj).first() # 访问文章的内容
content = mark_safe(detail_obj.content) # 标记安全
return render(request, 'home_detail.html',{'article_obj':article_obj,
'content':content,
'user_obj':user_obj,
'blog_obj':blog_obj,
'content_obj':content_obj})
else: return HttpResponse('文章不存在') import json
@check_login
def update_comment(request):
data = {'status':True,'message':None}
user_id = request.session['user_info']['nid']
print(user_id)
if request.method == 'POST':
print(request.POST)
content = request.POST.get('content')
article_id = request.POST.get('article_id')
reply_id = request.POST.get('reply_id')
nickname, new_content = content_handle(content) if reply_id:
comment_obj = models.Comment.objects.filter(article_id=article_id,nid=reply_id).first()
models.Comment.objects.create(user_id=user_id, content=new_content, article_id=article_id,reply=comment_obj)
else:
models.Comment.objects.create(user_id=user_id, content=content, article_id=article_id)
return HttpResponse(json.dumps(data)) import re
def content_handle(content): #对评论内容进行处理
obj = re.match('@.+\s', content)
if obj:
print(obj.group())
nickname = re.sub('@', '', obj.group()).strip() # 用户名
print(nickname)
new_content = content.split(nickname)[1].strip() # 评论内容
print(new_content)
else:
nickname = None
new_content = content
return nickname,new_content
views.py
{% extends 'home_layout.html' %}
{% block css %}
<link rel="stylesheet" href="/static/plugins/kindeditor/themes/default/default.css"/>
{% endblock %}
{% block content %}
<div class="art-title">
<a>{{ article_obj.title }}</a>
</div>
<div class="art-content">
{{ content }}
</div>
<div class="art-recommend clearfix">
<div class="recommend">
<a href="#" class="up"
style="margin: 5px 10px;display: inline-block;padding: 5px 15px;border: 1px solid #dddddd;text-align: center;">
<i class="fa fa-thumbs-o-up fa-3" aria-hidden="true" style="font-size: 25px"></i>
<div>0</div>
</a>
<a href="#" class="down"
style="margin: 5px 30px 5px 10px;display: inline-block;padding: 5px 15px;border: 1px solid #dddddd;text-align: center;">
<i class="fa fa-thumbs-o-down fa-3" aria-hidden="true" style="font-size: 25px"></i>
<div>0</div>
</a>
</div>
</div>
<div class="art-tips clearfix">
<div class="tips">
<span class="ctime">{{ article_obj.create_time }}</span>
<a class="author">{{ user_obj.nickname }}</a>
<span class="comment-count">评论(0)</span>
<span class="read-count">阅读(0)</span>
</div>
</div>
<div id="AllanboltSignature">
<div style="border-bottom: #e0e0e0 1px dashed; border-left: #e0e0e0 1px dashed; padding: 10px; font-family: 微软雅黑; font-size: 11px; border-top: #e0e0e0 1px dashed; border-right: #e0e0e0 1px dashed; "
id="PSignature">
<div style="float:left;width:70px;">
<img src="/static/imgs/o_Warning.png" style="width:65px;height:65px">
</div>
<div style="float:left;padding-top:10px;">
<div style="padding: 1px">作者:<a href="#" target="_blank">{{ user_obj.nickname }}</a></div>
<div style="padding: 1px">出处:<a href="#" target="_blank">http://http://127.0.0.1:8000/{{ blog_obj.site }}/</a>
</div>
<div style="padding: 1px">本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接</div>
</div>
<div style="clear:both;"></div>
</div>
</div>
<div class="art-comment">
<div class="comment-title">
评论列表
</div>
<div class="comment-list">
{% for item in content_obj %}
<div class="comment-item">
<div class="reply-title clearfix">
<div class="user-info">
<span style="color: #399ab2;">#{{ forloop.counter }}</span>
<span>{{ item.create_time }}</span>
<span id="nickname"style="color: #399ab2;"> {{ item.user.nickname }}</span>
</div>
<div class="reply">
<a nid="{{ item.nid }}" onclick="Reply(this);">回复</a>
</div>
</div>
<div class="reply-body" style="font-size: 12px;color:#888">
{% if item.reply_id %}
<div class="reply-user"> @{{ item.reply.user.nickname }}</div>
{% endif %}
<div class="content">
{{ item.content }}
</div>
<div style="margin-top: 30px"> </div>
</div>
</div>
{% endfor %}
</div>
<div class="comment-list-pager">
<ul class="pagination">
<li><a href="#">«</a></li>
<li><a href="#">1</a></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">4</a></li>
<li><a href="#">5</a></li>
<li><a href="#">»</a></li>
</ul>
</div>
<div class="comment-area">
<div class="replay-comment-user"></div>
<div class="reply-area" style="position: relative;">
{% if not request.session.user_info %}
<div style="text-align:center;line-height:200px;position: absolute;top:0;left:0;right:0;bottom: 0;background-color: rgba(255,255,255,.6)">
您需要登录后才可以回帖 <a href="/login.html">登录</a> | <a href="/register.html">立即注册</a>
</div>
{% endif %}
<textarea name="content" style="width: 100%;height:200px;visibility:hidden;"></textarea>
</div>
<div class="reply-btn">
<span><span>21</span>/255字</span>
<a onclick="CommentSub();">发表回复</a>
<div id="reply_id"></div>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script charset="utf-8" src="/static/plugins/kindeditor/kindeditor-min.js"></script>
<script charset="utf-8" src="/static/plugins/kindeditor/lang/zh_CN.js"></script>
<script src="/static/js/jquery.cookie.js"></script>
<script>
var editor;
KindEditor.ready(function (K) {
editor = K.create('textarea[name="content"]', {
resizeType: 1,
allowPreviewEmoticons: false,
allowImageUpload: false,
items: [
'fontname', 'fontsize', '|', 'forecolor', 'hilitecolor', 'bold', 'italic', 'underline',
'removeformat', '|', 'justifyleft', 'justifycenter', 'justifyright', 'insertorderedlist',
'insertunorderedlist', '|', 'emoticons', 'image', 'link']
});
});
function CommentSub() {
var content = $('iframe.ke-edit-iframe').contents().find('body').text();
var reply_id = $('#reply_id').attr('reply_id');
$.ajax({
url:'/update_comment.html',
type:'POST',
data:{'content':content,'article_id':{{article_obj.nid}},'reply_id':reply_id},
headers:{'X-CSRFtoken':$.cookie('csrftoken')},
success:function (data) {
var data = JSON.parse(data);
if(data['status']){
location.reload()
}
},error:function () {
}
})
}
function Reply(ths) {
var name = $(ths).parent().prev().find('span#nickname').text();
var nickname ='@'+name+ " \n\r ";
console.log(nickname);
$('iframe.ke-edit-iframe').contents().find('body').text(nickname);
var nid = $(ths).attr('nid');
console.log(nid);
$('#reply_id').attr('reply_id',nid);
console.log($('#reply_id')[0])
}
</script>
{% endblock %}
home_detail.html
最重要的是评论功能!...
代码-》跳转
Python开发【项目】:博客后台的更多相关文章
- 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入
使用react全家桶制作博客后台管理系统 前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...
- 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(三)
系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...
- 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(五)
系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...
- 基于.NetCore开发博客项目 StarBlog - (6) 页面开发之博客文章列表
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...
- 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(二)
系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...
- 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(四)
系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...
- 有哪些关于 Python 的技术博客?
Python是一种动态解释型的编程语言,它可以在Windows.UNIX.MAC等多种操作系统以及Java..NET开发平台上使用.不过包含的内容很多,加上各种标准库.拓展库,乱花渐欲迷人眼.因此如何 ...
- 《全栈营销之如何制作个人博客》之二:php环境安装及个人博客后台搭建 让你的博客跑起来
上一节我们讲了个人博客用什么开发语言,用什么CMS系统,从这一节我们就开始真正的干货,这一节我们讨论一下PHP环境的安装,及个人博客后台的搭建,让你的博客在正常的PHP环境中运行起来,你就可以进行后台 ...
- 使用react全家桶制作博客后台管理系统
前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基于react全家桶(React.React-r ...
随机推荐
- PHP大量数据循环时内存耗尽问题的解决方案
最近在开发一个PHP程序时遇到了下面的错误:PHP Fatal error: Allowed memory size of 268 435 456 bytes exhausted错误信息显...分析: ...
- apache Storm之一-入门学习
准备工作 这个教程使用storm-starter项目里面的例子.我推荐你们下载这个项目的代码并且跟着教程一起做.先读一下:配置storm开发环境和新建一个strom项目这两篇文章把你的机器设置好. 一 ...
- _variant_t和_bstr_t
_variant_t和_bstr_t这两个类分别封装并管理VARIANT和BSTR这两种数据类型, VARIANT和BSTR这两种类型是COM中使用的数据类型. 实现_variant_t向int,St ...
- 下载mysql server安装包的时候,不登录oracle账号,实现下载
需求描述: 之前下载mysql安装包的时候,都是使用oracle账号进行登录下载,最近看到可以不登录账号 就实现下载的方法,在此记录下. 操作过程: 1.选择mysql linux服务器上的安装包,点 ...
- linux环境中,nginx安装过程
需求描述: 记录在linux平台,nginx安装的过程. 环境描述: 操作系统:Red Hat Enterprise Linux Server release 6.6 (Santiago) 操作内核版 ...
- socket小实例
服务端 using System; using System.Collections.Generic; using System.Linq; using System.Text; using Syst ...
- UVA 1203 - Argus(优先队列)
UVA 1203 - Argus 题目链接 题意:给定一些注冊命令.表示每隔时间t,运行一次编号num的指令.注冊命令结束后.给定k.输出前k个运行顺序 思路:用优先队列去搞,任务时间作为优先级.每次 ...
- hadoop命令fsck命令
在HDFS中,提供了fsck命令,用于检查HDFS上文件和目录的健康状态.获取文件的block块信息和位置信息等. 具体命令介绍: -move: 移动损坏的文件到/lost+found目录下 -del ...
- 【渗透测试学习平台】 web for pentester -7.文件包含
Example 1 输入单引号,报错,得到物理路径 可通过../../../../etc/paaswd 读取敏感信息 可包含本地文件或远程文件 https://assets.pentesterlab. ...
- eclipse的.properties文件中文显示问题
eclipse的.properties文件,默认的编码方式是iso-8859-1. 所以中文显示有问题. 按照下面的方式,把Default Encoding修改成UTF-8就可以了.