django项目--新闻管理功能以及富文本编辑器

新闻管理功能

一、业务功能分析

1.业务需求分析

新闻的增删改查。

2.功能分析

  • 新闻列表
  • 查询
  • 新增
  • 修改

二、新闻列表

1.业务流程分析

  • 接收参数
  • 校验参数
  • 查询数据
  • 分页

2.接口设计

1.接口说明:

类目 说明
请求方法 GET
url定义 /admin/newses/
参数格式 查询参数

2.参数说明:

参数名 类型 是否必须 描述
title 字符串 要查询的新闻标题
tag 整数 要查询的tag_id
is_delete 整数 新闻是否可用
page 整数 父菜单id

3.返回数据

html

3.后端代码

1.视图

# 在admin/views.py中创建如下视图


class NewsesView(View):
    """
    新闻列表视图
    url:admin/newses/
    """

    def get(self, request):
        queryset = News.objects.only('title', 'tag__name', 'author__username', 'is_delete').select_related('tag',
                                                                                                           'author').all()
        tags = Tag.objects.only('name').filter(is_delete=False)
        query_dict = {}

        tag_id = request.GET.get('tag')
        if tag_id:
            query_dict['tag_id'] = tag_id
            queryset = queryset.filter(tag_id=tag_id)

        title = request.GET.get('title')
        if title:
            query_dict['title'] = title
            queryset = queryset.filter(title__contains=title)
        is_delete = request.GET.get('is_delete', None)

        flag = False

        if is_delete == '0':
            is_delete = True 
            flag = True

        if is_delete == '1':
            is_delete = False
            flag = True

        if flag:
            queryset = queryset.filter(is_delete=is_delete)

        query_dict['is_delete'] = is_delete

        paginator = Paginator(queryset, 10)

        try:
            page = int(request.GET.get('page', 1))
        except Exception as e:
            page = 1

        newses = paginator.get_page(page)

        context = {
            'newses': newses,
            'tags': tags
        }

        context.update(query_dict)

        return render(request, 'myadmin/news/news_list.html', context=context)

2.路由

# 在admin/views.py中添加如下路由
path('newses/', views.NewsesView.as_view(), name='news_list')

4.前端代码

1.html

<!-- 创建 templates/myadmin/news/news_list.html 模板-->
{% extends 'myadmin/base/content_base.html' %}
{% load static %}
{% load news_template_filters %}
{% block page_header %}
    系统设置
{% endblock %}
{% block page_option %}
    新闻管理
{% endblock %}
{% block content %}
    <div class="box">
        <div class="box-header with-border">
            <h3 class="box-title">新闻列表</h3>
            <div class="box-tools">
            </div>
        </div>
        <!-- /.box-header -->

        <div class="box-body">
            <div style="margin-bottom: 10px">
                <form class="form-inline user-query">
                    <div class="form-group">
                        <label for="">标题</label>
                        <input type="text" class="form-control" name="title" value="{{ title }}">
                    </div>
                    <div class="form-group">
                        <label for="">分类</label>
                        <select name="tag" id="" class="form-control">
                            <option value="">所有</option>
                            {% for tag in tags %}

                                <option {% if tag_id == tag.id %}selected{% endif %}
                                        value="{{ tag.id }}">{{ tag.name }}</option>

                            {% endfor %}
                        </select>
                    </div>
                    <div class="form-group">
                        <label for="">是否可用</label>
                        <select name="is_delete" id="" class="form-control">
                            <option value="2">所有</option>

                            <option {% if is_delete is False %}selected{% endif %} value="1">是</option>
                            <option {% if is_delete is True %}selected{% endif %} value="0">否</option>
                        </select>
                    </div>
                    <button type="button" class="btn btn-info query">查询</button>
                    <button type="button" class="btn btn-default reset">重置</button>
                </form>
            </div>

            <table class="table table-bordered">
                <tbody>
                <tr>
                    <th>#</th>
                    <th>标题</th>
                    <th>类型</th>
                    <th>作者</th>
                    <th>是否可用</th>
                </tr>
                {% for news in newses %}
                    <tr>
                        <td style="width: 40px"><a href="#">{{ forloop.counter }}</a></td>
                        <td>{{ news.title }}</td>
                        <td>{{ news.tag.name }}</td>
                        <td>{{ news.author.username }}</td>
                        <td>{% if news.is_delete %}否{% else %}是{% endif %}</td>
                    </tr>
                {% endfor %}
                </tbody>
            </table>


        </div>
        <!-- 分页 -->
        <div class="box-footer clearfix">
            <div class="row">
                <div class="col-sm-6">
                    <div class="dataTables_info" id="example2_info" role="status" aria-live="polite">总共:{{ newses.paginator.count }}条 第{{ newses.start_index }}到{{ newses.end_index }}条
                    </div>
                </div>
                <div class="col-sm-6">
                    <ul class="pagination pagination-sm no-margin pull-right">
                        <li {% if not newses.has_previous %}class="disabled"{% endif %} data-page="{{ newses.number|add:-1 }}"><a href="#">«</a></li>
                    {% for n in newses|page_bar %}
                        <li {% if n == newses.number %}class="active" {% endif %} data-page="{{ n }}"><a href="#">{{ n }}</a></li>
                    {% endfor %}
                        <li {% if not newses.has_next %}class="disabled"{% endif %} data-page="{{ newses.number|add:1 }}"><a href="#">»</a></li>
                    </ul>
                </div>
            </div>

        </div>

    </div>
{% endblock %}
{% block script %}
    <script src="{% static 'js/myadmin/news/news_list.js' %}"></script>
{% endblock %}

