概述
通过自己写的博客后台代码、思路,来与武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'), ]
我的代码:

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 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') == '0': 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))

<!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="0"> 一个月内自动登陆 </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 }
武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 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))

<!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="1" 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>
区别:
①、前后端交互
前后端交互这块,自己没有活学活用,上课时老师讲课时讲到直接把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)

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))

<!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>
代码有意思的地方:
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})

<!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>
关键的几段代码:
前端对时间进行处理:
<span>发布于 {{ item.create_time|date:"Y-m-d H:i:s" }}</span>
后台个人信息修改

@check_login def base_info(request): """博主个人信息""" if request.method == 'GET': blogTheme = [ {'id': '0', 'name': '默认主题'}, {'id': '1', 'name': '黑不溜秋'}, {'id': '2', 'name': '乌七八啦'}, {'id': '3', 'name': '红色火焰'}, {'id': '4', '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))

{% 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 %}
里面包含了图片的点击上传,更新 非常重要
文章列表+组合检索

@check_login def article(request,*args, **kwargs): """ 博主个人文章管理 """ blog_id = request.session['user_info']['blog__nid'] condition = {} for k, v in kwargs.items(): if v == '0': 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 } )

{% 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 %}
组合搜索-跳转
套路之新建文章和编辑文章

@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})

{% 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 %}

{% 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 %}
最重要的是生成编辑框:
{% 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

{% 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 %}
最重要的是评论功能!...
代码-》跳转