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 ...
随机推荐
- mysql5.7 编码统一utf-8
查看mysql数据库编码: show variables like 'character%'; mysql> show variables like 'character%'; +------- ...
- delphi7中添加QuickRep
具体的方法是: delphi主菜单的Project|Options命令, 在Package选项卡的Desing packages列表中如果可以看到QuickReport Components选项, 那 ...
- centos7,yum安装工具报错
1.问题描述:yum安装gcc和其他的工具时一直报错: 2.问题解决: 网上看到有类似文章: No more mirrors to try. 得知这可能是错误的缓存源导致,直接两个命令解决: yum ...
- javascript与 ios通讯解决办法
阔别1年半之久,一个JavaScript和ios通讯的想法终于被实现了(我不知道别人有没有早就实现过~). 记得早期ios内嵌html做通讯时,貌似做好的办法只能是 ios通过url来截取页面发送消息 ...
- docker images 详解
docker images 用于查看本地已下载的镜像 [root@localhost ~]$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE ce ...
- mybatis-spring-1.2.1 jar下载、源码下载
http://www.everycoding.com/maven2/org/mybatis/mybatis-spring/1.2.1.html
- java基础---->java中国际化的实现
应用程序的功能和代码设计考虑在不同地区运行的需要,其代码简化了不同本地版本的生产.开发这样的程序的过程,就称为国际化.今天,我们就开始学习java中国际化的代码实现. Java国际化主要通过如下3个类 ...
- 深入浅出Docker(四):Docker的集成测试部署之道
1. 背景 敏捷开发已经流行了很长时间,如今有越来越多的企业开始践行敏捷开发所提倡的以人为中心.迭代.循序渐进的开发理念.在这样的场景下引入Docker技术,首要目的就是使用Docker提供的虚拟化方 ...
- setTimeout原来有这种用途
setTimeout有两个参数,第一个是需要执行的函数,第二个是将该函数推入UI队列的时间. 需要注意的两点: 1.第二个参数中设置的时间,是从执行setTimeout开始计算,而不是从整个函数执行完 ...
- Weinre 远程调试移动端手机web页面
调试场景 1.调试页面在手机上.2.调试工具在PC的chrome3.手机跟pc要在同一个网络环境下,也就是都使用一个wifi 一.安装 Weinre 1.Weinre是基于nodejs实现的,所以使用 ...