2.js

// 创建 js/myadmin/news/news_list.js 
$(() => {
    let $queryForm = $('form.user-query');       // 查询表单
    let $queryBtn = $('form.user-query button.query');    // 查询按钮
    let $resetBtn = $('form.user-query button.reset');    // 重置按钮
    // 查询
    $queryBtn.click(() => {
        $
            .ajax({
                url: $('.sidebar-menu li.active a').data('url'),
                data: $queryForm.serialize(),
                type: 'GET'
            })
            .done((res) => {
                $('#content').html(res)
            })
            .fail(() => {
                message.showError('服务器超时,请重试!')
            })
    });

        // 重置
    $resetBtn.click(() => {
        $queryForm[0].reset();
        $
            .ajax({
                url: $('.sidebar-menu li.active a').data('url'),
                type: 'GET'
            })
            .done((res) => {
                $('#content').html(res)
            })
            .fail(() => {
                message.showError('服务器超时,请重试!')
            })
    });
    // 分页 给非
    let $pageLi = $('ul.pagination li').not('.active').not('.disabled');
    $pageLi.click(function () {
        let $this = $(this);

        $
            .ajax({
                url: $('.sidebar-menu li.active a').data('url'),
                data: $queryForm.serialize() + '&page=' + $this.data('page'),
                type: 'GET'
            })
            .done((res) => {
                $('#content').html(res)
            })
            .fail(() => {
                message.showError('服务器超时,请重试!')
            })

    });
});

三、新闻详情页

1.业务流程分析

根据提供的新闻id,返回新闻的详细,渲染新闻页面。

这里新闻编写部分,我们引入富文本编辑器。所以,引入django-ckeditor插件

2.django-ckeditor

1.介绍

django-ckeditor是一个集成了富文本编辑器ckeditor的插件,利用它可以在django项目中快速集成富文本编辑器的相关功能。

2.安装配置

1.安装

pip install django-ckeditor

