18 文章发布功能+fdfs+七牛云

前端功能实现

templates/admin/news/news_pub.html

{% extends 'admin/base/base.html' %}
{% load static %}

{% block title %}
    {% if news %}
        文章更新页
    {% else %}
        文章发布页
    {% endif %}
{% endblock %}

{% block content_header %}
    {% if news %}
        文章更新
    {% else %}
        文章发布
    {% endif %}
{% endblock %}

{% block header_option_desc %}
  书是人类进步的阶梯
{% endblock %}

{% block link %}
    <link rel="stylesheet" href="{% static 'plugins/markdown_editor/css/editormd.css' %}">
{% endblock %}

{% block content %}
<div class="row">
  <div class="col-md-12 col-xs-12 col-sm-12">
    <div class="box box-primary">
      <div class="box-body">
        <div class="form-group">
          <label for="news-title">文章标题</label>
          {% if news %}
            <input type="text" class="form-control" id="news-title" name="news-title" placeholder="请输入文章标题"
                   value="{{ news.title }}">
          {% else %}
            <input type="text" class="form-control" id="news-title" name="news-title" placeholder="请输入文章标题" autofocus>
          {% endif %}
        </div>
        <div class="form-group">
          <label for="news-desc">文章摘要</label>
          {% if news %}
            <textarea name="news-desc" id="news-desc" placeholder="请输入新闻描述" class="form-control"
                      style="height: 8rem; resize: none;">{{ news.digest }}</textarea>
          {% else %}
            <textarea name="news-desc" id="news-desc" placeholder="请输入新闻描述" class="form-control"
                      style="height: 8rem; resize: none;"></textarea>
          {% endif %}
        </div>
        <div class="form-group">
          <label for="news-category">文章分类</label>
          <select name="news-category" id="news-category" class="form-control">
            <option value="0">-- 请选择文章分类 --</option>
            {% for one_tag in tags %}
              <!-- 传tag_id到后台 -->
              {% if news and one_tag == news.tag %}
                <option value="{{ one_tag.id }}" selected>{{ one_tag.name }}</option>
              {% else %}
                <option value="{{ one_tag.id }}">{{ one_tag.name }}</option>
              {% endif %}
            {% endfor %}
          </select>
        </div>
        <div class="form-group" id="container">
          <label for="news-thumbnail-url">文章缩略图</label>
          <div class="input-group">
            {% if news %}
            <input type="text" class="form-control" id="news-thumbnail-url" name="news-thumbnail-url"
                   placeholder="请上传图片或输入文章缩略图地址" value="{{ news.image_url }}">
              {% else %}
              <input type="text" class="form-control" id="news-thumbnail-url" name="news-thumbnail-url"
                   placeholder="请上传图片或输入文章缩略图地址">
            {% endif %}

            <div class="input-group-btn">
              <label class="btn btn-default btn-file">
                上传至服务器 <input type="file" id="upload-news-thumbnail">
              </label>
              <button class="btn btn-info" id="upload-btn">上传至七牛云</button>
            </div>
          </div>
        </div>
        <div class="form-group">
          <div class="progress" style="display: none">
            <div class="progress-bar progress-bar-striped progress-bar-animated" style="width: 0;">0%</div>
          </div>
        </div>
        <div class="form-group">
          <label for="news-content">文章内容</label>
          {% if news %}
            <div id="news-content">
                 <textarea name="content" id="content">{{ news.content|safe }}</textarea>
            </div>
          {% else %}
            <div id="news-content">
                <textarea name="content" style="display:none;" id="content"></textarea>
            </div>
          {% endif %}
        </div>
      </div>
      <div class="box-footer">
          {% if news %}
            <a href="javascript:void (0);" class="btn btn-primary pull-right" id="btn-pub-news" data-news-id="{{ news.id }}">更新文章 </a>
          {% else %}
           <a href="javascript:void (0);" class="btn btn-primary pull-right" id="btn-pub-news">发布文章 </a>
          {% endif %}
      </div>
    </div>
  </div>
