[django项目] 新闻管理功能

新闻管理功能

和前面的相似, 所谓管理其实就是新闻的增删改查

I. 业务功能分析

新闻的增删改查, 我们的新闻页面将具有这些功能

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

II. 新闻列表

1>业务流程分析

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

2>接口设计

2.1>接口说明:
类目说明
请求方法GET
url定义/admin/newses/
参数格式查询参数
2.2>参数说明:
参数名类型是否必须描述
title字符串要查询的新闻标题
tag整数要查询的tag_id
is_delete整数新闻是否可用
page整数父菜单id
2.3>返回数据

html

3>后端代码

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)
3.2>路由
# 在admin/views.py中添加如下路由
path('newses/', views.NewsesView.as_view(), name='news_list')

4>前端代码

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 %}
4.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('服务器超时,请重试!')
            })

    });
});

III. 新闻详情页

1>业务流程分析

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

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

django-ckeditor介绍

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

2>安装配置

2.1>安装
pip install django-ckeditor
2.2>配置
  1. INSTALLED_APPS中添加ckeditor

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

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

    然后运行命令

    python manage.py collectstatic 
    

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

    然后将STATIC_ROOT参数注释掉, 这里一定要注意!!!

  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>上传配置

3.1>配置

注意:如果需要上传功能,则还需要如下步骤

  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')),
    
3.2>自定义文件名生成函数

由于平台上线后可能会有多人上传相同名字的文件, 因此我们要自定义一个生成文件名的函数, 避免文件的名字重复

  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>接口设计

4.1> 接口说明
类目说明
请求方法GET
url定义/admin/news/<int:news_id>
参数格式路径参数
4.2>参数说明:
参数名类型是否必须描述
news_id整数要查询的新闻id
4.3>返回数据

html

5>后端代码

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

from utils.models import BaseModel

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

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

5.2>表单
# 在myadmin/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']

5.3>视图
# 在myadmin/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})

5.4>路由
# 在myadmin/urls.py中添加如下视图
path('news/<int:news_id>', views.NewsUpdateView.as_view(), name='news_update'),

6>前端代码

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('服务器超时,请重试!')
                    }
                }
            );
        })
    });

6.2>html
<!-- 修改 templates/myadmin/news/news_list.html 中for循环的第一个td -->
<td style="width: 40px" 
    data-url="{% url 'myadmin:news_update' 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 %}

IIII. 新闻封面图片上传

1>接口设计

1.1>接口说明:
类目说明
请求方法POST
url定义/admin/upload/
参数格式form表单
1.2>参数说明:
参数名类型是否必须描述
upload二进制上传的图片
1.3>返回数据

json数据

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

2>后端代码

2.1>视图

文件上传不要一次性写, 会影响效率, 因此我们采用的形式传输, 使用chunks()方法

# 在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.2>路由
# 在myadmin/urls.py中添加如下路由
path('upload/', views.UploadFileView.as_view(), name='upload')

3>前端代码

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>

3.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('服务器超时, 请重新尝试!')
                })
        }
    });
   
});

富文本编辑器帮还帮我们实现了, 截图可以直接粘贴的功能

V. 新闻更新功能

1>接口设计

1.1>接口说明:
类目说明
请求方法PUT
url定义/admin/news/<int:news_id>/
参数格式路径参数+form表单
1.2>参数说明:
参数名类型是否必须描述
news_id整数需要修改的新闻id
title字符串需要修改的新闻标题
is_delete字符串是否删除新闻
digest字符串新闻摘要
image_url字符串封面图片url
tag整数新闻类型
content字符串新闻内容
1.3>返回数据

json数据

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

2>后端代码

2.1>视图
# 在admin/views.py中的NewsUpdateView视图中添加PUT方法
class NewsUpdateView(View):
    """
    新闻更新视图
    url:/admin/news/<int:news_id>/
    """
    def put(self, request, news_id):
        # 1. 获取新闻对象
        news = News.objects.filter(id=news_id, is_delete=False).first()
        # 1.1 判断新闻是否存在
        if not news:
            return json_response(errno=Code.NODATA, errmsg='该新闻不存在!')
        # 2. 接收参数
        put_data = QueryDict(request.body)
        # 3. 创建表单对象
        form = NewsModelForm(put_data, instance=news)
        # 4. 校验
        if form.is_valid():
            # 4.1 如果成功,保存数据,返回消息ok
            # form.save()
            # 优化保存
            if form.has_changed():
                # 如果表单参数有改动
                # 先延迟form表单的保存
                instance = form.save(commit=False)
                # 然后按照有改动的字段来保存,避免错误
                instance.save(update_fields=form.changed_data)
            return json_response(errmsg='修改新闻成功!')
        else:
            # 4.2 如果失败,返回渲染了错误信息的html
            return render(request, 'myadmin/news/news_detail.html', context={'form': form})

3>前端代码

3.1>html
<!-- 修改模板 templates/admin/news/news_detail.html 中保存按钮上的data-url属性 -->
<button type="button" data-url="{% url 'myadmin:news_update' 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.change(function () {...});
    // 返回按钮
    $('.box-footer button.back').click(() => {...});
	// 保存按钮
    $('.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('服务器超时,请重试!')
            })
    })

});

4>其他

这样直接运行保存会触发一个错误, 如图:

[外链图片转存失败(img-fF5GUVfr-1568117052235)(%E6%96%B0%E9%97%BB%E7%AE%A1%E7%90%86%E5%8A%9F%E8%83%BD.assets/1567988263284.png)]

因为上传保存的图片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()])

修改完模型记得迁移

VI. 添加新闻页面

1>接口设计

1.1>接口说明:
类目说明
请求方法GET
url定义/admin/news/
参数格式无参数
1.2>返回数据

返回html

2>后端代码

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.2>路由
# 在admin/urls.py中添加如下路由		
path('news/', views.NewsAddView.as_view(), name='add_news'),

3>前端代码

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>
...
3.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('服务器超时,请重试!')
                    }
                }
            );

    });

VII. 添加新闻功能

1>接口设计

1.1>接口说明:
类目说明
请求方法POST
url定义/admin/news/
参数格式form表单
1.2>参数说明:
参数名类型是否必须描述
title字符串需要修改的新闻标题
is_delete字符串是否删除新闻
digest字符串新闻摘要
image_url字符串封面图片url
tag整数新闻类型
content字符串新闻内容
1.3>返回数据

成功返回json数据

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

失败返回html

2>后端代码

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

class NewsAddView(View):
    """
    新闻添加视图
    """
    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>前端代码

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>
...
3.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')	// <<< new!
            })
            .done((res) => {...})
            .fail((res) => {...})
    })

项目源码:https://gitee.com/hao4875/newssite

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值