2.配置

  1. INSTALLED_APPS中添加ckeditor

  2. 在settings.py中添加STATIC_ROOT参数,注意,配置项目的静态文件根目录绝对路径例如:

    STATIC_ROOT = os.path.join(BASE_DIR, 'static')
    

    然后运行命令

    python manage.py collectstatic 
    

    将ckeditor所需要的静态文件都copy到项目静态文件夹中

  3. 设置ckeditor所需静态文件路径

    # 在settings.py中添加如下配置,注意填写你自己的静态文件路径
    CKEDITOR_BASEPATH = "/your_static/ckeditor/ckeditor/"
    
  4. 配置ckeditor外观按钮

    # 在settings.py中添加如下配置
    CKEDITOR_CONFIGS = {
        'default': {
            'skin': 'moono',
            # 'skin': 'office2013',
            'toolbar_Basic': [
                ['Source', '-', 'Bold', 'Italic']
            ],
            'toolbar_YourCustomToolbarConfig': [
                {'name': 'document', 'items': ['Source', '-', 'Save', 'NewPage', 'Preview', 'Print', '-', 'Templates']},
                {'name': 'clipboard', 'items': ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo']},
                {'name': 'editing', 'items': ['Find', 'Replace', '-', 'SelectAll']},
                {'name': 'forms',
                 'items': ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton',
                           'HiddenField']},
                '/',
                {'name': 'basicstyles',
                 'items': ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'RemoveFormat']},
                {'name': 'paragraph',
                 'items': ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote', 'CreateDiv', '-',
                           'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'BidiLtr', 'BidiRtl',
                           'Language']},
                {'name': 'links', 'items': ['Link', 'Unlink', 'Anchor']},
                {'name': 'insert',
                 'items': ['Image', 'Flash', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar', 'PageBreak', 'Iframe']},
                '/',
                {'name': 'styles', 'items': ['Styles', 'Format', 'Font', 'FontSize']},
                {'name': 'colors', 'items': ['TextColor', 'BGColor']},
                {'name': 'tools', 'items': ['Maximize', 'ShowBlocks']},
                {'name': 'about', 'items': ['About']},
                '/',  # put this to force next toolbar on new line
                {'name': 'yourcustomtools', 'items': [
                    # put the name of your editor.ui.addButton here
                    'Preview',
                    'Maximize',
    
                ]},
            ],
            'toolbar': 'YourCustomToolbarConfig',  # put selected toolbar config here
            'tabSpaces': 4,
            'extraPlugins': ','.join([
                'uploadimage', # the upload image feature
                # your extra plugins here
                'div',
                'autolink',
                'autoembed',
                'embedsemantic',
                'autogrow',
                # 'devtools',
                'widget',
                'lineutils',
                'clipboard',
                'dialog',
                'dialogui',
                'elementspath'
            ]),
        }
    }
    

3.上传配置

<span style="color:red">注意:如果需要上传功能,则还需要如下步骤</span>

  1. INSTALLED_APPS中添加ckeditor_uploader

  2. 在settings.py中添加``CKEDITOR_UPLOAD_PATH参数,用来指定上传的文件保存目录。这个路径是相对于项目的媒体文件保存目录(MEDIA_ROOT)。例如:

    CKEDITOR_UPLOAD_PATH = 'uploads/'
    

    如果使用默认的文件存储系统,那么图片将会被上传到MEDIA_ROOT所指定的文件夹下的uploads文件夹中。

  3. 添加CKEditor URL 到项目根urls.py中

    path('ckeditor/', include('ckeditor_uploader.urls')),
    
  4. 自定义文件名生成函数

    1. 在utils文件夹下创建名为ck_uploader的目录,在其下创建funcs.py的脚本内容如下:

      import os
      from hashlib import md5
      
      
      def get_filename(filename: str) -> str:
          suffix = filename.split('.')[-1]
          m = md5(os.urandom(16)+filename.encode('utf-8'))
          return m.hexdigest() + '.' + suffix
      
      
      if __name__ == '__main__':
          print(get_filename('aa.txt'))
      
    2. 在settings.py中设置

      # 设置文件名生成函数
      CKEDITOR_FILENAME_GENERATOR = 'utils.ck_uploader.funcs.get_filename'
      

4.接口设计

1. 接口说明

类目 说明
请求方法 GET
url定义 /admin/news/<int:news_id>
参数格式 路径参数

2.参数说明:

参数名 类型 是否必须 描述
news_id 整数 要查询的新闻id

