import requests
import json
import logging
import qiniu
from datetime import datetime
from urllib.parse import urlencode
from django.core.paginator import Paginator, EmptyPage
from django.db.models import Count
from django.shortcuts import render
from django.views import View
from django.http import JsonResponse,Http404
from django.contrib.auth.models import Group, Permission
from django.contrib.auth.mixins import LoginRequiredMixin,PermissionRequiredMixin
from apps.news import models
from apps.admin import constants
from apps.doc.models import Doc
from apps.course.models import Course,Teacher,CourseCategory
from apps.users.models import Users
from . import forms
from web_prv import settings
from utils.json_fun import to_json_data
from utils.res_code import Code, error_map
from utils import paginator_script
from utils.fastdfs.fdfs import FDFS_Client
from utils.secrets import qiniu_secret_info
logger = logging.getLogger('django')
class IndexView(LoginRequiredMixin,View):
"""
/admin/
"""
login_url = 'users:login' #没登录就跳转到登录页面,或者在 settings.py 中设置
redirect_field_name = 'next' #重定向,'next' 和前端对应
def get(self,request):
"""
:param request:
:return:
"""
return render(request,'admin/index/index.html')
class TagManagerView(PermissionRequiredMixin,View):
"""
/admin/tag/
"""
#'app_name.codename'
#Tag 模型在 news app 中,对应的 codename 是 add_tag
permission_required = ('news.add_tag','news.view_tag')
raise_exception = True #没权限就返回报错页面
def handle_no_permission(self):
"""
该方法默认返回报错页面
get 返回页面渲染,不用改, post 返回 json 格式,改为报错的 json 格式
:return:
"""
# Python lower() 方法转换字符串中所有大写字符为小写,其效果和 casefold() 方法非常相似。
# 两者的区别是:lower() 方法只对ASCII编码,也就是‘A - Z’有效,
# 对于其他语言(非汉语或英文)中把大写转换为小写的情况只能用 casefold() 方法。
#lower() 转换字符串中所有的大小写为小写, 对应的是 upper()
if self.request.method.lower() != 'get':
return to_json_data(errno=Code.ROLEERR,errmsg='没有操作权限')
else:
return super(TagManagerView,self).handle_no_permission()
def get(self,request):
"""
要返回给前端: tag_name news_name
:param request:
:return:
"""
#分组查询 annotate 配合 values 使用 values 指定输出的内容(id: ,name: ),是字典格式
#按标签名(tag_name)和 id(tag_id) 分组,统计每个标签(Tag)中 news(外键关联,
# 通过 news的主键 id 统计) 的数量,并且按降序排列
tags = models.Tag.objects.select_related('news').values('id','name').annotate(num_news = \
Count('news')).filter(is_delete=False).order_by('-num_news','update_time')
return render(request,'admin/news/tag_manage.html',locals())
def post(self,request):
"""
get_or_create(defaults=None, **kwargs)
一个通过给出的kwargs 来查询对象的便捷方法(如果你的模型中的所有字段都有默认值,可以为空),需要的话创建一个对象。
返回一个由(object, created)组成的元组,元组中的object 是一个查询到的或者是被创建的对象,
created 是一个表示是否创建了新的对象的布尔值。
:param request:
:return:
"""
# 1. 获取参数
json_data = request.body
if not json_data:
return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
dict_data = json.loads(json_data.decode('utf8'))
#从前端传进来的要修改的标签名
tag_name = dict_data.get('name')
if tag_name:
# tag_name = tag_name.strip()
# a = tag_name
#get_or_create 查名为 tag_name 的标签,无则增,有则查 得到的是元组类型 请确保只在 post 请求中使用它
tag_tuple = models.Tag.objects.filter(is_delete=False).get_or_create(name=tag_name)
#对得到的元组进行拆包
tag_instance,tag_boolean = tag_tuple
#如果 tag_boolean 为 True,即创建了名字为 tag_name 的标签
# 否则就是查到了名字为 tag_name 的标签,即标签已存在,不创建
if tag_boolean:
news_tag_dict = {
'id':'tag_instance.id',
'name':'tag_instance.name',
}
return to_json_data(errmsg='标签创建成功!',data=news_tag_dict)
else:
return to_json_data(errno=Code.DATAEXIST,errmsg='标签已存在!')
else:
return to_json_data(errno=Code.PARAMERR,errmsg='标签不能为空!')
#传参:
class TagEditView(PermissionRequiredMixin,View):
"""
/admin/tag//
"""
permission_required = ('news.delete_tag','news.change_tag')
raise_exception = True
def handle_no_permission(self):
"""
delete,put 都返回 json 格式,所以不用判断,都改为报错的 json 格式,和 TagManagerView 中的 handle_no_permission 不一样
:return:
"""
return to_json_data(errno=Code.ROLEERR,errmsg='没有操作权限')
def delete(self,request,tag_id):
"""
:param request:
:param tag_id:
:return:
"""
tag = models.Tag.objects.only('id').filter(id=tag_id).first()
if tag:
tag.is_delete = True
#只改 is_delete 字段,用于优化
tag.save(update_fields=['is_delete'])
#删除后局部刷新,用 ajax,返回给前端 json 格式
return to_json_data(errmsg='标签删除成功!')
else:
return to_json_data(errno=Code.PARAMERR,errmsg='标签不存在!')
def put(self,request,tag_id):
"""
:param request:
:param tag_id:
:return:
"""
#修改之后的 name 通过 ajax 获取 只有一个参数,用 ajax 即可
# 1. 获取参数
json_data = request.body #byte str
if not json_data:
return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
dict_data = json.loads(json_data.decode('utf8'))
# 从前端传进 要修改的 tag
tag_name = dict_data.get('name')
# 从数据库中拿到 tag
tag = models.Tag.objects.only('name').filter(id=tag_id).first()
if tag:
#去掉两边的空格
tag_name = tag_name.strip()
#如果不加 is_delete 删除的标签再创建或者修改无法实现
if tag.name == tag_name:
return to_json_data(errno=Code.PARAMERR,errmsg='标签未变化!')
if not models.Tag.objects.only('id').filter(name=tag_name, is_delete=False).exists():
#如果标签没变化,提示报错
# if tag.name == tag_name:
# return to_json_data(errno=Code.PARAMERR,errmsg='标签未变化!')
tag.name = tag_name
tag.save(update_fields=['name', 'update_time'])
return to_json_data(errmsg='标签更新成功!')
else:
return to_json_data(errno=Code.PARAMERR,errmsg='标签已经存在!')
else:
#有异常的一定会用到 errno
return to_json_data(errno=Code.PARAMERR,errmsg='标签不存在!')
# 热门文章管理页面
#news.title,tag.name,hotnews.priority
class HotNewsManageView(PermissionRequiredMixin,View):
"""
/admin/hotnews/
"""
permission_required = ('news.view_hotnews')
raise_exception = True
#因为只有 get,返回的是页面,handle_no_permission 默认返回报错页面,所以不用重写
def get(self,request):
hot_news = models.HotNews.objects.select_related('news__tag').only('news__title','news__tag__name','news__id').\
filter(is_delete=False).order_by('priority','-news__clicks')[:constants.SHOW_HOTNEWS_COUNT]
return render(request,'admin/news/news_hot.html',locals())
class HotNewsEditView(PermissionRequiredMixin,View):
"""
/admin/hotnews/
"""
permission_required = ('news.delete_hotnews','news.change_hotnews')
raise_exception = True
def handle_no_permission(self):
return to_json_data(errno=Code.ROLEERR,errmsg='没有操作权限')
def delete(self,request,hotnews_id):
hotnews = models.HotNews.objects.only('id').filter(id=hotnews_id).first()
if hotnews:
hotnews.is_delete = True
hotnews.save(update_fields = ['is_delete','update_time'])
return to_json_data(errmsg='热门文章删除成功!')
else:
return to_json_data(errno=Code.PARAMERR,errmsg='需要删除的热门文章不存在!')
def put(self,request,hotnews_id):
"""
更新热门新闻
#1. 获取参数
:param request:
:param hotnews_id:
:return:
"""
json_data = request.body
if not json_data:
return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
dict_data = json.loads(json_data.decode('utf8'))
try:
priority = int(dict_data.get('priority'))
#列表生成式 对于不需要的可以用 _ 代替
priority_list = [i for i,_ in models.HotNews.PRI_CHOICES]
if priority not in priority_list:
return to_json_data(errno=Code.PARAMERR,errmsg='热门文章优先级设置错误!')
except Exception as e:
logger.info('热门文章优先级异常:\n{}'.format(e))
return to_json_data(errno=Code.PARAMERR,errmsg='热门文章优先级设置错误!')
hotnews = models.HotNews.objects.only('id').filter(id=hotnews_id).first()
if not hotnews:
return to_json_data(errno=Code.PARAMERR,errmsg='需要更新的热门新闻不存在!')
if hotnews.priority == priority:
return to_json_data(errno=Code.PARAMERR,errmsg='热门新闻优先级未改变!')
hotnews.priority = priority
hotnews.save(update_fields=['priority','update_time'])
return to_json_data(errmsg='热门新闻优先级更新成功!')
class HotNewsAddView(PermissionRequiredMixin,View):
"""
添加热门新闻,先选标签,才能选标签下的文章,选择优先级不受他们影响
/addmin/hotnews/add/
"""
permission_required = ('news.view_hotnews','news.add_hotnews')
raise_exception = True
def handle_no_permission(self):
if self.request.method.lower() != 'get':
return to_json_data(errno=Code.ROLEERR,errmsg='没有操作权限')
else:
return super().handle_no_permission()
def get(self,request):
#选择标签和优先级
#有排序
tags = models.Tag.objects.values('id','name').annotate(num_news = Count('news')).filter(is_delete=False).\
order_by('-num_news','update_time')
#将列表嵌套个元组转换成字典
priority_dict = dict(models.HotNews.PRI_CHOICES)
return render(request,'admin/news/news_hot_add.html',locals())
def post(self,request):
#添加热门新闻
#1.获取参数
json_data = request.body
if not json_data:
return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
dict_data = json.loads(json_data.decode('utf8'))
#news_id priority
try:
news_id = int(dict_data.get('news_id'))
except Exception as e:
logger.info('热门文章:\n{}'.format(e))
return to_json_data(errno=Code.PARAMERR,errmsg='参数错误')
if not models.News.objects.filter(id=news_id).exists():
return to_json_data(errno=Code.PARAMERR,errmsg='文章不存在!')
try:
priority = int(dict_data.get('priority'))
priority_list = [i for i,_ in models.HotNews.PRI_CHOICES]
if priority not in priority_list:
return to_json_data(errno=Code.PARAMERR,errmsg='热门文章优先级设置错误!')
except Exception as e:
logger.info('热门文章优先级异常:\n{}'.format(e))
return to_json_data(errno=Code.PARAMERR,errmsg='热门文章优先级设置错误')
hotnews_tuple = models.HotNews.objects.get_or_create(news_id=news_id)
hotnews,is_created = hotnews_tuple
hotnews.priority = priority
hotnews.save(update_fields=['priority','update_time'])
return to_json_data(errmsg='热门文章创建成功!')
class NewsByTagIdView(PermissionRequiredMixin,View):
"""
选完标签后进行这步,选标签下的文章
不用展示 tag_id
/admin/tags//news/
"""
permission_required = ('news.view_news')
raise_exception = True
def handle_no_permission(self):
return to_json_data(errno=Code.ROLEERR,errmsg='没有操作权限')
def get(self,request,tag_id):
news = models.News.objects.filter(is_delete=False,tag_id=tag_id).values('id','title')
#将字典嵌套进列表
news_list = [i for i in news]
return to_json_data(data={
'news':news_list
})
#请求方式:get
#携带的方式:?start_time=&end_time=&title=&author_name=&tag_id=
#返回的参数:5个 title author_username tag_name update_time id
class NewsManageView(PermissionRequiredMixin,View):
"""
/admin/news/
"""
permission_required = ('news.view_news')
raise_exception = True
def get(self,request):
#查询
tags = models.Tag.objects.only('id','name').filter(is_delete=False)
newses = models.News.objects.select_related('author','tag').only('title',\
'author__username','tag__name','update_time').filter(is_delete=False)
#通过时间进行过滤
try:
#用于查询 url 中的字符串参数
# 寻找名为 start_time 的 GET 参数,而且如果参数没有提交,返回一个空的字符串
# 这里的 get() 是每个python的的字典数据类型都有的方法
# 使用 get('start_time', '') 提供一个缺省的返回值''(一个空字符串)
# 如果只是使用 request.GET['q'] 访问变量,在Get数据时 q 不可得, 可能引发 KeyError
#查询起始时间
start_time = request.GET.get('start_time','')
#对时间格式化
start_time = datetime.strptime(start_time,'%Y/%m/%d') if start_time else ''
#查询截止时间
end_time = request.GET.get('end_time','')
#对时间格式化
end_time = datetime.strptime(end_time,'%Y/%m/%d') if end_time else ''
except Exception as e:
#获取不到起始,截止时间就报错,然后重置
logger.info('用户输入的时间有误:\n{}'.format(e))
start_time = end_time = ''
if start_time and not end_time:
#__gte 大于 __let 小于 __range 范围
newses = newses.filter(update_time__gte=start_time)
if end_time and not start_time:
newses = newses.filter(update_time__lte=end_time)
if start_time and end_time:
newses = newses.filter(update_time__range=(start_time,end_time))
#通过 title 进行过滤
title = request.GET.get('title','')
if title:
#__icontains 包含并且忽略大小写
newses = newses.filter(title__icontains=title)
#通过作者名进行过滤
author_name = request.GET.get('author_name','')
if author_name:
newses = newses.filter(author__username__icontains=author_name)
#通过标签 id 进行过滤
try:
tag_id = int(request.GET.get('tag_id',0))
except Exception as e:
logger.info('标签错误:\n{}'.format(e))
tag_id = 0
#id 存在但没有 news 的时候,返回值为空
if tag_id:
newses = newses.filter(tag_id=tag_id)
#id 存在但没有 news 的时候,返回前面查询到的 news
# newses = newses.filter(is_delete=False,tag_id=tag_id) or newses.filter(is_delete=False)
#获取第几页内容
try:
page = int(request.GET.get('page',1))
except Exception as e:
logger.info('当前页数错误:\n{}'.format(e))
page = 1
paginator = Paginator(newses,constants.PER_PAGE_NEWS_COUNT)
try:
news_info = paginator.page(page)
except EmptyPage:
#若用户访问的页数大于实际页数,则返回最后一页
logging.info('用户返回的页数大于总页数!')
news_info = paginator.page(paginator.num_pages)
paginator_data = paginator_script.get_paginator_data(paginator,news_info)
start_time = start_time.strftime('%Y/%m/%d') if start_time else ''
end_time = end_time.strftime('%Y/%m/%d') if end_time else ''
context = {
'news_info': news_info,
'tags': tags,
'start_time': start_time,
'end_time': end_time,
'title': title,
'author_name': author_name,
'tag_id': tag_id,
'other_param': urlencode({ #将以下参数拼接成 url 返回给前端
'start_time': start_time,
'end_time': end_time,
'title': title,
'author_name': author_name,
'tag_id': tag_id,
})
}
#将 paginator_script 里的返回值增加到上下文中,返回给前端
#update 字典方法,将 paginator_data 加入到 context 中
context.update(paginator_data)
return render(request, 'admin/news/news_manage.html', context=context)
class NewsPubView(PermissionRequiredMixin,View):
"""
/admin/news/pub/
"""
permission_required = ('news.view_news','news.add_news')
raise_exception = True
def handle_no_permission(self):
if self.request.method.lower() != 'get':
return to_json_data(errno=Code.ROLEERR,errmsg='没有操作权限')
else:
return super().handle_no_permission()
def get(self,request):
tags = models.Tag.objects.only('id','name').filter(is_delete=False)
return render(request,'admin/news/news_pub.html',locals())
def post(self,request):
json_data = request.body
if not json_data:
return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
dict_data = json.loads(json_data.decode('utf8'))
form = forms.NewsPubForm(data=dict_data)
if form.is_valid():
#直接将验证通过的进行保存,不用一一获取再保存
#因为还有 author 没有传,并且需要传,所以先缓存,将 author 单独列出,再一起保存
news_instance = form.save(commit=False)
news_instance.author = request.user
news_instance = form.save()
return to_json_data(errmsg='文章更新成功')
else:
err_msg_list = []
for item in form.errors.get_json_data().values():
err_msg_list.append(item[0].get('message'))
err_msg_str = '/'.join(err_msg_list)
return to_json_data(errno=Code.PARAMERR,errmsg=err_msg_str)
class NewsUploadImage(View):
"""
"""
def post(self,request):
image_file = request.FILES.get('image_file')
if not image_file:
return to_json_data(errno=Code.PARAMERR,errmsg='从前端获取图片失败!')
if image_file.content_type not in ('image/jpeg', 'image/png', 'image/gif'):
return to_json_data(errno=Code.DATAERR,errmsg='不能上传非图片文件!')
try:
image_ext_name = image_file.name.split('.')[-1]
except Exception as e:
logger.info('图片拓展名异常:\n{}'.format(e))
image_ext_name = 'jpg'
try:
#upload_by_buffer 上传两个参数: 文件对象内容,文件对象拓展名
upload_res = FDFS_Client.upload_by_buffer(image_file.read(),file_ext_name=image_ext_name)
except Exception as e:
logger.error('图片上传出现异常:\n{}'.format(e))
return to_json_data(errno=Code.UNKOWNERR,errmsg='图片上传异常!')
else:
if upload_res.get('Status') != 'Upload successed.':
logger.info('图片上传到服务器失败!')
return to_json_data(Code.UNKOWNERR, errmsg='图片上传到服务器失败!')
else:
image_name = upload_res.get('Remote file_id')
image_url = settings.FASTDFS_SERVER_DOMAIN + image_name
return to_json_data(data={'image_url': image_url},errmsg='图片上传成功!')
class UploadToken(View):
"""
"""
def get(self,request):
access_key = qiniu_secret_info.QI_NIU_ACCESS_KEY
secret_key = qiniu_secret_info.QI_NIU_SECRET_KEY
bucket_name = qiniu_secret_info.QI_NIU_BUCKET_NAME
#构建鉴权对象
q = qiniu.Auth(access_key,secret_key)
token = q.upload_token(bucket_name)
return JsonResponse({'uptoken': token})
from django.utils.decorators import method_decorator #可以将函数装饰器变成类装饰器
from django.views.decorators.csrf import csrf_exempt #函数装饰器
@method_decorator(csrf_exempt,name='post')
# @method_decorator(csrf_exempt,name='dispatch') #将类里面所有方法都进行装饰
class MarkDownUploadImage(View):
"""
"""
def post(self,request):
image_file = request.FILES.get('editor-image-file')
if not image_file:
logger.info('从前端获取图片失败!')
return JsonResponse({'success': 0,'message': '从前端获取图片失败!'})
if image_file.content_type not in ('image/jpeg','image/png','image/gif'):
return JsonResponse({'success': 0,'message':'不能上传非图片文件!'})
try:
image_ext_name = image_file.name.split('.')[-1]
except Exception as e:
logger.info('图片拓展名异常:{}'.format(e))
image_ext_name = 'jpg'
try:
upload_res = FDFS_Client.upload_by_buffer(image_file.read(),file_ext_name=image_ext_name)
except Exception as e:
logger.error('图片上传出现异常:{}'.format(e))
return JsonResponse({'success':0,'message':'图片上传异常!'})
else:
if upload_res.get('Status') != 'Upload successed.':
logger.info('图片上传到FastDFS服务器失败!')
return JsonResponse({'success':0,'message':'图片上传到服务器失败!'})
else:
image_name = upload_res.get('Remote file_id')
image_url = settings.FASTDFS_SERVER_DOMAIN + image_name
return JsonResponse({'success':1,'message':'图片上传成功!','url':image_url})
class NewsEditView(PermissionRequiredMixin,View):
"""
/admin/news//
"""
permission_required = ('news.delete_news','news.view_news','news.change_news')
raise_exception = True
def handle_no_permission(self):
if self.request.method.lower() != 'get':
return to_json_data(errno=Code.ROLEERR,errmsg='没有操作权限')
else:
return super().handle_no_permission()
def delelte(self,request,news_id):
news = models.News.objects.only('id').filter(id=news_id).first()
if not news:
return to_json_data(errno=Code.PARAMERR, errmsg='需要删除的文章不存在!')
else:
news.is_delete = True
news.save(update_fields=['is_delete','update_time'])
return to_json_data(errmsg='文章删除成功!')
def get(self,request,news_id):
#获取文章的对象
news = models.News.objects.filter(id=news_id).first()
if not news:
return to_json_data(errno=Code.PARAMERR, errmsg='需要删除的文章不存在!')
else:
tags = models.Tag.objects.only('id','name').filter(is_delete=False)
return render(request,'admin/news/news_pub.html',locals())
def put(self,request,news_id):
news = models.News.objects.filter(id=news_id).first()
if not news:
return to_json_data(errno=Code.PARAMERR,errmsg='需要更新的文章不存在!')
json_data = request.body
if not json_data:
return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
#将 json 转化为 dict
dict_data = json.loads(json_data.decode('utf8'))
#用 form 验证
form = forms.NewsPubForm(data=dict_data)
#验证通过
if form.is_valid():
news.title = form.cleaned_data.get('title')
news.digest = form.cleaned_data.get('digest')
news.content = form.cleaned_data.get('content')
news.image_url = form.cleaned_data.get('image_url')
news.tag = form.cleaned_data.get('tag')
news.save()
return to_json_data(errmsg='文章更新成功')
else:
#定义一个错误信息列表
err_msg_list = []
for item in form.errors.get_json_data().values():
err_msg_list.append(item[0].get('message'))
err_msg_str = '/'.join(err_msg_list) #拼接错误信息为一个字符串
return to_json_data(errno=Code.PARAMERR,errmsg=err_msg_str)
class DocsManageView(PermissionRequiredMixin,View):
"""
"""
permission_required = ('doc.view_doc')
raise_exception = True
def get(self,request):
docs = Doc.objects.only('title','update_time').filter(is_delete=False)
return render(request,'admin/doc/docs_manage.html',locals())
class DocsEditView(PermissionRequiredMixin,View):
"""
/admin/docs//
"""
permission_required = ('doc.view_doc','doc.delete_doc','doc.change_doc')
raise_exception = True
def handle_no_permission(self):
if self.request.method.lower() != 'get':
return to_json_data(errno=Code.ROLEERR,errmsg='没有操作权限')
else:
return super().handle_no_permission()
def get(self,request,doc_id):
doc = Doc.objects.filter(is_delete=False,id=doc_id).first()
if not doc:
return to_json_data(errno=Code.NODATA,errmsg='需要删除的文档不存在')
else:
return render(request,'admin/doc/docs_pub.html',locals())
def delete(self,request,doc_id):
doc = Doc.objects.filter(is_delete=False,id=doc_id).first()
if not doc:
return to_json_data(errno=Code.NODATA,errmsg='需要删除的文档不存在!')
else:
doc.is_delete = True
doc.save(update_fields=['is_delete','update_time'])
return to_json_data(errmsg='文档删除成功!')
def put(self,request,doc_id):
doc = Doc.objects.filter(is_delete=False,id=doc_id).first()
if not doc:
return to_json_data(errno=Code.NODATA,errmsg='需要删除的文档不存在')
json_data = request.body
if not json_data:
return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
#将 json 转化为 dict
dict_data = json.loads(json_data.decode('utf8'))
form = forms.DocsPubForm(data=dict_data)
if form.is_valid():
#form.cleaned_data 是 dict 对象
for attr, value in form.cleaned_data.items():
#对 doc 对象的 attr 属性赋值 value
setattr(doc,attr,value)
doc.save()
return to_json_data(errmsg='文档更新成功')
else:
#定义一个错误信息列表
err_msg_list = []
for item in form.errors.get_json_data().values():
err_msg_list.append(item[0].get('message'))
err_msg_str = ''.join(err_msg_list) #拼接错误信息为一个字符串
return to_json_data(errno=Code.PARAMERR,errmsg=err_msg_str)
class DocsUploadFile(View):
"""
"""
def post(self,request):
text_file = request.FILES.get('text_file')
if not text_file:
logger.info('从前端获取文件失败')
return to_json_data(errno=Code.NODATA,errmsg='从前端获取文件失败')
if text_file.content_type not in ('application/octet-stream','application/pdf','application/zip',\
'text/plain','application/x-rar','application/msword'):
print(text_file.content_type)
return to_json_data(errno=Code.DATAERR,errmsg='不能上传的文件类型')
try:
text_ext_name = text_file.name.split('.')[-1]
except Exception as e:
logger.info('文件拓展名异常:{}'.format(e))
text_ext_name = 'pdf'
try:
upload_res = FDFS_Client.upload_by_buffer(text_file.read(),file_ext_name=text_ext_name)
except Exception as e:
logger.error('文件上传出现异常:{}'.format(e))
return to_json_data(errno=Code.UNKOWNERR,errmsg='文件上传异常')
else:
text_name = upload_res.get('Remote file_id')
text_url = settings.FASTDFS_SERVER_DOMAIN + text_name
return to_json_data(data={'text_file':text_url},errmsg='文件上传成功')
class DocsPubView(PermissionRequiredMixin,View):
"""
/admin/docs/pub/
"""
permission_required = ('doc.view_doc','doc.add_doc')
raise_exception = True
def handle_no_permission(self):
if self.request.method.lower() != 'get':
return to_json_data(errno=Code.ROLEERR,errmsg='没有操作权限')
else:
return super().handle_no_permission()
def get(self,request):
return render(request,'admin/doc/docs_pub.html')
def post(self,request):
json_data = request.body
if not json_data:
return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
#将 json 转换为 dict
dict_data = json.loads(json_data.decode('utf8'))
form = forms.DocsPubForm(data=dict_data)
if form.is_valid():
doc_instance = form.save(commit=False)
doc_instance.author = request.user
doc_instance.save()
return to_json_data(errmsg='文档更新成功')
else:
#定义一个错误信息列表
err_msg_list = []
for item in form.errors.get_json_data().values():
err_msg_list.append(item[0].get('message'))
err_msg_str = ''.join(err_msg_list) #拼接错误信息为一个字符串
return to_json_data(errno=Code.PARAMERR,errmsg=err_msg_str)
class CoursesManageView(PermissionRequiredMixin,View):
"""
"""
permission_required = ('course.view_course')
raise_exception = True
def get(self,request):
courses = Course.objects.select_related('category','teacher').only('title','category__name',\
'teacher__name').filter(is_delete=False)
return render(request,'admin/course/courses_manage.html',locals())
class CoursesEditView(PermissionRequiredMixin,View):
"""
/admin/courses//
"""
permission_required = ('course.view_course')
raise_exception = True
def handle_no_permission(self):
if self.request.method.lower() != 'get':
return to_json_data(errno=Code.ROLEERR,errmsg='没有操作权限')
else:
return super().handle_no_permission()
def get(self,request,course_id):
course = Course.objects.filter(is_delete=False,id=course_id).first()
if course:
teachers = Teacher.objects.only('name').filter(is_delete=False)
categories = CourseCategory.objects.only('name').filter(is_delete=False)
return render(request,'admin/course/courses_pub.html',locals())
else:
return to_json_data(errno=Code.NODATA,errmsg='需要更新的课程不存在')
def delete(self,request,course_id):
course = Course.objects.filter(is_delete=False,id=course_id).first()
if course:
course.is_delete = True
course.save(update_fields=['is_delete','update_time'])
return to_json_data(errmsg='课程删除成功')
else:
return to_json_data(errno=Code.PARAMERR,errmsg='需要删除的课程不存在')
def put(self,request,course_id):
course = Course.objects.filter(is_delete=False,id=course_id).first()
if not course:
return to_json_data(errno=Code.NODATA,errmsg='需要更新的课程不存在')
json_data = request.body
if not json_data:
return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
#将 json 格式转换为 dict
dict_data = json.loads(json_data.decode('utf8'))
form = forms.CoursesPubForm(data=dict_data)
if form.is_valid():
for attr,value in form.cleaned_data.items():
setattr(course,attr,value)
course.save()
return to_json_data(errmsg='课程更新成功')
else:
#定义一个错误信息列表
err_msg_list = []
for item in form.errors.get_json_data().values():
err_msg_list.append(item[0].get('message'))
err_msg_str = ''.join(err_msg_list) #拼接错误信息为一个字符串
return to_json_data(errno=Code.PARAMERR,errmsg=err_msg_str)
class CoursesPubView(PermissionRequiredMixin,View):
"""
"""
permission_required = ('course.view_course','course.add_course')
raise_exception = True
def handle_no_permission(self):
if self.request.method.lower() != 'get':
return to_json_data(errno=Code.ROLEERR,errmsg='没有操作权限')
else:
return super().handle_no_permission()
def get(self,request):
teachers = Teacher.objects.only('name').filter(is_delete=False)
categories = CourseCategory.objects.only('name').filter(is_delete=False)
return render(request,'admin/course/courses_pub.html',locals())
def post(self,request):
json_data = request.body
if not json_data:
return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
dict_data = json.loads(json_data.decode('utf8'))
form = forms.CoursesPubForm(data=dict_data)
if form.is_valid():
#此处模型里需要的字段都传了,所以不需要先缓存,直接存即可
courses_instance = form.save()
return to_json_data(errmsg='课程发布成功')
else:
#定义一个错误信息列表
err_msg_list = []
for item in form.errors.get_json_data().values():
err_msg_list.append(item[0].get('message'))
err_msg_str = ''.join(err_msg_list) #拼接错误信息为一个字符串
return to_json_data(errno=Code.PARAMERR,errmsg=err_msg_str)
class GroupsManageView(PermissionRequiredMixin,View):
"""
"""
permission_required = ('group.view_group')
raise_exception = True
def get(self,request):
groups = Group.objects.values('id','name').annotate(num_users=Count('user')).order_by('-num_users','id')
return render(request,'admin/user/groups_manage.html',locals())
class GroupsEditView(PermissionRequiredMixin,View):
"""
/admin/group//
"""
def get(self,request,group_id):
group = Group.objects.filter(id=group_id).first()
if group:
permissions = Permission.objects.only('id').all()
return render(request,'admin/user/groups_add.html',locals())
else:
raise Http404('需要更新的组不存在')
def delete(self,request,group_id):
group = Group.objects.filter(id=group_id).first()
if group:
# group.permissions.clear() #清空权限,多对多外键关联,不能直接删除,所以先清空
# group.user_set.clear()
group.delete() #group 没有 is_delete,这里是物理删除,多对多关系的删除 django 会自动处理,级联删除
return to_json_data(errmsg='用户组删除成功')
else:
return to_json_data(errno=Code.PARAMERR,errmsg='需要删除的用户组不存在!')
def put(self,request,group_id):
group = Group.objects.filter(id=group_id).first()
if not group:
return to_json_data(errno=Code.PARAMERR,errmsg='需要更新的用户组不存在')
json_data = request.body
if not json_data:
return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
#将 json 转化为 dict
dict_data = json.loads(json_data.decode('utf8'))
group_name = dict_data.get('name','').strip()
if not group_name:
return to_json_data(errno=Code.PARAMERR,errmsg='组名为空')
if group_name != group.name and Group.objects.filter(name=group_name).exists():
return to_json_data(errno=Code.DATAEXIST,errmsg='组名已存在')
#取出权限
group_permissions = dict_data.get('group_permissions')
if not group_permissions:
return to_json_data(errno=Code.PARAMERR,errmsg='权限参数为空')
try:
# 得到的有可能有重复,用 set 集合去重
permissions_set = set(int(i) for i in group_permissions)
except Exception as e:
logger.info('传的权限参数异常:\n{}'.format(e))
return to_json_data(errno=Code.PARAMERR,errmsg='权限参数异常')
all_permissions_set = set(i.id for i in Permission.objects.only('id'))
if not permissions_set.issubset(all_permissions_set):
return to_json_data(errno=Code.PARAMERR,errmsg='有不存在的权限参数')
for perm_id in permissions_set:
p = Permission.objects.get(id=perm_id)
#添加权限 多对多数据操作用 add 添加
group.permissions.add(p)
group.name = group_name
group.save()
return to_json_data(errmsg='组更新成功')
class GroupsAddView(PermissionRequiredMixin,View):
"""
/admin/groups/add/
"""
permission_required = ('group.view_group','group.add_group')
raise_exception = True
def handle_no_permission(self):
if self.request.method.lower() != 'get':
return to_json_data(errno=Code.ROLEERR,errmsg='没有操作权限')
else:
return super().handle_no_permission()
def get(self,request):
permissions = Permission.objects.only('id').all()
return render(request,'admin/user/groups_add.html',locals())
def post(self,request):
json_data = request.body
if not json_data:
return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
dict_data = json.loads(json_data.decode('utf8'))
#取出组名,进行判断
group_name = dict_data.get('name','').strip()
if not group_name:
return to_json_data(errno=Code.PARAMERR,errmsg='组名为空')
one_group,is_created = Group.objects.get_or_create(name=group_name)
if not is_created:
return to_json_data(errno=Code.DATAEXIST,errmsg='组名已存在')
#取出权限
group_permissions = dict_data.get('group_permissions')
if not group_permissions:
return to_json_data(errno=Code.PARAMERR,errmsg='权限参数为空')
try:
permissions_set = set(int(i) for i in group_permissions)
except Exception as e:
logger.info('传的权限参数异常:\n{}'.format(e))
return to_json_data(errno=Code.PARAMERR,errmsg='权限参数异常')
all_permissions_set = set(i.id for i in Permission.objects.only('id'))
if not permissions_set.issubset(all_permissions_set):
return to_json_data(errno=Code.PARAMERR,errmsg='有不存在的权限参数')
#设置权限
for perm_id in permissions_set:
p = Permission.objects.get(id=perm_id)
one_group.permissions.add(p) #多对多数据操作
one_group.save()
return to_json_data(errmsg='组创建成功')
class UsersManageView(PermissionRequiredMixin,View):
"""
/admin/users/
"""
permission_required = ('users.view_users')
raise_exception = True
def get(self,request):
users = Users.objects.only('username','is_staff','is_superuser').filter(is_active=True)
return render(request,'admin/user/users_manage.html',locals())
class UsersEditView(PermissionRequiredMixin,View):
"""
/admin/users//
"""
permission_required = ('users.view_users','users.delete_users','users.change_users')
raise_exception = True
def handle_no_permission(self):
if self.request.method.lower() != 'get':
return to_json_data(errno=Code.ROLEERR,errmsg='没有操作权限')
else:
return super().handle_no_permission()
def get(self,request,user_id):
user_instance = Users.objects.filter(id=user_id).first()
if user_instance:
groups = Group.objects.only('name').all()
return render(request,'admin/user/users_edit.html',locals())
else:
return Http404('需要更新的用户不存在!')
def delete(self,request,user_id):
user_instance = Users.objects.filter(id=user_id).first()
if user_instance:
user_instance.groups.clear()
user_instance.user_permissions.clear()
user_instance.is_active = False #逻辑删除,需要手动清空多对多表,权限,组信息
user_instance.save()
return to_json_data(errmsg='用户删除成功')
else:
return to_json_data(errno=Code.PARAMERR,errmsg='需要删除的用户不存在')
def put(self,request,user_id):
user_instance = Users.objects.filter(id=user_id).first()
if not user_instance:
return to_json_data(errno=Code.NODATA,errmsg='需要更新的用户不存在')
json_data = request.body
if not json_data:
return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
#将 json 转化为 dict
dict_data = json.loads(json_data.decode('utf8'))
try:
groups = dict_data.get('groups')
is_staff = int(dict_data.get('is_staff'))
is_superuser = int(dict_data.get('is_superuser'))
is_active = int(dict_data.get('is_active'))
params = (is_staff,is_superuser,is_active)
#返回形如 [True,True,True],如果不全为 True
# all() 函数用于判断给定的可迭代参数 iterable 中的所有元素是否都为 TRUE,如果是返回 True,否则返回 False。
# 元素除了是 0、空、None、False 外都算 True。
# iterable:元组或列表。
if not all([p in (0,1) for p in params]):
return to_json_data(errno=Code.PARAMERR,errmsg='参数错误')
except Exception as e:
logger.info('从前端获取参数出现异常:\n{}'.format(e))
return to_json_data(errno=Code.PARAMERR,errmsg='参数错误')
try:
groups_set = set(int(i) for i in groups)
except Exception as e:
logger.info('传的用户组参数异常:\n{}'.format(e))
return to_json_data(errno=Code.PARAMERR,errmsg='用户组参数异常')
all_groups_set = set(i.id for i in Group.objects.only('id'))
if not groups_set.issubset(all_groups_set):
return to_json_data(errno=Code.PARAMERR,errmsg='有不存在的用户组参数')
gs = Group.objects.filter(id__in=groups_set)
user_instance.groups.clear()
user_instance.groups.set(gs) #多对多数据操作,也可以用 add
user_instance.is_staff = bool(is_staff)
user_instance.is_speruser = bool(is_superuser)
user_instance.is_active = bool(is_active)
user_instance.save()
return to_json_data(errmsg='用户信息更新成功')
apps/admin/users/models.py
在 Users 中添加如下方法
def get_groups_name(self): #用于实例的调用
group_name_list = [i.name for i in self.groups.all()]
return '|'.join(group_name_list)
#多对多数据操作: add,remove,clear,属性,类名小写_set
如图
apps/admin/urls.py
#!/home/xiaoge/env python3.6
# -*- coding: utf-8 -*-
"""
__title__ = ' urls'
__author__ = 'xiaoge'
__mtime__ = '2019/6/26 下午8:55'
# code is far away from bugs with the god animal protecting
I love animals. They taste delicious.
┏┓ ┏┓
┏┛┻━━━━━━┛┻┓
┃ ☃ ┃
┃ ┳┛ ┗┳ ┃
┃ ┻ ┃
┗━┓ ┏━┛
┃ ┗━━━┓
┃ 神兽保佑 ┣┓
┃永无BUG!┏┛
┗┓┓┏━┳┓┏┛
┃┫┫ ┃┫┫
┗┻┛ ┗┻┛
"""
from apps.admin import views
from django.urls import path
app_name = 'admin'
urlpatterns = [
path('',views.IndexView.as_view(),name='index'),
path('tags/',views.TagManagerView.as_view(),name='tags'),
path('tags//',views.TagEditView.as_view(),name='tag_edit'),
path('tags//news/',views.NewsByTagIdView.as_view(),name='hotnews_by_tagid'),
path('hotnews/',views.HotNewsManageView.as_view(),name='hotnews'),
path('hotnews//',views.HotNewsEditView.as_view(),name='hotnews_edit'),
path('hotnews/add/',views.HotNewsAddView.as_view(),name='hotnews_add'),
path('news/', views.NewsManageView.as_view(), name='news_manage'),
path('news//',views.NewsEditView.as_view(),name='news_edit'),
path('news/pub/',views.NewsPubView.as_view(),name='news_pub'),
path('news/images/',views.NewsUploadImage.as_view(),name='upload_image'),
path('docs/',views.DocsManageView.as_view(),name='docs_manage'),
path('docs//',views.DocsEditView.as_view(),name='docs_edit'),
path('docs/pub/',views.DocsPubView.as_view(),name='docs_pub'),
path('docs/files/',views.DocsUploadFile.as_view(),name='upload_text'),
path('courses/',views.CoursesManageView.as_view(),name='courses_manage'),
path('courses//',views.CoursesEditView.as_view(),name='courses_edit'),
path('courses/pub/',views.CoursesPubView.as_view(),name='courses_pub'),
path('groups/',views.GroupsManageView.as_view(),name='groups_manage'),
path('groups/add/',views.GroupsAddView.as_view(),name='groups_add'),
path('groups//',views.GroupsEditView.as_view(),name='groups_edit'),
path('users/',views.UsersManageView.as_view(),name='users_manage'),
path('users//',views.UsersEditView.as_view(),name='users_edit'),
path('token/',views.UploadToken.as_view(),name='upload_token'),
path('markdown/images/',views.MarkDownUploadImage.as_view(),name='markdown_image_upload'),
]
在系统运行后,在线程快照里总是看到线程池的名字为pool-xx,这样导致很不好定位,怎么给线程池一个有意义的名字呢。参照ThreadPoolExecutor类的ThreadFactory,自己实现ThreadFactory接口,重写newThread方法即可。参考代码如下:
public class Named
错误: IE 中"HTML Parsing Error:Unable to modify the parent container element before the child element is closed"
现象: 同事之间几个IE 测试情况下,有的报这个错,有的不报。经查询资料后,可归纳以下原因。
PropertyPlaceholderConfigurer是个bean工厂后置处理器的实现,也就是BeanFactoryPostProcessor接口的一个实现。关于BeanFactoryPostProcessor和BeanPostProcessor类似。我会在其他地方介绍。PropertyPlaceholderConfigurer可以将上下文(配置文件)中的属性值放在另一个单独的标准java P
创建一个类
public class ContextInitListener implements ServletContextListener
使得该类成为一个监听器。用于监听整个容器生命周期的,主要是初始化和销毁的。
类创建后要在web.xml配置文件中增加一个简单的监听器配置,即刚才我们定义的类。
<listener>
<des
http://developer.apple.com/iphone/library/qa/qa2009/qa1649.html
Excerpt:
You are getting this warning because you probably added your Info.plist file to your Copy Bundle
hi,
自己在做工程的时候,遇到批量插入数据的数据修复场景。我的思路是在插入前准备一个临时表,临时表的整理就看当时的选择条件了,临时表就是要插入的数据集,最后再批量插入到数据库中。
WITH tempT AS (
SELECT
item_id AS combo_id,
item_id,
now() AS create_date
FROM
a