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 ...
随机推荐
- Xcode 5.0 编译低版本app
Xcode 5.0 默认的编译环境是iOS7,编译出来的app,安装到iOS7.0版本以上的手机上,会表现出iOS7.0的风格.兼容不太好的应用,布局上可能会因此乱八七糟. 如果还不想让app升级到i ...
- c++ mktime()
今天联系写一个日历的程序,需要算出月份中的第一天是星期几,用到了mktime()这个函数,感觉这个函数挺有用的,分享给大家. 原型:time_t mktime(struct tm *) 其中的tm结构 ...
- protobuf语法
是什么? 目前市面上的unity手游开发主流数据通讯协议的解决方案.protobuf是google提供的一个开源序列化框架,类似于XML,JSON这样的数据表示语言,其最大的特点是基于二进制,因此比传 ...
- SVN目录权限设置
---恢复内容开始--- 如图,这里我建的项目库为myRepositories,其下边又有许多文件,现在要分别对每个文件进行svn权限配置. 配置 进入上面生成的文件夹conf下,进行配置.有以下几个 ...
- 什么是集群(Cluster)技术
什么是集群(Cluster)技术Cluster集群技术可如下定义:一组相互独立的服务器在网络中表现为单一的系统,并以单一系统的模式加以管理.此单一系统为客户工作站提供高可*性的服务.大多数模式下,集群 ...
- [Scikit-learn] Dynamic Bayesian Network - HMM
Warning The sklearn.hmm module has now been deprecated due to it no longer matching the scope and th ...
- VS------修改项目命名空间
1.以文本形式打开此文件 2.修改一下部分 3.vs会自动提示,选择“放弃”即可
- 7 -- Spring的基本用法 -- 3... Spring 的核心机制 : 依赖注入
7.3 Spring 的核心机制 : 依赖注入 Spring 框架的核心功能有两个. Spring容器作为超级大工厂,负责创建.管理所有的Java对象,这些Java对象被称为Bean. Spring容 ...
- ref 属性使用eslint报错
react 使用 ref 报错 ,[eslint] Using string literals in ref attributes is deprecated. (react/no-string-re ...
- js 小数取整,js 小数向上取整,js小数向下取整
js 小数取整,js 小数向上取整,js小数向下取整 >>>>>>>>>>>>>>>>>>& ...