3.返回数据

html

5.后端代码

1.模型

# 修改news应用下的News模型中的content字段
from ckeditor_uploader.fields import RichTextUploadingField

from utils.models import BaseModel

class News(BaseModel):
# ...
    content = RichTextUploadingField('内容', help_text='内容')
# ...

修改模型之后,记得迁移数据库

2. 表单

# 在admin/forms.py中创建如下表单

from ckeditor_uploader.widgets import CKEditorUploadingWidget

class NewsModelForm(forms.ModelForm):
    tag = forms.ModelChoiceField(queryset=None, required=False, help_text='分类', label='分类')
    content = forms.CharField(widget=CKEditorUploadingWidget(), label='内容')

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['tag'].queryset = Tag.objects.filter(is_delete=False)

    class Meta:
        model = News
        fields = ['title', 'is_delete', 'digest', 'image_url', 'tag', 'content']

3.视图

# 在admin/views.py中添加如下视图
class NewsUpdateView(View):
    """
    新闻修改视图
    url:/admin/news/<int:news_id>/
    """
    def get(self, request, news_id):
        # 1. 拿到对应的新闻对象
        news = News.objects.filter(id=news_id).first()
        if news:
            # 2. 生成表单对象
            form = NewsModelForm(instance=news)
        else:
            return json_response(errno=Code.NODATA, errmsg='没有此新闻!')

        # 3. 渲染并返回
        return render(request, 'myadmin/news/news_detail.html', context={'form': form})

6.前端代码

1.js

// 在js/myadmin/news/news_list.js中添加如下代码
// 实例详情
    $('tr').each(function () {
        $(this).children('td:first').click(function () {
            $('#content').load(
                $(this).data('url'),
                (response, status, xhr) => {
                    if (status !== 'success') {
                        message.showError('服务器超时,请重试!')
                    }
                }
            );
        })
    });

2.html

<!-- 修改 templates/myadmin/news/news_list.html 中for循环的第一个td -->
<td style="width: 40px" data-url="{% url 'admin:update_news' news.id %}"><a href="#">{{ forloop.counter }}</a></td>
<!-- 创建模板 templates/myadmin/news/news_detail.html -->
{% extends 'myadmin/base/content_base.html' %}
{% load static %}
{% load admin_customer_tags %}
{% block page_header %}
    系统设置
{% endblock %}
{% block page_option %}
    新闻管理
{% endblock %}
{% block content %}
    <div class="box box-primary">
        <div class="box-header with-border">
            <h3 class="box-title">新闻详情</h3>
        </div>
        <!-- /.box-header -->
        <!-- form start -->
        <div class="box-body">

            <form class="form-horizontal">
                {% csrf_token %}

                {% for field in form %}
                    {% if field.name == 'is_delete' %}
                        <div class="form-group">

                            <div class="col-sm-offset-1 col-sm-11">

                                <div class="checkbox">
                                    <label for="{{ field.id_for_label }}">{{ field }}{{ field.label }}</label>
                                </div>
                            </div>

                        </div>
                    {% elif field.name == 'image_url' %}
                        <div class="form-group {% if field.errors %}has-error{% endif %}">

                            <label for="{{ field.id_for_label }}"
                                   class="col-sm-1 control-label">{{ field.label }}</label>

                            <div class="col-sm-11">
                                <div class="input-group">
                                    {% for error in field.errors %}
                                        <label class="control-label"
                                               for="{{ field.id_for_label }}">{{ error }}</label>
                                    {% endfor %}
                                    {% add_class field 'form-control' %}
                                    <span class="input-group-btn">
                      <button type="button" class="btn btn-info btn-flat">上传图片</button>
                    </span>
                                </div>

                            </div>
                        </div>
                    {% else %}
                        <div class="form-group {% if field.errors %}has-error{% endif %}">

                            <label for="{{ field.id_for_label }}"
                                   class="col-sm-1 control-label">{{ field.label }}</label>

                            <div class="col-sm-11">
                                {% for error in field.errors %}
                                    <label class="control-label"
                                           for="{{ field.id_for_label }}">{{ error }}</label>
                                {% endfor %}
                                {% add_class field 'form-control' %}
                            </div>
                        </div>
                    {% endif %}
                {% endfor %}

                {{ form.media }}
            </form>

        </div>
        <div class="box-footer">

            <button type="button" class="btn btn-default back">返回</button>
            <button type="button" data-url="{% url 'myadmin:user_update' form.instance.id %}"
                    class="btn btn-primary pull-right save">保存
            </button>


        </div>
    </div>
{% endblock %}

