报障系统之博客主页及后台管理
个人博客:
url函数(路由系统):
"""baozhang URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/1.11/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^all/(?P<type_id>\d+)/', views.index), url(r'^login/', views.login), url(r'^logout/', views.logout), url(r'^check_code/', views.check_code), url(r'^register/', views.register),注册页面 url(r'^(?P<site>\w+)/(?P<key>((tag)|(category)|(date)))/(?P<val>\w+-*\w*).html$', views.home_filter),#筛选 url(r'^(?P<site>\w+).html$', views.home),#个人已开通博客主页 url(r'^(?P<site>\w+)/p/(?P<nid>\w+).html$', views.detail), # 文章详细 url(r'^(?P<site>\w+)/(?P<nid>\d+).html$', views.article),#评论 url(r'^up.html$', views.up),#点赞或踩 url(r'^lizhi-(?P<article_type_id>\d+)-(?P<categpry_id>\d+)-(?P<tags_id>\d+).html$', views.lizhi), url(r'^upload/', views.upload),#上传 # url(r'^openblog/', views.openblog), # url(r'^(\w+)/', views.blog), url(r'^test/', views.test), # url(r'^(\w+)/$', views.home), url(r'^', views.index),#个人主页 ]
报障系统主页
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def index(request, *args, **kwargs): """ 个人主页 :param request: :param args: :param kwargs: :return: """ # print(request.path_info) # /all/1/ /all/2/ username = request.session.get('username') obj = models.UserInfo.objects.filter(username=username).values('blog__site').first() condition = {} type_id = int(kwargs.get('type_id')) if kwargs.get('type_id') else None # 如果kwargs.get('type_id')有值时转成整型没有值等于None if type_id: condition['article_type_id'] = type_id article_list = models.Article.objects.filter(**condition) # article_type_id = models.IntegerField(choices=type_choices, default=None) type_choice_list = models.Article.type_choices # 文章分类 user_list = models.UserInfo.objects.filter() return render( request, 'index.html', { 'username': username, 'obj': obj, 'type_id': type_id, 'type_choice_list': type_choice_list, 'article_list': article_list, } )
Class Form类
注册类:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.forms import Form from django.forms import fields from django.forms import widgets from django.core.exceptions import ValidationError from django.core.validators import RegexValidator from app01 import models class RegisterForm(Form): username = fields.CharField( required=True, widget=widgets.TextInput( attrs={'class':'form-control','placeholder':'用户名为6-10个字符'} ), min_length=6, max_length=10, strip=True, error_messages={ 'required': '用户名不能为空', 'min_length':'用户名至少为6个字符', 'max_length':'用户名不超过10个字符', }, ) password = fields.CharField( required=True, widget=widgets.PasswordInput( attrs={'class':'form-control','placeholder':'密码为8-12个字符'} ), min_length=8, max_length=12, strip=True, validators=[ RegexValidator(r'((?=.*\d))^.{8,12}$','必须包含数字'), RegexValidator(r'((?=.*[a-zA-Z]))^.{8,12}','必须包含字母'), # RegexValidator(r'((?=.*[^a-zA-Z0-9]))^.{8,12}','必须包含特殊字符'), # RegexValidator(r'^.(\s){8,12}','必须包含空格'), ],#用于对密码的正则验证 error_messages={ 'required': '密码不能为空', 'min_length':'密码不能少于8个字符', 'max_length':'密码最多为12个字符!', } ) password_again = fields.CharField( required=True, widget=widgets.PasswordInput( attrs={'class':'form-control','placeholder':'密码为8-12个字符'} ), min_length=8, max_length=12, strip=True, error_messages={'required':'请再次输入密码!',} ) nickname = fields.CharField( required=True, widget=widgets.TextInput( attrs={'class':'form-control','placeholder':'请输入昵称'} ) ) email = fields.EmailField( required=True, widget=widgets.TextInput(attrs={'class':'form-control','placeholder':'请输入邮箱'}), # strip=True, # error_messages={'required':'邮箱不能为空','invalid':'请输入正确的邮箱格式'}, ) avatar = fields.FileField(widget=widgets.FileInput(attrs={'id':'imgFile','class':'f1'})) code = fields.CharField(widget=widgets.TextInput(attrs={'class':'form-control','placeholder':'请输入验证码'})) def clean_username(self): #对于username扩展验证,查看是否存在 username = self.cleaned_data['username'] users = models.UserInfo.objects.filter(username=username).count() if users:#如果用户名已存在 raise ValidationError('用户名已经存在!') return username def clean_email(self): #对于email的扩展验证,查看是否存在 email = self.cleaned_data['email'] email_count = models.UserInfo.objects.filter(email=email).count() if email_count: raise ValidationError('邮箱已经存在!') return email # def _clean_password(self):#验证两次输入密码是否一致 # password1 = self.cleaned_data['password'] # password2 = self.cleaned_data['password_again'] # if password1 and password2: # if password1 != password2: # raise ValidationError('您两次输入的密码不一致') def __init__(self,request,*args,**kwargs):#构造方法,传request参数 super(RegisterForm,self).__init__(*args,**kwargs)#完成原有form功能以外 self.request = request#再封装一个request def clean_code(self): input_code = self.cleaned_data['code'] session_code = self.request.session.get('code')#session取验证码 if input_code.upper() == session_code.upper():#验证相等时 return input_code# raise ValidationError('验证码错误') def clean(self): # 验证两次输入密码是否一致 p1 = self.cleaned_data.get('password') p2 = self.cleaned_data.get('password_again') if p1 == p2: # return self.cleaned_data return None # else: # raise ValidationError('密码不一致') self.add_error("password_again",ValidationError('密码不一致')) # except ValidationError as e: # self.add_error(name, e) # def clean(self): # #基于form对象的验证,字段全部验证通过会调用clean函数验证 # self._clean_password()#调用函数 # p1 = self.cleaned_data['password'] # p2 = self.cleaned_data['password_again'] # return p2 # def clean_password(self): # p1 = self.cleaned_data['password'] # p2 = self.cleaned_data['password_again'] return p2
登录类:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class LoginForm(forms.Form): """用户登录form验证""" username = fields.CharField( required=True, widget=widgets.TextInput(attrs={'class': 'form-control', 'placeholder': '请输入用户名'}), min_length=6, max_length=10, strip=True, error_messages={'required': '用户名不能为空', } ) password = fields.CharField( required=True, widget=widgets.TextInput(attrs={'class': 'form-control', 'placeholder': '请输入密码'}), max_length=12, min_length=8, strip=True, error_messages={'required': '密码不能为空', } ) def clean(self): username = self.cleaned_data.get('username') password = self.cleaned_data.get('password') user_list = models.UserInfo.objects.filter(username=username).first() if username and password: if not user_list: raise ValidationError('用户名不存在,请重新输入') elif password != user_list.password: raise ValidationError('密码错误')
用户登录视图函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 用户登录 def login(request): # GET请求 """ 用户登录 :param request: :return: """ if request.method == "GET": obj = LoginForm() return render(request, 'login.html', {'obj': obj}) else: # print(request.POST) # obj = LoginForm(request.POST) # errors = {} # print(obj.errors) # print(obj) # print(request.session) # if obj.is_valid(): input_code = request.POST.get('code') # print(input_code) session_code = request.session.get('code') if input_code.upper() == session_code.upper(): # 判断验证码是否正确 username = request.POST.get('username') user_list = models.UserInfo.objects.filter(username=username).first() print(user_list) if user_list: password = request.POST.get('password') if user_list.password == password: request.session['is_login'] = 'true' request.session['username'] = user_list.username return redirect('/') else: msg = '密码错误' obj = LoginForm(request.POST) return render(request, 'login.html', {'msg': msg, 'obj': obj}) else: msg = '该账号不存在' obj = LoginForm(request.POST) return render(request, 'login.html', {'msg': msg, 'obj': obj}) else: msg = '验证码错误' obj = LoginForm(request.POST) return render(request, 'login.html', {'msg': msg, 'obj': obj}) # if request.POST.get('auto_login'): # request.session.get_expiry(60*60*24*24) # # request.session['is_login'] = 'true' # # request.session['username'] = data.get('username') # print(request.session['username']) # print('123') # return redirect('/') # else: # errors['code'] = '请输入正确的验证码' # return render(request,'login.html',{'obj':obj,'errors':errors}) # return render(request,'login.html',{'obj':obj})
退出登录:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def logout(request): """ 用户退出登录 :param request: :return: """ try: # 删除is_login对应的value值 del request.session['is_login'] del request.session['username'] except KeyError: pass return redirect('/login/') #验证码
获取验证码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#验证码 def check_code(request): """ #读取硬盘中的文件,在页面显示 # f = open('static/images/aa.png','rb') # data = f.read() # f.close() # return HttpResponse(data) #先写到本地,再读出到页面 # from PIL import Image # f = open('code.png','wb') # img = Image.new(mode='RGB',size=(120,30),color=(255,255,255)) # img.save(f,'png') # f.close() # # f = open('code.png','rb') # data = f.read() # f.close() #内存开辟一块空间 from PIL import Image,ImageDraw,ImageFont from io import BytesIO f = BytesIO() img = Image.new(mode='RGB',size=(120,30),color=(255,255,255))#图片对象 draw = ImageDraw.Draw(img,mode='RGB')#画笔对象 #画点 draw.point([10,10],fill="red") draw.point([30,10],fill="red") #画线 draw.line((15,10,50,50),fill='red') draw.line((45,20,100,100),fill=(0,255,0)) #画圆圈 draw.arc((0,0,30,30),0,360,fill="red") #写文本内容 # draw.text([0,0],'python',"red") # font = ImageFont.truetype("kumo.ttf",28) # draw.text([0,0],'python',(0,255,0),font=font) import random #生成随机数 # char_list = [] # for i in range(5): # char = chr(random.randint(65,90)) # char_list.append(char) # ''.join(char_list) # v = ''.join([chr(random.randint(65,90)) for i in range(5)]) char_list = [] for i in range(5): char = chr(random.randint(65,90)) char_list.append(char)#保存写的随机字符 font = ImageFont.truetype("kumo.ttf",28) draw.text([i*24,0],char,(random.randint(0,255),random.randint(0,255),random.randint(0,255)),font=font) # code = ''.join(char_list) img.save(f,'png') data = f.getvalue()#读取内存里的值 code = ''.join(char_list) print(request.session)#<django.contrib.sessions.backends.db.SessionStore object at 0x00000258DB88DC88> request.session['code'] = code#保存在session里 """ from io import BytesIO from utils.random_check_code import rd_check_code stream = BytesIO() img, code = rd_check_code() img.save(stream, 'png') # data = stream.getvalue() request.session['code'] = code return HttpResponse(stream.getvalue())
用户注册时上传头像:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import os #用户上传图片 def upload(request): print(request.POST, request.FILES) file_obj = request.FILES.get('imgUrl') #取文件时,需以FILES获取文件数据 file_path = os.path.join('static/img/', file_obj.name) print(file_path) with open(file_path, 'wb') as f: # 以wb方式写入到指定目录(bytes格式写入) # 写字节方式打开空文件,拼接文件路径 for trunk in file_obj.chunks(): # 写入到指定目录 f.write(trunk) return HttpResponse("/" + file_path)
用户注册:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def register(request): """ 用户注册 :param request: :return: """ if request.method == "GET": obj = RegisterForm(request) return render(request, 'register.html', {'obj': obj}) else: # 验证码 obj = RegisterForm(request, request.POST, request.FILES) if obj.is_valid(): print(type(obj.cleaned_data)) dict = {} dict['username'] = obj.cleaned_data['username'] dict['password'] = obj.cleaned_data['password'] dict['nickname'] = obj.cleaned_data['nickname'] dict['email'] = obj.cleaned_data['email'] dict['avatar'] = obj.cleaned_data['avatar'] models.UserInfo.objects.create(**dict) return redirect('/') else: # print(obj.errors['__all__']) # print(obj.errors[NON_FIELD_ERRORS]) """ <ul class="errorlist nonfield"><li>密码不一致</li></ul> <ul class="errorlist nonfield"><li>密码不一致</li></ul> """ # obj.errors是一个字典 # - 对于Form组件错误信息 """ { __all__: [错误1,错误2] user: [错误1,错误2] password: [错误1,错误2] } """ return render(request, 'register.html', {'obj': obj})
个人博客主页:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def home(request,site): """ #个人博客主页 :param request: :param site: :return: """ # condition= {} # type_id = int(kwargs.get('type_id')) if kwargs.get('type_id') else None # if type_id: # condition['article_type_id'] = type_id # type_choice_list = models.Article.type_choices blog = models.Blog.objects.filter(site=site).first() # 创建一个博客对象 if not blog: return redirect('/') # print(site) #按照:标签,分类,时间 # 标签 tag_list = models.Article2Tag.objects.filter(article__blog=blog).values('tag_id', 'tag__title').annotate(ct=Count('id')) #分类 category_list = models.Article.objects.filter(blog=blog).values('category_id','category__title').annotate(ct=Count('nid'))#queryset[字典1,字典2......] #时间 date_list = models.Article.objects.filter(blog=blog).extra(select={'ctime':"date_format(create_time,'%%Y-%%m')"}).values('ctime').order_by('-ctime').annotate(ct=Count('nid')) #文章分页 all_count = models.Article.objects.all().count() page_info = PageInfo(request.GET.get('page'), all_count, 2, '/bingabcd.html', 11) article_list = models.Article.objects.all()[page_info.start():page_info.end()] # #获取时间分组 # # date_list = models.Article.objects.filter(blog=blog).extra(select={'c':"date_format(create_time,'%%Y-%%m')"}).values('c').order_by('-c').annotate(ct=Count('nid')) """ nid,title """ return render( request, 'home.html', { 'site':site,#博客后缀 'blog': blog,#博客对象 'article_list':article_list,#文章列表 'page_info':page_info,#分页 'tag_list':tag_list,#标签 'category_list':category_list,#分类 'date_list':date_list,#时间 } ) # 1.当前博客的所有文章
个人博客文章筛选:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#个人博客筛选 def home_filter(request,site,key,val): """ 个人博客筛选 :param request: :param site: :param key: :param val: :return: """ # blog = models.Blog.objects.filter(site=site).select_related('user').first()#related_name直接连表,如果做for循环query_set对象时,减少查询次数 # print(blog) # if not blog: # return redirect('/') blog = models.Blog.objects.filter(site=site).first() # 创建一个博客对象 print(blog) if not blog: return redirect('/') #按照:标签,分类,时间 #标签 tag_list = models.Article2Tag.objects.filter(article__blog=blog).values('tag_id','tag__title').annotate(ct=Count('id')) print(tag_list) print(tag_list.query) #分类 category_list = models.Article.objects.filter(blog=blog).values('category_id','category__title').annotate(ct=Count('nid'))#queryset[字典1,字典2......] print(category_list) print(category_list.query) #时间 date_list = models.Article.objects.filter(blog=blog).extra(select={'ctime':"date_format(create_time,'%%Y-%%m')"}).values('ctime').order_by('-ctime').annotate(ct=Count('nid')) print(date_list.query) print('1111') #文章分页 all_count = models.Article.objects.all().count()#总页数 page_info = PageInfo(request.GET.get('page'), all_count, 2, '/bingabcd.html', 11) if key == 'tag': article_list = models.Article.objects.filter(tags__nid=val,blog=blog)[page_info.start():page_info.end()] #文章列表 print(article_list) # v = models.Article.objects.filter(blog=blog,article2tag__tag__title=val) # print(v.query) # #自定义第三张表 # #自己反向关联 # v = models.Article.objects.filter(blog=blog,article2tag__tag=val) # #通过M2M字段 # v = models.Article.objects.filter(blog=blog,tags__nid=val) elif key == 'category': article_list = models.Article.objects.filter(category_id=val,blog=blog)[page_info.start():page_info.end()] #文章列表 print(article_list) else: # article_list = models.Article.objects.filter(create_time=val,blog=blog).all()[page_info.start():page_info.end()]#文章列表 article_list = models.Article.objects.filter(blog=blog).extra(where=["date_format(create_time,'%%Y-%%m')=%s"],params=[val,])[page_info.start():page_info.end()] print(article_list) return render( request, 'home_filter.html', { 'blog':blog, 'tag_list':tag_list, 'category_list':category_list, 'date_list':date_list, 'article_list':article_list, 'page_info':page_info } )
查看文章详细页:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#查看文章详细页 def detail(request,*args,**kwargs):#文章详细 """ 查看文章详细页 :param request: :return: """ site = kwargs.get("site") nid = kwargs.get('nid') url = request.path_info user_id = request.session.get('user_id') # 博客信息 blog = models.Blog.objects.filter(site=site).first() if user_id: userinfo = models.UserInfo.objects.filter(nid=user_id).first() else: userinfo = False #文章 obj = models.Article.objects.filter(blog__site=site,nid=nid).first() print(obj) #分类列表 category_list = models.Article.objects.filter(blog__site=site).values('category__title','category_id').annotate(c=Count('nid')) #标签列表 tag_list = models.Article.objects.filter(blog__site=site).values('tags__title','tags__nid').annotate(c=Count('nid')) #时间列表 date_list = models.Article.objects.filter(blog=blog).extra(select={'c':"date_format(create_time,'%%Y-%%m')"}).values('c').order_by('-c').annotate(ct=Count('nid')) comment_list = [] com = [] comment = enumerate(obj.comment_set.all()) for i,j in comment: com = [] com.append(i+1) com.append(j) comment_list.append(com) return render( request, 'article_detail.html', { 'url':url, 'obj':obj, 'blog':blog, 'date_list':date_list, 'category_list': category_list, 'tag_list': tag_list, 'userinfo':userinfo, 'comment':comment_list, } )
文章中赞或者踩:
待补充:
博客系统后台管理:
后台管理:
组合筛选:
第一种方式:用for循环:
views.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def lizhi(request,**kwargs): print(kwargs)#{'article_type_id': '1', 'category_id': '1', 'tags_id': '0'}#从url传过来的数据 condition = {} for k,v in kwargs.items(): kwargs[k] = int(v) if v != '0': condition[k] = v print(condition)#{'article_type_id': '1', 'category_id': '1'} # 大分类 type_list = models.Article.type_choices print(type_list)#[(1, 'Python'), (2, 'Linux'), (3, 'OpenStack'), (4, 'GoLang'), (5, 'Car')] #个人分类 category_list = models.Category.objects.filter(blog_id=1)# print(category_list)#<QuerySet [<Category: bingabcd-Python>, <Category: bingabcd-设计模式>, <Category: bingabcd-机器人>]> #个人标签 tag_list = models.Tag.objects.filter(blog_id=1) print(tag_list)#<QuerySet [<Tag: bingabcd-Python之路>, <Tag: bingabcd-Django>, <Tag: bingabcd-HTML>, <Tag: bingabcd-机器人>, <Tag: bingabcd-人工智能>]> # 对文章进行筛选 condition['blog_id']=1 article_list = models.Article.objects.filter(**condition) print(article_list)#<QuerySet [<Article: bingabcd-Python>, <Article: bingabcd-机器人>, <Article: bingabcd-Python基本数据类型>, <Article: bingabcd-Django基础>]> return render(request, 'lizhi.html', { 'type_list':type_list, 'category_list':category_list, 'tag_list':tag_list, 'article_list':article_list, 'kwargs':kwargs } )
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <style> .condition a{ display: inline-block; padding: 5px; } .condition a.active{ background-color: #204d74; color: white; } </style> </head> <body> <h3>筛选</h3> <div class="condition"> 大大分类: {% if kwargs.article_type_id == 0 %} <a class="active" href="/lizhi-0-{{ kwargs.category_id }}-{{ kwargs.tags__nid }}.html">全部</a> {% else %} <a href="/lizhi-0-{{ kwargs.category_id }}-{{ kwargs.tags__nid }}.html">全部</a> {% endif %} {% for row in type_list %} {% if row.0 == kwargs.article_type_id %} <a class="active" href="/lizhi-{{ row.0 }}-{{ kwargs.category_id }}-{{ kwargs.tags__nid }}.html">{{ row.1 }}</a> {% else %} <a href="/lizhi-{{ row.0 }}-{{ kwargs.category_id }}-{{ kwargs.tags__nid }}.html">{{ row.1 }}</a> {% endif %} {% endfor %} <div> 个人分类 <a href="#">全部</a> {% for row in category_list %} <a href="{{ row.nid }}">{{ row.title }}</a> {% endfor %} </div> <div> 个人标签 <a href="#">全部</a> {% for row in tag_list %} <a href="{{ row.nid }}">{{ row.title }}</a> {% endfor %} </div> <div> <h3>结果</h3> {% for row in article_list %} <h4><a href="#">{{ row.title }}</a></h4> <div>{{ row.summary }}</div> {% endfor %} </div> {# 大大分类:#} {# {% if kwargs.article_type_id == 0 %}#} {# <a class="active" href="/lizhi-0-{{ kwargs.category_id }}-{{ kwargs.tags__nid }}.html">全部</a>#} {# {% else %}#} {# <a href="/lizhi-0-{{ kwargs.category_id }}"></a>#} {# {% endif %}#} </div> </body> </html>
组合搜索功能还未完善:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#URL #urls.py # url(r'^screen-(?P<article_type_id>\d+)-(?P<category_id>\d+)-(?P<article2tag__tag_id>\d+).html$', views.screen), url(r'^screen-(?P<article_type_id>\d+)-(?P<category_id>\d+)-(?P<tags__nid>\d+).html$', views.screen), #视图函数 #views.py def screen(request,**kwargs): # print(kwargs) condition = {} for k,v in kwargs.items(): kwargs[k] = int(v) if v != '0': condition[k] = v print(condition) #大分类 type_list = models.Article.type_choices #个人分类 catagory_list = models.Category.objects.filter(blog_id=1) #个人标签 tag_list = models.Tag.objects.filter(blog_id=1) #进行筛选 condition['blog_id']=1 article_list = models.Article.objects.filter(**condition) return render(request,'screen.html',{ 'type_list':type_list, 'catagory_list':catagory_list, 'tag_list':tag_list, 'article_list':article_list, 'kwargs':kwargs, }) #模板语言 #screen.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .condition a{ display: inline-block; padding: 5px; } .condition a.active{ background-color: #0a386a; color: white; } </style> </head> <body> <h3>筛选</h3> <div class="condition"> 大大分类: {% if kwargs.article_type_id == 0 %} <a class="active" href="/screen-0-{{ kwargs.category_id }}-{{ kwargs.tags__nid }}.html">全部</a> {% else %} <a href="/screen-0-{{ kwargs.category_id }}-{{ kwargs.tags__nid }}.html">全部</a> {% endif %} {% for row in type_list %} {% if row.0 == kwargs.article_type_id %} <a class="active" href="/screen-{{ row.0 }}-{{ kwargs.category_id }}-{{ kwargs.tags__nid }}.html">{{ row.1 }}</a> {% else %} <a href="/screen-{{ row.0 }}-{{ kwargs.category_id }}-{{ kwargs.tags__nid }}.html">{{ row.1 }}</a> {% endif %} {% endfor %} </div> <div class="condition"> 个人分类: <a href="#">全部</a> {% for row in catagory_list %} <a href="{{ row.nid }}">{{ row.title }}</a> {% endfor %} </div> <div class="condition"> 个人标签: <a href="#">全部</a> {% for row in tag_list %} <a href="{{ row.nid }}">{{ row.title }}</a> {% endfor %} </div> <h3>结果</h3> {% for row in article_list %} <div> <h4><a href="#">{{ row.title }}</a></h4> <div>{{ row.summary }}</div> </div> {% endfor %} </body> </html> 报障系统后台管理组合筛选
第二种方式:用simple_tag+filter
KindEditor上传图片:
参考文档:KindEditor
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#URL路由 #urls.py url(r'^upload_img.html$', views.upload_img), #视图函数 #views.py def upload_img(request): import os # print(request.POST, request.FILES) # upload_type = request.GET.get('dir') # 根据上传得文件类型控制上传得文件目录 file_obj = request.FILES.get('imgFile') file_path = os.path.join('static/img',file_obj.name) with open(file_path,'wb') as f: for chunk in file_obj.chunks(): f.write(chunk) dic = { 'error':0, 'url':'/'+file_path, 'message':'错误了...' } import json return HttpResponse(json.dumps(dic)) #模板语言 #editor.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="POST" action="/editor.html" novalidate> {#如果不加novalidate,{{ obj.content }}会报错#} <p> 文章标题 {{ obj.title }} </p> {% csrf_token %} <div> <div>文章内容</div> <div> {{ obj.content }} </div> </div> <input type="submit" value="提交"> </form> <script src="/static/kindeditor-4.1.10/kindeditor-all.js"></script> <script> KindEditor.create("#i1",{ width: "700px", height: "300px", uploadJson: '/upload_img.html', extraFileUploadParams: { "csrfmiddlewaretoken":"{{ csrf_token }}" {# 需要添加CSRF验证#} } }) </script> </body> </html> KindEditor上传图片
补充:
文件上传其实内部就是iframe+form 伪Ajax操作
input type='file' name='imgFile' 提交
可以通过filePostName 更改默认name属性:
filePostName: 'fafafa'
BeautifulSoup模块基本使用:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
beautifulsoup4的基本使用 安装: pip3 install beautifulsoup4 导入模块: from bs4 import BeautifulSoup valid_tag = [] #只能设置标签名的白名单 valid_tag = {} #既能加标签名又能加标签的属性的白名单 tag.name 获取标签名 soup.find() #查找第一个标签 soup.find_all() #查找所有的p标签 tag.clear() #清除标签中的内容 tag.decompose() #清空标签中的内容并且删除标签 decode() soup对象转换成字符串 encode() soup对象转换成字节 #示例: content = """ <p id='i1' a='123' b='999'> <script>alert(123)</script> </p> <p id='i2'> <div> <p>asfjldjf</p> </div> <img id='i3' src="/static/img\lang.jpg" alt="" /> </p> """ from bs4 import BeautifulSoup soup = BeautifulSoup(content,'html.parser') #设置名叫valid_tag的白名单: # valid_tag = ['p','img','div'] #只能放置标签名,列表形式 valid_tag = { #既能加标签名又能加标签的属性,字典形式。 'p':['class','id'], 'img':['src'], 'div':['class'] } # v=soup.find(name='p',attrs={'id':'i2'}) #查找第一个p标签,并且id是i2 # print(v) # tag = soup.find(name='p') #查找第一个p标签,生成的是对象的形式,可以通过“.”形式继续查找 # sc=tag.find('script') # print(sc) # v=soup.find_all(name='p') #查找所有的p标签 # print(v) # tags = soup.find_all() #查找所有的标签 # for tag in tags: #tag.name是标签名 # if tag.name not in valid_tag: #如果标签名不在白名单中则情况标签中的内容 # tag.clear() tags = soup.find_all() for tag in tags: if tag.name not in valid_tag: tag.decompose() #删除不再白名单中的标签 if tag.attrs: for k in list(tag.attrs.keys()): #{id:'i1',a=123,b=999} if k not in valid_tag[tag.name]: del tag.attrs[k] content_str=soup.decode() #去掉特殊标签后,拿到它的字符串 print(content_str) #打印过滤后的标签字符串 beautifulsoup4的基本使用
基于KindEditor和BeautifuSoup实现防止XSS攻击:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
基于KindEditor和BeautifuSoup实现防止XSS攻击 #URL路由系统 #urls.py url(r'^editor.html$', views.editor), #可视化编辑器 url(r'^see.html$', views.see), #查看可视化编辑器生成的样式 url(r'^upload_img.html$', views.upload_img), #上传图片 #视图函数 #views.py CONTENT = "" from app01.forms import ArticleForm def editor(request): if request.method=="GET": obj = ArticleForm() return render(request,'editor.html',{'obj':obj}) else: obj = ArticleForm(request.POST) if obj.is_valid(): content = obj.cleaned_data['content'] global CONTENT CONTENT = content print(content) return HttpResponse("...") def see(request): return render(request,'see.html',{'con':CONTENT}) def upload_img(request): import os # print(request.POST, request.FILES) # upload_type = request.GET.get('dir') # 根据上传得文件类型控制上传得文件目录 file_obj = request.FILES.get('imgFile') file_path = os.path.join('static/img',file_obj.name) with open(file_path,'wb') as f: for chunk in file_obj.chunks(): f.write(chunk) dic = { 'error':0, 'url':'/'+file_path, 'message':'错误了...' } import json return HttpResponse(json.dumps(dic)) #模板语言 #editor.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="POST" action="/editor.html" novalidate> {# 如果不加novalidate,{{ obj.content }}会报错#} <p> 文章标题 {# <input type="text" name="title">#} {{ obj.title }} </p> {# <p>#} {# 选择分类#} {# <select name="" id="">#} {##} {# </select>#} {# </p>#} {# <p>#} {# 选择标签#} {# <input type="checkbox">#} {# <input type="checkbox">#} {# <input type="checkbox">#} {# <input type="checkbox">#} {# </p>#} {% csrf_token %} <div> <div>文章内容</div> <div> {# <textarea name="content" id="i1" cols="30" rows="10"></textarea>#} {{ obj.content }} </div> </div> <input type="submit" value="提交"> </form> <script src="/static/kindeditor-4.1.10/kindeditor-all.js"></script> <script> KindEditor.create("#i1",{ width: "700px", height: "300px", {# items: [ 'source', '|', 'undo', 'redo', '|', 'preview', 'print', 'template', 'code', 'cut', 'copy', 'paste',],#} {# noDisableItems: ['undo','redo'],#} {# designMode: false,#} {# resizeType:1,#} uploadJson: '/upload_img.html', extraFileUploadParams: { "csrfmiddlewaretoken":"{{ csrf_token }}" {# 需要添加CSRF验证#} } }) </script> </body> </html> #Form组件 #forms.py from django.forms import Form from django.forms import fields from django.forms import widgets from django.core.exceptions import ValidationError class ArticleForm(Form): title = fields.CharField(max_length=64) content = fields.CharField( widget=widgets.Textarea(attrs={'id':'i1'}) ) def clean_content(self): from bs4 import BeautifulSoup valid_tag = { 'p': ['class', 'id'], 'img': ['src'], 'div': ['class'] } old=self.cleaned_data['content'] soup = BeautifulSoup(old, 'html.parser') tags = soup.find_all() for tag in tags: if tag.name not in valid_tag: tag.decompose() # 删除不再白名单中的标签 if tag.attrs: for k in list(tag.attrs.keys()): # {id:'i1',a=123,b=999} if k not in valid_tag[tag.name]: del tag.attrs[k] content_str = soup.decode() return content_str 防止XSS攻击
补充:
防止XSS攻击为什么不用黑名单,要用白名单?
使用白名单过滤html标签比黑名单操作起来更简单,把攻击锁定在自己可控制范围内。
参考博客:http://www.cnblogs.com/wupeiqi/articles/6283017.html
你现在所遭遇的每一个不幸,都来自一个不肯努力的曾经。大数据SRE(运维开发)学习交流群239963844
posted on 2017-07-20 01:16 bigdata_devops 阅读(468) 评论(0) 编辑 收藏 举报