</div>
{% endblock %}

{% block script %}
    <script src="{% static 'plugins/markdown_editor/editormd.js' %}"></script>
  <script>
    let testEditor;
    $(function () {
      $.get("{% static 'plugins/markdown_editor/examples/test.md' %}", function (md) {
        testEditor = editormd("news-content", {
          width: "98%",
          height: 730,
          path: "{% static 'plugins/markdown_editor/lib/' %}",
          markdown: md,
          codeFold: true,
          saveHTMLToTextarea: true,
          searchReplace: true,
          htmlDecode: "style,script,iframe|on*",
          emoji: true,
          taskList: true,
          tocm: true,         			// Using [TOCM]
          tex: true,                   // 开启科学公式TeX语言支持,默认关闭
          flowChart: true,             // 开启流程图支持,默认关闭
          sequenceDiagram: true,       // 开启时序/序列图支持,默认关闭,
          imageUpload: true,
          imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
          {#imageUploadURL: "{% url 'admin:markdown_image_upload' %}",#}
{#          onload: function () {#}
{#            console.log('onload', this);#}
{##}
{#          },#}
          /**设置主题颜色 把这些注释去掉主题就是黑色的了*/
{#          editorTheme: "pastel-on-dark",#}
{#          theme: "dark",#}
{#          previewTheme: "dark"#}
        });
      });
    });

  </script>
  {# 导入七牛云需要的js文件 这3个js文件有依赖关系,qiniu.min.js需要放在后面#}
  <script src="https://cdn.bootcss.com/plupload/2.1.9/moxie.min.js"></script>
  <script src="https://cdn.bootcss.com/plupload/2.1.9/plupload.dev.js"></script>
  <script src="https://cdn.bootcss.com/qiniu-js/1.0.17.1/qiniu.min.js"></script>

  <script src="{% static 'js/admin/base/fqiniu.js' %}"></script>
  <script src="{% static 'js/admin/news/news_pub.js' %}"></script>
{% endblock %}

static/js/admin/news/news_pub.js

$(function () {
  // let $e = window.wangEditor;
  // window.editor = new $e('#news-content');
  // window.editor.create();

  // 获取缩略图输入框元素
  let $thumbnailUrl = $("#news-thumbnail-url");

  // ================== 上传图片文件至服务器 ================
  let $upload_to_server = $("#upload-news-thumbnail");
  $upload_to_server.change(function () {
    let file = this.files[0];   // 获取文件
    let oFormData = new FormData();  // 创建一个 FormData
    oFormData.append("image_file", file); // 把文件添加进去
    // 发送请求
    $.ajax({
      url: "/admin/news/images/",
      method: "POST",
      data: oFormData,
      processData: false,   // 定义文件的传输
      contentType: false,
    })
      .done(function (res) {
        if (res.errno === "0") {
          // 更新标签成功
          message.showSuccess("图片上传成功");
          let sImageUrl = res["data"]["image_url"];
          // console.log(thumbnailUrl);
          $thumbnailUrl.val('');
          $thumbnailUrl.val(sImageUrl);
        } else {
          message.showError(res.errmsg)
        }
      })
      .fail(function () {
        message.showError('服务器超时,请重试!');
      });

  });


  // ================== 上传至七牛(云存储平台) ================
  let $progressBar = $(".progress-bar");
  QINIU.upload({
    "domain": "",  // 自己的七牛空间域名
    "uptoken_url": "/admin/token/",	 // 后台返回 token的地址
    "browse_btn": "upload-btn",		// 按钮
    "success": function (up, file, info) {	 // 成功
      let domain = up.getOption('domain');
      let res = JSON.parse(info);
      let filePath = domain + res.key;
      console.log(filePath);
      $thumbnailUrl.val('');
      $thumbnailUrl.val(filePath);
    },
    "error": function (up, err, errTip) {
      // console.log('error');
      console.log(up);
      console.log(err);
      console.log(errTip);
      // console.log('error');
      message.showError(errTip);
    },
    "progress": function (up, file) {
      let percent = file.percent;
      $progressBar.parent().css("display", 'block');
      $progressBar.css("width", percent + '%');
      $progressBar.text(parseInt(percent) + '%');
    },
    "complete": function () {
      $progressBar.parent().css("display", 'none');
      $progressBar.css("width", '0%');
      $progressBar.text('0%');
    }
  });


  // ================== 发布文章 ================
  let $newsBtn = $("#btn-pub-news");
  $newsBtn.click(function () {
    // 判断文章标题是否为空
    let sTitle = $("#news-title").val();  // 获取文章标题
    if (!sTitle) {
        message.showError('请填写文章标题!');
        return
    }
    // 判断文章摘要是否为空
    let sDesc = $("#news-desc").val();  // 获取文章摘要
    if (!sDesc) {
        message.showError('请填写文章摘要!');
        return
    }

    let sTagId = $("#news-category").val();
    if (!sTagId || sTagId === '0') {
      message.showError('请选择文章标签');
      return
    }

    let sThumbnailUrl = $thumbnailUrl.val();
    if (!sThumbnailUrl) {
      message.showError('请上传文章缩略图');
      return
    }
    let sContentHtml = $(".markdown-body").html();
    console.log(sContentHtml);
    // let sContentHtml = $("#content").val();
    if (!sContentHtml || sContentHtml === '<p><br></p>') {
        message.showError('请填写文章内容!');
        return
    }

    // 获取news_id 存在表示更新 不存在表示发表
    let newsId = $(this).data("news-id");
    let url = newsId ? '/admin/news/' + newsId + '/' : '/admin/news/pub/';
    let data = {
      "title": sTitle,
      "digest": sDesc,
      "tag": sTagId,
      "image_url": sThumbnailUrl,
      "content": sContentHtml,
    };

    $.ajax({
      // 请求地址
      url: url,
      // 请求方式
      type: newsId ? 'PUT' : 'POST',
      data: JSON.stringify(data),
      // 请求内容的数据类型(前端发给后端的格式)
      contentType: "application/json; charset=utf-8",
      // 响应数据的格式(后端返回给前端的格式)
      dataType: "json",
    })
      .done(function (res) {
        if (res.errno === "0") {
          if (newsId) {
              message.showSuccess("文章更新成功");
              setTimeout(function () {
                window.location.reload();
                }, 1000)

          } else {
              message.showSuccess("文章添加成功");
              setTimeout(function () {
                window.location.reload();
                }, 1000)
          }
        } else {
          fAlert.alertErrorToast(res.errmsg);
        }
      })
      .fail(function () {
        message.showError('服务器超时,请重试!');
      });
  });


  // get cookie using jQuery
  function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
      let cookies = document.cookie.split(';');
      for (let i = 0; i < cookies.length; i++) {
        let cookie = jQuery.trim(cookies[i]);
        // Does this cookie string begin with the name we want?
        if (cookie.substring(0, name.length + 1) === (name + '=')) {
          cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
          break;
        }
      }
    }
    return cookieValue;
  }

  function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
  }

  // Setting the token on the AJAX request
  $.ajaxSetup({
    beforeSend: function (xhr, settings) {
      if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
        xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
      }
    }
  });

});

static/plugins/markdown_editor

  • 在 static 下新建 plugins 文件夹,将 markdown_editor 放入其中

上传至 FastDFS 服务器

utils/fastdfs/fdfs.py

from fdfs_client.client import Fdfs_client

FDFS_Client = Fdfs_client('utils/fastdfs/client.conf')

settings.py

# fastdfs服务的站点
FASTDFS_SERVER_DOMAIN = "http://127.0.0.1:8888/"

apps/admin/views.py

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

from apps.news import models
from apps.admin import constants

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(View):
    """
    /admin/
    """
    def get(self,request):
        """

        :param request:
        :return:
        """
        return render(request,'admin/index/index.html')


class TagManagerView(View):
    """
    /admin/tag/
    """
    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(View):
    """
    /admin/tag/<tag_id>/
    """
    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(View):
    """
    /admin/hotnews/
    """
    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(View):
    """
    /admin/hotnews/<int:tag_id>
    """
    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(View):
    """
    添加热门新闻,先选标签,才能选标签下的文章,选择优先级不受他们影响
    /addmin/hotnews/add/
    """
    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(View):
    """
    选完标签后进行这步,选标签下的文章
    不用展示 tag_id
    /admin/tags/<int:tag_id>/news/
    """
    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(View):
    """
    /admin/news/
    """
    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(View):
    """
    /admin/news/pub/
    """
    def get(self,request):
        tags = models.Tag.objects.only('id','name').filter(is_delete=False)
        return render(request,'admin/news/news_pub.html',locals())


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='图片上传成功!')

apps/admin/urls.py

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/<int:tag_id>/',views.TagEditView.as_view(),name='tag_edit'),
    path('hotnews/',views.HotNewsManageView.as_view(),name='hotnews'),
    path('hotnews/<int:hotnews_id>/',views.HotNewsEditView.as_view(),name='hotnews_edit'),
    path('hotnews/add/',views.HotNewsAddView.as_view(),name='hotnews_add'),
    path('tags/<int:tag_id>/news/',views.NewsByTagIdView.as_view(),name='hotnews_by_tagid'),
    path('news/', views.NewsManageView.as_view(), name='news_manage'),
    path('news/pub/',views.NewsPubView.as_view(),name='news_pub'),
    path('news/images/',views.NewsUploadImage.as_view(),name='upload_image'),
    path('token/',views.UploadToken.as_view(),name='upload_token'),
    path('markdown/images/',views.MarkDownUploadImage.as_view(),name='markdown_image_upload'),

]

上传至七牛云

创建存储空间

  • 创建存储空间,填写空间名称,选择存储区域,访问控制选择为公开空间
    在这里插入图片描述

获取测试域名

在这里插入图片描述

utils/secrets/qiniu_secret_info.py

QI_NIU_ACCESS_KEY = '你自己七牛云上的AK'
QI_NIU_SECRET_KEY = '你自己七牛云上的SK'
QI_NIU_BUCKET_NAME = '你自己在七牛云上创建的存储空间名'

安装七牛云

pip install qiniu

apps/admin/views.py

  • 添加如下代码
import qiniu
from utils.secrets import qiniu_secret_info
from django.http import JsonResponse

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

富文本编辑器上传

apps/admn/views.py

  • 添加如下代码
import logging
import json

from django.shortcuts import render,reverse,redirect
from django.views import View
from django.http import JsonResponse

from mysite import settings
from utils.json_fun import to_json_data
from utils.res_code import Code,error_map
from utils.fastdfs.fdfs import FDFS_Client


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

apps/admin/urls.py

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/<int:tag_id>/',views.TagEditView.as_view(),name='tag_edit'),
    path('hotnews/',views.HotNewsManageView.as_view(),name='hotnews'),
    path('hotnews/<int:hotnews_id>/',views.HotNewsEditView.as_view(),name='hotnews_edit'),
    path('hotnews/add/',views.HotNewsAddView.as_view(),name='hotnews_add'),
    path('tags/<int:tag_id>/news/',views.NewsByTagIdView.as_view(),name='hotnews_by_tagid'),
    path('news/', views.NewsManageView.as_view(), name='news_manage'),
    path('news/pub/',views.NewsPubView.as_view(),name='news_pub'),
    path('news/images/',views.NewsUploadImage.as_view(),name='upload_image'),
    path('token/',views.UploadToken.as_view(),name='upload_token'),
    path('markdown/images/',views.MarkDownUploadImage.as_view(),name='markdown_image_upload'),

]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值