四、新闻封面图片上传

1.接口设计

1.接口说明:

类目 说明
请求方法 POST
url定义 /admin/upload/
参数格式 form表单

2.参数说明:

参数名 类型 是否必须 描述
upload 二进制 上传的图片

3.返回数据

json数据

{
    "errno": "0",
    "errmsg": "ok",
    "data":{
        "url": "/media/666215aae3d55eaf0c8848a8340174a5.png",
        "name": "666215aae3d55eaf0c8848a8340174a5.png",
        "uploaded": "1"
    }
}

2.后端代码

1.视图

# 在myadmin/views.py中添加如下视图
class UploadFileView(View):
    """
    上传文件视图
    url:/admin/upload/
    """
    def post(self, request):
        try:
            file = request.FILES['upload']
            filename = get_filename(file.name)
            file_path = os.path.join(settings.MEDIA_ROOT, filename)
            with open(file_path, 'wb') as f:
                for chunk in file.chunks():
                    f.write(chunk)
            
            return json_response(data={
                'url': settings.MEDIA_URL + filename,
                'name': filename,
                'uploaded': '1'
            })
        except Exception as e:
            return json_response(data={'uploaded': '0'})

除了自己写视图之外,还可以直接利用ckeditor_uploader的视图,只需要在前端改url即可。

2.路由

# 在myadmin/urls.py中添加如下路由
path('upload/', views.UploadFileView.as_view(), name='upload')

3.前端代码

1.html

<!-- 修改模板 templates/myadmin/news/news_detail.html 在上传图片的button处添加一个file类型的隐藏input -->
<span class="input-group-btn"><input class="hidden" type="file" >
    <button type="button" class="btn btn-info btn-flat">上传图片</button>
</span>

2.js

// 创建 js/myadmin/news/news_detail.js
$(() => {
    // 上传封面
    // 上传文件input
    let $fileInput = $('.input-group-btn input');
    let $uploadBtn = $('.input-group-btn button');
    $uploadBtn.click(function () {
            $fileInput.click()
        }
    );
    // 自动上传文件
   $fileInput.change(function () {
        $this = $(this);
        if ($this.val() !== ''){
            let formData = new FormData();
            formData.append('upload', $this[0].files[0]);
            formData.append('csrfmiddlewaretoken', $('input[name="csrfmiddlewaretoken"]').val());
            $
                .ajax({
                    url: '/admin/upload/',
                    // 使用ckeditor_uploader 就使用下面的url
                    // url: '/ckeditor/upload/&responseType=json',
                    type: 'POST',
                    data: formData,
                    processData: false,
                    contentType: false
                })
                .done((res)=>{
                    if (res.data.uploaded === '1'){
                        message.showSuccess('封面图片上传成功!');
                        $('input[name="image_url"]').val(res.data.url);
                        // 清空一下
                        $this.val('')
                    }else{
                        message.showError('封面图片上传失败!')
                    }
                })
                .fail(()=>{
                    message.showError('服务器超时, 请重新尝试!')
                })
        }
    });
   
});

五、新闻更新功能

1.接口设计

1.接口说明:

类目 说明
请求方法 PUT
url定义 /admin/news/<int:news_id>/
参数格式 路径参数+form表单

2.参数说明:

参数名 类型 是否必须 描述
news_id 整数 需要修改的新闻id
title 字符串 需要修改的新闻标题
is_delete 字符串 是否删除新闻
digest 字符串 新闻摘要
image_url 字符串 封面图片url
tag 整数 新闻类型
content 字符串 新闻内容

3.返回数据

json数据

{
    "errno": "0",
    "errmsg": "ok",
}

2.后端代码

2.1视图

# 在admin/views.py中的NewsUpdateView视图中添加PUT方法
class NewsUpdateView(View):
    """
    新闻更新视图
    url:/admin/news/<int:news_id>/
    """
    def get(self, request, news_id):
        news = News.objects.filter(id=news_id)
        if news:
            news = news.first()
            form = NewsModeForm(instance=news)
        else:
            form = NewsModeForm()
        return render(request, 'admin/news/news_detail.html', context={'form': form})

    def put(self, request, news_id):
        news = News.objects.filter(id=news_id).first()
        put = QueryDict(request.body)
        if news:
            form = NewsModeForm(put, instance=news)
        else:
            form = NewsModeForm()

        if form.is_valid():
            # 优化
            # form.save()
            if form.has_changed():
                instance = form.save(commit=False)
                instance.save(update_fields=form.changed_data)

            return json_response(errmsg='修改新闻成功!')
        else:

            return render(request, 'admin/news/news_detail.html', context={
                'form': form,
            })

2.2其他

因为上传保存的图片url有类似/media/xxxx.jpg这样的url,直接是用django的内置URL字段类型通不过,所以需要自定义校验器。

# 在utils中创建validators.py文件,编写如下代码
import re

from django.core import validators


class MediaUrlValidator(validators.URLValidator):
    def __call__(self, value):
        try:
            super().__call__(value)
        except Exception as e:
            if re.match(r'^/media/.*$', value):
                pass
            else:
                raise e

然后修改news/models.py中的News模型中的image_url字段如下

image_url = models.CharField('图片url', max_length=200, default='', help_text='图片url', validators=[MediaUrlValidator()])

3.前端代码

3.1 html

<!-- 修改模板 templates/admin/news/news_detail.html 中保存按钮上的data-url属性 -->
<button type="button" data-url="{% url 'admin:update_news' form.instance.id %}"
                    class="btn btn-primary pull-right save">保存
            </button>

3.2 js

// 更新 js/admin/news/news_detail.js
$(() => {
    // 上传封面
    // 上传文件input
    let $fileInput = $('.input-group-btn input');
    let $uploadBtn = $('.input-group-btn button');
    $uploadBtn.click(function () {
            $fileInput.click()
        }
    );
    // 自动上传文件
    $fileInput.change(function () {
        $this = $(this);
        if ($this.val() !== ''){
            let formData = new FormData();
            formData.append('upload', $this[0].files[0]);
            $
                .ajax({
                    url: '/admin/upload/',
                    // 使用ckeditor_uploader 就使用下面的url
                    // url: '/ckeditor/upload/&responseType=json',
                    type: 'POST',
                    data: formData,
                    processData: false,
                    contentType: false
                })
                .done((res)=>{
                    if (res.uploaded === '1'){
                        message.showSuccess('封面图片上传成功!');
                        $('input[name="image_url"]').val(res.url);
                        // 清空一下
                        $this.val('')
                    }else{
                        message.showError('封面图片上传失败!')
                    }
                })
                .fail(()=>{
                    message.showError('服务器超时, 请重新尝试!')
                })
        }
    });
    // 返回按钮
    $('.box-footer button.back').click(() => {
        $('#content').load(
            $('.sidebar-menu li.active a').data('url'),
            (response, status, xhr) => {
                if (status !== 'success') {
                    message.showError('服务器超时,请重试!')
                }
            }
        );
    });

    $('.box-footer button.save').click(function () {
        // 更新富文本编辑器内容到form表单
        window.window.CKEDITOR.instances.id_content.updateElement();
        $
            .ajax({
                url: $(this).data('url'),
                data: $('form').serialize(),
                type: 'PUT'
            })
            .done((res) => {
                if (res.errno === '0') {
                    message.showSuccess(res.errmsg);
                    $('#content').load(
                        $('.sidebar-menu li.active a').data('url'),
                        (response, status, xhr) => {
                            if (status !== 'success') {
                                message.showError('服务器超时,请重试!')
                            }
                        }
                    );
                } else {
                    $('#content').html(res)
                }
            })
            .fail((res) => {
                message.showError('服务器超时,请重试!')
            })
    })

});

六、添加新闻页面

1.接口设计

1.接口说明:

类目 说明
请求方法 GET
url定义 /admin/news/
参数格式 无参数

2.返回数据

返回html

2.后端代码

1.视图

# 在admin/views.py中天添加如下视图
class NewsAddView(View):
    """
    新闻添加视图
    """
    def get(self, request):
        form = NewsModeForm()
        return render(request, 'admin/news/news_detail.html', context={'form': form})

2.路由

# 在admin/urls.py中添加如下路由     
path('news/', views.NewsAddView.as_view(), name='add_news'),

3.前端代码

1.html

<!-- 在admin/news/news_list.html 模板中,添加添加新闻按钮 -->
...
            <div class="box-tools">
                <button type="button" class="btn btn-primary btn-sm"
                        data-url="{% url 'admin:add_news' %}">添加新闻
                </button>
            </div>
...

2.js

// 在js/admin/news/news_list.js中添加添加新闻按钮的js代码
// 添加新闻
    $('.box-tools button').click(function () {
        $('#content').load(
                $(this).data('url'),
                (response, status, xhr) => {
                    if (status !== 'success') {
                        message.showError('服务器超时,请重试!')
                    }
                }
            );

    });

七、添加新闻功能

1.接口设计

1.接口说明:

类目 说明
请求方法 POST
url定义 /admin/news/
参数格式 form表单

2.参数说明:

参数名 类型 是否必须 描述
title 字符串 需要修改的新闻标题
is_delete 字符串 是否删除新闻
digest 字符串 新闻摘要
image_url 字符串 封面图片url
tag 整数 新闻类型
content 字符串 新闻内容

3.返回数据

成功返回json数据

{
    "errno": "0",
    "errmsg": "添加新闻成功!",
}

失败返回html

2.后端代码

1.视图

# 在admin/views.py中的NewsAddView视图中添加一下post方法

class NewsAddView(View):
    """
    新闻添加视图
    """
    def get(self, request):
        form = NewsModeForm()
        return render(request, 'admin/news/news_detail.html', context={'form': form})

    def post(self, request):
        form = NewsModeForm(request.POST)
        if form.is_valid():
            instance = form.save(commit=False)
            instance.author = request.user
            instance.save()
            return json_response(errmsg='添加新闻成功!')
        else:
            return render(request, 'admin/news/news_detail.html', context={'form': form})

3.前端代码

1.html

<!-- 在admin/news/news_detail.html 模板中,修改保存按钮 -->
...
            <button type="button" {% if form.instance.id %}
                    data-url="{% url 'admin:update_news' form.instance.id %}"
                    data-type="PUT"
            {% else %}
                    data-url="{% url 'admin:add_news' %}"
                    data-type="POST"
            {% endif %}
                    class="btn btn-primary pull-right save">保存
            </button>
...

2.js

// 修改js/admin/news/news_detail.js中响应点击保存按钮后的js代码如下

$('.box-footer button.save').click(function () {
        // 更新富文本编辑器内容到form表单
        window.window.CKEDITOR.instances.id_content.updateElement();
        $
            .ajax({
                url: $(this).data('url'),
                data: $('form').serialize(),
                type: $(this).data('type')
            })
            .done((res) => {
                if (res.errno === '0') {
                    message.showSuccess(res.errmsg);
                    $('#content').load(
                        $('.sidebar-menu li.active a').data('url'),
                        (response, status, xhr) => {
                            if (status !== 'success') {
                                message.showError('服务器超时,请重试!')
                            }
                        }
                    );
                } else {
                    $('#content').html(res)
                }
            })
            .fail((res) => {
                message.showError('服务器超时,请重试!')
            })
    })
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容