Django打造大型企业官网-项目实战(三)

Django打造大型企业官网-项目实战(三)


 一、CRM 后台管理系统

 前面我们使用的是 xadmin 后台管理系统,在使用中发现,在权限限制中,我们能实现不同等级的用户/管理(超级管理员/管理员/用户)登录后台时拥有不同数据的操作权限(查看、修改等),但实际上在更细方面的地方我们并没有找到有效的方法实现精准的权限限制,比如当普通用户想新增新闻时,在作者字段一栏会直接列出目前所有的用户名(作者),当前用户可以任意选择作者作为当前文章的发布者,这样明显是很不安全的,而如果我们需要改动这些细节就必须得从xadmin源码上寻求解决方法。为了更好的控制后台权限及数据展示,我们重新部署自己的后台管理系统。

 重新部署后台管理系统,需要使用到新的第三方库:adminLTE ,

 官网:https://adminlte.io/

 文档及下载:http://adminlte.la998.com/documentation/

 GitHub 下载:https://github.com/ColorlibHQ/AdminLTE

 操作演示:http://adminlte.la998.com/index2.html

 界面:

  

 一般使用,可以将所需要的页面的代码拷贝到自己项目系统中对应的位置,再把相关联的样式文件等拷贝进去。如果感兴趣,可以参考上述文档链接或查询官方文档。


 

 crm 后台管理系统

 1、后台系统登录相关功能

  新建app 并注册到settings.py下的INSTALED_APPS 中 ,命名 crm :python manage.py startapp crm 

  登录界面:

   

 登录功能相关代码:

  1)HTML前端代码:

<!-- crm/login.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>小饭桌 | 后台登录</title>
  <!-- Tell the browser to be responsive to screen width -->
  <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
  <!-- Bootstrap 3.3.7 -->
  <link rel="stylesheet" href="{% static 'adminlte/bower_components/bootstrap/dist/css/bootstrap.min.css' %}">
  <!-- Font Awesome -->
  <link rel="stylesheet" href="{% static 'adminlte/bower_components/font-awesome/css/font-awesome.min.css' %}">
  <!-- Ionicons -->
  <link rel="stylesheet" href="{% static 'adminlte/bower_components/Ionicons/css/ionicons.min.css' %}">
  <!-- Theme style -->
  <link rel="stylesheet" href="{% static 'adminlte/dist/css/AdminLTE.min.css' %}">
  <!-- iCheck -->
  <link rel="stylesheet" href="{% static 'adminlte/plugins/iCheck/square/blue.css' %}">
  <link rel="stylesheet" href="{% static 'adminlte/login error.css' %}">

  <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
  <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
  <!--[if lt IE 9]>
  <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
  <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
  <![endif]-->

  <!-- Google Font -->
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic">
</head>
<body class="hold-transition login-page">
<div class="login-box">
  <div class="login-logo">
    <a href="#">小饭桌管理系统</a>
  </div>
  <!-- /.login-logo -->
  <div class="login-box-body">
    <p class="login-box-msg">请登录</p>

    <form action="{% url 'crm_login' %}" method="post">
        {% csrf_token %}
      <div class="form-group has-feedback {% if login_form.errors.mobile.0 %}errorput{% endif %}">
        <input type="text" class="form-control" name="mobile" {% if login_form.errors.mobile.0 %} placeholder="{{ login_form.errors.mobile.0 }}" {% elif login_form.mobile.value %} value="{{ login_form.mobile.value }}" {% else %} placeholder="请输入您的手机号" {% endif %}>
        <span class="glyphicon glyphicon-envelope form-control-feedback"></span>
      </div>
      <div class="form-group has-feedback {% if login_form.errors.password.0 %} errorput {% endif %}">
        <input type="password" class="form-control" name="password" {% if login_form.errors.password.0 %} placeholder="{{ login_form.errors.password.0 }}" {% else %} placeholder="请输入密码" {% endif %}>
        <span class="glyphicon glyphicon-lock form-control-feedback"></span>
      </div>
      <div class="form-group has-feedback error">
        {{ msg }}
      </div>
      <div class="row">
        <div class="col-xs-8">
          <div class="checkbox icheck">
            <label>
              <input type="checkbox" name="remember" value="1"> 记住我
            </label>
          </div>
        </div>
        <!-- /.col -->
        <div class="col-xs-4">
          <button type="submit" class="btn btn-primary btn-block btn-flat">登录</button>
        </div>
        <!-- /.col -->
      </div>
    </form>
  </div>
  <!-- /.login-box-body -->
</div>
<!-- /.login-box -->

<!-- jQuery 3 -->
<script src="{% static 'adminlte/bower_components/jquery/dist/jquery.min.js' %}"></script>
<!-- Bootstrap 3.3.7 -->
<script src="{% static 'adminlte/bower_components/bootstrap/dist/js/bootstrap.min.js' %}"></script>
<!-- iCheck -->
<script src="{% static 'adminlte/plugins/iCheck/icheck.min.js' %}"></script>
<script>
  $(function () {
    $('input').iCheck({
      checkboxClass: 'icheckbox_square-blue',
      radioClass: 'iradio_square-blue',
      increaseArea: '20%' /* optional */
    });
  });
</script>
</body>
</html>
登录:login.html

  2)views.py 后端代码:

from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login, 

from xfz.forms import LoginForm

def crm_login(request):
    """crm 登录"""
    if request.method == 'GET':
        # 如果在前端已经登录,可以直接进入后台管理系统
        try:
            if request.user.is_authenticated:
                return redirect('/crm/index/')
        except:
            pass
        login_form = LoginForm()
        return render(request, 'crm/login.html')
    else:
        login_form = LoginForm(request.POST)
        if login_form.is_valid():
            mobile = login_form.cleaned_data.get('mobile')
            password = login_form.cleaned_data.get("password")
            remember = login_form.cleaned_data.get('remember')
            user = authenticate(request, username=mobile, password=password)
            if user:
                if user.is_active and user.is_staff:
                    login(request, user)
                    if remember:
                        request.session.set_expiry(None)
                    else:
                        request.session.set_expiry(0)
                    return redirect('/crm/index/')
                else:
                    return render(request, 'crm/login.html', {'msg': '用户无权登录后台系统', 'login_form': login_form})
            else:
                return render(request, 'crm/login.html', {'msg': '用户名或密码错误', 'login_form': login_form})
        else:
            return render(request, 'crm/login.html', {'msg': '用户名或密码格式错误,请重新输入!', 'login_form': login_form})
登录:views.py

  3)urls.py 路由:

# xfz/urls.py:path("crm/", include("apps.crm.urls")),   # crm 管理后台
from django.urls import path

from .views import crm_login, index


urlpatterns = [

    path("login/", crm_login, name='crm_login'),   # crm 管理后台
    path("index/", index, name='crm_index'),   # crm 管理后台

]
crm/urls.py

 2、后台登录权限限制:

  限制只有后台权限的用户才能登录(django自带员工识别装饰器,识别只有is_staff 的用户才能登录后台系统),如后台首页登录限制,代码如下:

# crm/views.py

from django.contrib.admin.views.decorators import staff_member_required   # 是否为后台员工识别装饰器

@staff_member_required(login_url='index')  # 不是后台员工,无法登录后台,重定向到前端首页
def index(request):
    """小饭桌管理后台首页"""
    return render(request, 'crm/index.html')

 这样,无论是在网址栏上直接输入网址还是前端跳转,只要不是员工都无法访问到后台


  3、cms后台管理系统-首页

  界面:

    

  功能实现代码:

  1)HTML前端代码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>{% block title %}{% endblock %} | 小饭桌管理系统</title>
    <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
    <link rel="stylesheet" href="{% static 'adminlte/bower_components/bootstrap/dist/css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'adminlte/bower_components/font-awesome/css/font-awesome.min.css' %}">
    <link rel="stylesheet" href="{% static 'adminlte/dist/css/AdminLTE.min.css' %}">
    <link rel="stylesheet" href="{% static 'adminlte/dist/css/skins/_all-skins.min.css' %}">
    <link rel="stylesheet" href="{% static 'adminlte/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css' %}">
    <link rel="stylesheet" href="{% static 'sweetalert/sweetalert.css' %}">
    <script src="{% static 'adminlte/bower_components/jquery/dist/jquery.min.js' %}"></script>
    <script src="{% static 'adminlte/bower_components/bootstrap/dist/js/bootstrap.min.js' %}"></script>
    <script src="{% static 'adminlte/dist/js/adminlte.min.js' %}"></script>
    <script src="{% static 'sweetalert/sweetalert.min.js' %}"></script>
{#    <script src="{% static 'js/xfzajax.min.js' %}"></script>#}
{#    <script src="{% static 'js/xfzalert.min.js' %}"></script>#}
{#    <script src="{% static 'js/message.min.js' %}"></script>#}
    {% block head %}{% endblock %}
</head>
<body class="hold-transition skin-blue sidebar-mini">
<div class="wrapper">
    <header class="main-header">
        <!-- Logo -->
        <a href="#" class="logo">
            <!-- mini logo for sidebar mini 50x50 pixels -->
            <span class="logo-mini"><b>CMS</b></span>
            <!-- logo for regular state and mobile devices -->
            <span class="logo-lg">小饭桌后台管理系统</span>
        </a>
        <!-- Header Navbar: style can be found in header.less -->
        <nav class="navbar navbar-static-top">
            <!-- Sidebar toggle button-->
            <a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button">
                <span class="sr-only">Toggle navigation</span>
            </a>

            <div class="navbar-custom-menu">
                <ul class="nav navbar-nav">
                    <li class="dropdown user user-menu">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown">
                            <img src="{% static 'images/auth/aobama.png' %}"
                                 class="user-image" alt="User Image">
                            <span class="hidden-xs">{{ request.user.username }}</span>
                        </a>
                        <ul class="dropdown-menu">
                            <!-- User image -->
                            <li class="user-header">
                                <img src="{% static 'images/auth/aobama.png' %}"
                                     class="img-circle" alt="User Image">
                                <p>
                                    {{ request.user.username }}
                                    <small>{{ request.user.employee }}</small>
                                </p>
                            </li>
                            <!-- Menu Footer-->
                            <li class="user-footer">
                                <div class="pull-left">
                                    <a href="#" class="btn btn-default btn-flat">个人信息中心</a>
                                </div>
                                <div class="pull-right">
                                    <a href="#" class="btn btn-default btn-flat">退出登录</a>
                                </div>
                            </li>
                        </ul>
                    </li>
                </ul>
            </div>
        </nav>
    </header>
    <aside class="main-sidebar">
        <!-- sidebar: style can be found in sidebar.less -->
        <section class="sidebar">
            <form action="/" method="get" class="sidebar-form">
                <div class="input-group">
                    <input type="text" name="q" class="form-control" placeholder="首页">
                    <span class="input-group-btn">
                <button type="submit" name="search" id="search-btn" class="btn btn-flat"><i class="fa fa-search"></i>
                </button>
          </span>
                </div>
            </form>
            <!-- /.search form -->
            <!-- sidebar menu: : style can be found in sidebar.less -->
            <ul class="sidebar-menu" data-widget="tree">
                <li class="active">
                    <a href="{% url 'crm_index' %}">
                        <i class="fa fa-home"></i>
                        <span>首页</span>
                    </a>
                </li>
                <li class="header">新闻管理</li>
                <li>
                    <a href="#">
                        <i class="fa fa-list"></i>
                        <span>新闻列表</span>
                    </a>
                </li>
                <li>
                    <a href="#">
                        <i class="fa fa-edit"></i>
                        <span>发布新闻</span>
                    </a>
                </li>
                <li>
                    <a href="#">
                        <i class="fa fa-tag"></i>
                        <span>新闻分类</span>
                    </a>
                </li>
                <li>
                    <a href="#">
                        <i class="fa fa-window-restore"></i>
                        <span>轮播图</span>
                    </a>
                </li>
                <li class="header">课程管理</li>
                <li>
                    <a href="#">
                        <i class="fa fa-tv"></i>
                        <span>发布课程</span>
                    </a>
                </li>
            </ul>
        </section>
    </aside>
    <div class="content-wrapper">
        <section class="content-header">
            {% block content-header %}{% endblock %}
        </section>
        <section class="content">
            {% block content %}{% endblock %}
        </section>
    </div>
    <footer class="main-footer">
        <strong>小饭桌</strong>后台管理系统
    </footer>
</div>
</body>
</html>
base.html
{% extends 'crm/base.html' %}

{% block title %}首页{% endblock %}

{% block content-header %}
    <h1>欢迎来到小饭桌CMS管理系统</h1>
{% endblock %}
crm:index.html

  2)views.py 后端代码(有待优化):

from django.contrib.admin.views.decorators import staff_member_required   # 是否为后台员工识别装饰器

@staff_member_required(login_url='index')  # 不是后台员工,无法登录后台,重定向到前端首页
def index(request):
    """小饭桌管理后台首页"""
    return render(request, 'crm/index.html')
views.py

  3)urls.py:

from django.urls import path

from .views import index


urlpatterns = [

    path("index/", index, name='crm_index'),   # crm 管理后台首页

]
urls.py

  4、新闻分类 - 功能:获取所有新闻分类 、 添加新闻分类 、 修改新闻分类 、 删除新闻分类

  需注意的是,在获取新闻分类时,需要设定权限限制,只有拥有后台管理权限的用户才能对新闻分类进行一系列的操作

 1)获取所有新闻分类

  界面:

    

 HTML前端代码:继承于crm/index.html

{% extends 'crm/base.html' %}
{% load crm_tags %}

{% block title %}
    新闻分类
{% endblock %}

{% block head %}
    <link rel="stylesheet" href="{% static 'css/news_category/category.min.css'%}">
{#    <script src="{% static 'js/news_category.min.js' %}"></script>#}
{% endblock %}

{% block content-header %}
    <h1>新闻分类</h1>
{% endblock %}

{% block content %}
    <div class="row">
        <div class="col-md-12">
            <div class="box box-primary">
                <div class="box-header">
                    <button id="add-btn" class="btn btn-primary pull-right" onclick="addAction()">添加分类</button>
                </div>
                <div class="box-body">
                    <table class="table table-bordered">
                        <thead>
                            <tr>
                                <th>分类名称</th>
                                <th>新闻数量</th>
                                <th>操作</th>
                            </tr>
                        </thead>
                        <tbody>
                            {% for category in categories %}
                                <tr data-pk="{{ category.id}}" data-name="{{ category.name }}">
                                    <td>{{ category.name }}</td>
                                    <td>{{ category.news_nums }}</td>
                                    <td>
                                        <button class="btn btn-warning btn-xs edit-btn">编辑</button>
                                        <button class="btn btn-danger btn-xs delete-btn">删除</button>
                                    </td>
                                </tr>
                            {% endfor %}
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>

    <div class="pagination">
        <!-- 分页 -->
        {% render_paginator categories %}
    </div>
news_cetagory.html

 views.py:包含分页功能

from django.shortcuts import render
from django.contrib.admin.views.decorators import staff_member_required   # 是否为后台员工识别装饰器
from django.views.decorators.http import require_POST, require_GET
from django.core.paginator import Paginator,PageNotAnInteger,EmptyPage
from news.models import NewsCategory

@staff_member_required(login_url='index')
@require_GET
def new_category(request):
    """新闻分类详情"""
    
    categories = NewsCategory.objects.all()
    # 分页(分页功能)
    paginator = Paginator(categories, 2)  # 每页显示2行数据

    page = request.GET.get('_page')
    try:
        categories = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        categories = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        categories = paginator.page(paginator.num_pages)  # paginator.num_pages:总页数,即返回最后一页
    return render(request, 'crm/news_category.html', locals())
views.py

 urls.py:

from django.urls import path

from crm.views import new_category


urlpatterns = [

    path("category/", new_category, name='news_category'),   # crm 管理后台 新闻分类

]
urls.py

 crm/models.py/news_cetagory

class NewsCategory(models.Model):
    """新闻分类"""
    name = models.CharField("新闻分类", max_length=40)
    add_time = models.DateTimeField("添加时间", default=datetime.now)

    class Meta:
        verbose_name = "新闻分类"
        verbose_name_plural = verbose_name

    def news_nums(self):
        """该分类下新闻数量"""
        return self.news_set.all().count()

    def __str__(self):
        return self.name
NewsCategory

 获取所有新闻数据后,我们需要对所有新闻数据进行分页操作,使用 templatetags 自定义标签的方式实现:

"""
templatetags包,用于创建自定义标签,再用于前端显示
自定义标签步骤: 1、在APP文件中创建 templatetags包(不是文件夹)
                2、在templatetags包中创建 kingadmin.py文件
                3、导入:from django.template import Library
                4、实例化:register = Library() 。注:命名必须‘register’,不能改
"""
templatetags 自定义标签步骤

 在crm 中新建 templatetags 包,在 templatetags 包中新建crm_tags.py文件,分页代码如下:

from django.template import Library
from django.utils.safestring import mark_safe

register = Library()


@register.simple_tag
def render_paginator(querysets):
    """
    分页功能
    从views中拿到querysets
    paginator = Paginator(querysets, 2) :一页显示2行
    querysets = paginator.page(page):当前页码
    """

    ele = '''
        <nav aria-label="Page navigation">
            <ul class="pagination">
                <li>
                    <a href="?_page=1" aria-label="shouye">
                        <span aria-hidden="true">首页</span>
                    </a>
                </li>
    '''
    if querysets.has_previous():
        p_ele = '''
        <li><a href="?_page=%s" aria-label="Previous">&laquo;上一页</a></li>
        ''' % (querysets.previous_page_number())
        ele += p_ele
    # querysets.paginator=paginator,page_range:页数范围
    for i in querysets.paginator.page_range:
        # querysets.number:当前页码

        if abs(querysets.number - i) < 4:# 只显示相邻页码,最多3页
            active = ''
            # 当前页
            if querysets.number ==i:
                active = 'active'

            p_ele = '''
            <li class="%s"><a href="?_page=%s">%s</a></li>
            '''% (active, i, i)
            ele += p_ele
        # 是否有下一页
    if querysets.has_next():
        p_ele = '''
            <li><a href="?_page=%s" aria-label="Next">下一页&raquo;</a></li>
             ''' % (querysets.next_page_number())
        ele += p_ele

        # querysets.paginator.num_pages:总页数
    p_ele = '''
        <li>
            <a href="?_page=%s" aria-label="weiye">
                <span aria-hidden="true">尾页</span>
            </a>
        </li>
    '''%(querysets.paginator.num_pages)
    ele += p_ele

    ele += "</ul></nav>"
    return mark_safe(ele)
crm_tags.py

 HTML 模板中运用:

{% load crm_tags %}

    <div class="pagination">
        <!-- 分页 -->
        {% render_paginator categories %}
    </div>
news_cetagory.html

 views.py代码实现:

from django.core.paginator import Paginator,PageNotAnInteger,EmptyPage

from news.models import NewsCategory

categories = NewsCategory.objects.all()
    # 分页(分页功能)
    paginator = Paginator(categories, 2)  # 每页显示2行数据

    page = request.GET.get('_page')
    try:
        categories = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        categories = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        categories = paginator.page(paginator.num_pages)  # paginator.num_pages:总页数,即返回最后一页
views.py

 2)新增新闻分类功能

  在新闻分类页面中,使用模态对话框的形式实现 (修改新闻分类、删除新闻分类也使用此方法)

  

 功能实现:

  HTML前端代码(结合获取新闻前端代码,及js、ajax异步post数据):

{% extends 'crm/base.html' %}
{% load crm_tags %}

{% block title %}
    新闻分类
{% endblock %}

{% block head %}
    <link rel="stylesheet" href="{% static 'css/news_category/category.min.css'%}">
{#    <script src="{% static 'js/news_category.min.js' %}"></script>#}
{% endblock %}

{% block content-header %}
    <h1>新闻分类</h1>
{% endblock %}

{% block content %}
    <div class="row">
        <div class="col-md-12">
            <div class="box box-primary">
                <div class="box-header">
                    <button id="add-btn" class="btn btn-primary pull-right" onclick="addAction()">添加分类</button>
                </div>
                <div class="box-body">
                    <table class="table table-bordered">
                        <thead>
                            <tr>
                                <th>分类名称</th>
                                <th>新闻数量</th>
                                <th>操作</th>
                            </tr>
                        </thead>
                        <tbody>
                            {% for category in categories %}
                                <tr data-pk="{{ category.id}}" data-name="{{ category.name }}">
                                    <td>{{ category.name }}</td>
                                    <td>{{ category.news_nums }}</td>
                                    <td>
                                        <button class="btn btn-warning btn-xs edit-btn">编辑</button>
                                        <button class="btn btn-danger btn-xs delete-btn">删除</button>
                                    </td>
                                </tr>
                            {% endfor %}
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>

    <div class="pagination">
        <!-- 分页 -->
        {% render_paginator categories %}
    </div>

    <!-- add cetagory -->
    <div class="shade hide" id="shade-hide"></div>
    <div class="login-box-body add-action hide" id="add-form">
        <p class="login-box-msg">添加新闻分类</p>
        <span class="glyphicon glyphicon-remove form-control-feedback" id="add-close" onclick="addClose()"></span>
        <form action="javascript:void(0)" method="post" id="add-form">
            {% csrf_token %}
            <div class="form-group has-feedback {% if not msg.status %}errorput{% endif %}">
                <input type="text" class="form-control" name="name" placeholder="请输入新闻分类">
            </div>
            <div>
                <div class="col-xs-7 add-btn">
                    <button type="submit" class="btn btn-primary btn-block btn-flat" onclick="addCetagory()">确定</button>
                </div>
            </div>
        </form>
    </div>

{% endblock %}

{% block front-js %}
        <script>
        // add category
        function addAction() {
            $("#shade-hide").removeClass("hide");
            $("#add-form").removeClass("hide");
        }
        function addClose() {
            $("#shade-hide").addClass("hide");
            $("#add-form").addClass("hide");
        }
        function addCetagory() {
            var nameInput = $("input[name='name']").val();
            if(nameInput == ""){
                alert("请输入新闻分类!")
            }
            else {
                $.ajax({
                    cache:false,
                    type:'post',
                    async:true,
                    headers:{"X-CSRFToken":"{{ csrf_token }}"},  // 获取csrf token
                    dataType:'json',
                    url:'{% url 'add_category' %}',
                    data:{'name':nameInput},
                    'success':function (result) {
                        console.log(result);
                        alert(result['message']);
                        if(result['status'] == true){
                            window.location.href = '{% url "news_category" %}'
                        }
                    },
                    'fail':function (error) {
                        console.log(error);
                    }
                })
            }
        }

    </script>
{% endblock %}
add_cetagory

  views.py:

@require_POST
def add_new_category(request):
    """新增-新闻分类"""
    name = request.POST.get('name')
    print("name:", name)
    exists = NewsCategory.objects.filter(name=name).exists()

    if exists:
        return JsonResponse({"status": False, "message": "该分类已经存在"})

    else:
        NewsCategory.objects.create(name=name)
        return JsonResponse({"status": True, "message": "分类添加成功"})
views.py

   urls.py:

from django.urls import path

from crm.views import add_new_category


urlpatterns = [

    path("add_category/", add_new_category, name='add_category'),   # crm 管理后台 新闻分类

]
urls.py

  3)修改新闻分类 、删除新闻分类

  修改新闻分类、删除新闻分类,在本质来说实现方式是差不多的,当 修改/删除 某条新闻分类时,我们获取当条分类的分类名及id号,在生成的模态对话框中(修改/删除页面),将当前选择的当条分类的分类名及分类id填到对应的输入框中,其中id input框选择隐藏属性,目的只是将当条分类的id传回给后端,方便结合分类名对当条新闻分类进行相关修改或删除操作,在实现删除功能能,分类名input框最好改成p标签等,起到用户不能修改数据的作用。

  关于修改新闻分类、删除新闻分类功能实现,结合新增新闻分类相关功能代码,具体实现如下(新闻分类 新增/修改/删除 全部前后端代码),关于项目所用的js、css及其他相关文件均会在项目博文最后提供下载地址

  修改操作界面:

  

  删除操作界面:

  

 HTML前端代码:

{% extends 'crm/base.html' %}
{% load crm_tags %}

{% block title %}
    新闻分类
{% endblock %}

{% block head %}
    <link rel="stylesheet" href="{% static 'css/news_category/category.min.css'%}">
{#    <script src="{% static 'js/news_category.min.js' %}"></script>#}
{% endblock %}

{% block content-header %}
    <h1>新闻分类</h1>
{% endblock %}

{% block content %}
    <div class="row">
        <div class="col-md-12">
            <div class="box box-primary">
                <div class="box-header">
                    <button id="add-btn" class="btn btn-primary pull-right" onclick="addAction()">添加分类</button>
                </div>
                <div class="box-body">
                    <table class="table table-bordered">
                        <thead>
                            <tr>
                                <th>分类名称</th>
                                <th>新闻数量</th>
                                <th>操作</th>
                            </tr>
                        </thead>
                        <tbody>
                            {% for category in categories %}
                                <tr data-pk="{{ category.id}}" data-name="{{ category.name }}">
                                    <td>{{ category.name }}</td>
                                    <td>{{ category.news_nums }}</td>
                                    <td>
                                        <button class="btn btn-warning btn-xs edit-btn" onclick="editAction(this)">编辑</button>
                                        <button class="btn btn-danger btn-xs delete-btn" onclick="deleteAction(this)">删除</button>
                                    </td>
                                </tr>
                            {% endfor %}
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>

    <div class="pagination">
        <!-- 分页 -->
        {% render_paginator categories %}
    </div>

    <!-- add category -->
    <div class="shade hide" id="add-category"></div>
    <div class="login-box-body add-action hide" id="add-form">
        <p class="login-box-msg">添加新闻分类</p>
        <span class="glyphicon glyphicon-remove form-control-feedback close-icon" id="add-close" onclick="addClose()"></span>
        <form action="javascript:void(0)" method="post" id="add-form">
            {% csrf_token %}
            <div class="form-group has-feedback">
                <input type="text" class="form-control" name="name" placeholder="请输入新闻分类" id="add-input">
            </div>
            <div>
                <div class="col-xs-7 add-btn">
                    <button type="submit" class="btn btn-primary btn-block btn-flat" id="add-submit">确定</button>
                </div>
            </div>
        </form>
    </div>

    <!-- edit category -->
    <div class="shade hide" id="edit-category"></div>
    <div class="login-box-body add-action hide" id="edit-form">
        <p class="login-box-msg">修改新闻分类</p>
        <span class="glyphicon glyphicon-remove form-control-feedback close-icon" id="edit-close" onclick="editClose()"></span>
        <form action="javascript:void(0)" method="post">
            {% csrf_token %}
            <div class="form-group has-feedback">
                <input type="text" class="form-control" name="name" placeholder="请输入新闻分类" id="edit-input">
                <input type="hidden" class="form-control" name="uid" placeholder="编号" id="edit-uid">
            </div>
            <div>
                <div class="col-xs-7 add-btn">
                    <button type="submit" class="btn btn-primary btn-block btn-flat" id="edit-submit">确定</button>
                </div>
            </div>
        </form>
    </div>


    <!-- delete category -->
    <div class="shade hide" id="delete-category"></div>
    <div class="login-box-body add-action hide" id="delete-form">
        <p class="login-box-msg">您确定删除此条分类吗?</p>
        <span class="glyphicon glyphicon-remove form-control-feedback close-icon" id="delete-close" onclick="deleteClose()"></span>
        <form action="javascript:void(0)" method="post">
            {% csrf_token %}
            <div class="form-group has-feedback">
                <p class="delete-category form-control"></p>
                <input type="hidden" class="form-control" name="uid" placeholder="编号" id="delete-uid">
            </div>
            <div>
                <span class="btn btn-info pull-right" onclick="backAction()">返回</span>
                <input type="submit" class="btn btn-danger pull-right margin-r-5" value="确认删除" onclick="deleteSubmit()">
            </div>
        </form>
    </div>

{% endblock %}

{% block front-js %}
    <script>

    // add category
    function addAction() {
        $("#add-category").removeClass("hide");
        $("#add-form").removeClass("hide");
        $(".pagination").addClass("hide");
    }
    function addClose() {
        $("#add-category").addClass("hide");
        $("#add-form").addClass("hide");
        $(".pagination").removeClass("hide");
    }
    // add-category submit
    $("#add-submit").click(function () {
        var nameInput = $("#add-input").val();
        if(nameInput == ""){
            alert("请输入新闻分类!")
        }
        else {
            $.ajax({
                cache:false,
                type:'post',
                async:true,
                headers:{"X-CSRFToken":"{{ csrf_token }}"},  // 获取csrf token
                dataType:'json',
                url:"{% url 'add_category' %}",
                data:{'name':nameInput},
                'success':function (result) {
                    alert(result['message']);
                    if(result['status'] == true){
                        window.location.href = '{% url "news_category" %}'
                    }
                },
                'fail':function (error) {
                    console.log(error);
                }
            })
        }
    });


    // edit category
    function editAction(self) {
        var tr = $(self).parent().parent();
        var uid = tr.attr('data-pk');   // id
        var name = tr.attr('data-name');   // name
        $("#edit-input").val(name);   //将name 填充到修改新闻分类的name栏上, 不用这种方式
        // $("input[name='name']").attr('placeholder',name);   //将name 填充到修改新闻分类的name栏上,placeholder形式
        $("#edit-uid").val(uid);   //将 id 号填充到修改分类的隐藏栏上,目的是为了给后台提供当前修改的某条数据
        $("#edit-category").removeClass("hide");
        $("#edit-form").removeClass("hide");
        $(".pagination").addClass("hide");
    }
    function editClose() {
        $("#edit-category").addClass("hide");
        $("#edit-form").addClass("hide");
        $(".pagination").removeClass("hide");
    }
    // edit-category submit
    $("#edit-submit").click(function () {
        var nameInput = $("#edit-input").val();
        var uidInput = $("#edit-uid").val();
        if(nameInput == ""){
            alert("请输入新闻分类!")
        }
        else {
            $.ajax({
                cache:false,
                type:'post',
                async:true,
                headers:{"X-CSRFToken":"{{ csrf_token }}"},  // 获取csrf token
                dataType:'json',
                url:"{% url 'edit_category' %}",
                data:{'name':nameInput, 'uid':uidInput},
                'success':function (result) {
                    alert(result['message']);
                    if(result['status'] == true){
                        window.location.href = '{% url "news_category" %}'
                    }
                },
                'fail':function (error) {
                    console.log(error);
                }
            })
        }
    });


    // delete category
    function deleteAction(self) {
        var tr = $(self).parent().parent();
        var uid = tr.attr('data-pk');   // id
        var name = tr.attr('data-name');   // name
        // $("#delete-input").val(name);   //将name 填充到隐藏 name栏上, 用于提交数据到后台
        $(".delete-category").text(name);
        $("#delete-uid").val(uid);   //将 id 号填充到删除分类的隐藏栏上,目的是为了给后台提供当前删除的某条数据
        $("#delete-category").removeClass("hide");
        $("#delete-form").removeClass("hide");
        $(".pagination").addClass("hide");
    }
    function deleteClose() {
        $("#delete-category").addClass("hide");
        $("#delete-form").addClass("hide");
        $(".pagination").removeClass("hide");
    }
    function backAction() {
        $("#delete-category").addClass("hide");
        $("#delete-form").addClass("hide");
        $(".pagination").removeClass("hide");
    }
    // delete-category submit
    function deleteSubmit() {
        var nameInput = $(".delete-category").text();
        var uidInput = $("#delete-uid").val();   // 结合uid、name双重判断,这样就算前端修改了name数值,id不通过也无法删除数据,保证安全
            $.ajax({
                cache:false,
                type:'post',
                async:true,
                headers:{"X-CSRFToken":"{{ csrf_token }}"},  // 获取csrf token
                dataType:'json',
                url:"{% url 'delete_category' %}",
                data:{'name':nameInput, 'uid':uidInput},
                'success':function (result) {
                    alert(result['message']);
                    if(result['status'] == true){
                        window.location.href = '{% url "news_category" %}'
                    }
                },
                'fail':function (error) {
                    console.log(error);
                }
            })
    }

    </script>
{% endblock %}
news_category.html

  views.py:

from django.shortcuts import render, redirect
from django.contrib.admin.views.decorators import staff_member_required   # 是否为后台员工识别装饰器
from django.views.decorators.http import require_POST, require_GET
from django.http import JsonResponse
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage


@staff_member_required(login_url='index')
@require_GET
def new_category(request):
    """新闻分类详情"""
    # if request.user.is_superuser:
    #     categories = NewsCategory.objects.all()  # 返回所有分类
    # else:
    #     categories = NewsCategory.objects.filter(news__author=request.user)   # 返回当前用户的分类
    # return render(request, 'crm/news_category.html', locals())
    categories = NewsCategory.objects.all().order_by("-id")
    # 分页(分页功能)
    paginator = Paginator(categories, 2)  # 每页显示2行数据

    page = request.GET.get('_page')
    try:
        categories = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        categories = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        categories = paginator.page(paginator.num_pages)  # paginator.num_pages:总页数,即返回最后一页
    return render(request, 'crm/news_category.html', locals())


@require_POST
def add_new_category(request):
    """新增-新闻分类"""
    name = request.POST.get('name')
    exists = NewsCategory.objects.filter(name=name).exists()

    if exists:
        return JsonResponse({"status": False, "message": "该分类已经存在"})

    else:
        NewsCategory.objects.create(name=name)
        return JsonResponse({"status": True, "message": "分类添加成功"})


@require_POST
def edit_new_category(request):
    """修改-新闻分类"""
    uid = request.POST.get('uid')
    name = request.POST.get('name')
    exists = NewsCategory.objects.filter(name=name).exists()

    if exists:
        return JsonResponse({"status": False, "message": "该分类已经存在"})

    else:
        NewsCategory.objects.filter(id=uid).update(name=name)
        return JsonResponse({"status": True, "message": "分类编辑完成"})


@require_POST
def delete_new_category(request):
    """删除-新闻分类"""
    name = request.POST.get('name')
    id = request.POST.get('uid')
    try:
        exists = NewsCategory.objects.filter(name=name, id=id)[0]
    except Exception as e:
        exists = None

    if exists:
        exists.delete()
        return JsonResponse({"status": True, "message": "该分类已被删除"})

    else:
        return JsonResponse({"status": False, "message": "该分类不存在!"})
新闻分类 新增/修改/删除

 urls.py:

from django.urls import path

from crm.views import new_category, add_new_category, edit_new_category, delete_new_category


urlpatterns = [

    path("category/", new_category, name='news_category'),   # crm 管理后台 新闻分类
    path("add_category/", add_new_category, name='add_category'),   # crm 管理后台 添加新闻分类
    path("edit_category/", edit_new_category, name='edit_category'),   # crm 管理后台 修改新闻分类
    path("delete_category/", delete_new_category, name='delete_category'),   # crm 管理后台 删除新闻分类

]
urls.py

 models.py:

class NewsCategory(models.Model):
    """新闻分类"""
    name = models.CharField("新闻分类", max_length=40)
    add_time = models.DateTimeField("添加时间", default=datetime.now)

    class Meta:
        verbose_name = "新闻分类"
        verbose_name_plural = verbose_name

    def news_nums(self):
        """该分类下新闻数量"""
        return self.news_set.all().count()

    def __str__(self):
        return self.name
news/models.py

 crm_tag.py 自定义分页标签:

from django.template import Library
from django.utils.safestring import mark_safe

register = Library()


@register.simple_tag
def render_paginator(querysets):
    """
    分页功能
    从views中拿到querysets
    paginator = Paginator(querysets, 2) :一页显示2行
    querysets = paginator.page(page):当前页码
    """

    ele = '''
        <nav aria-label="Page navigation">
            <ul class="pagination">
                <li>
                    <a href="?_page=1" aria-label="shouye">
                        <span aria-hidden="true">首页</span>
                    </a>
                </li>
    '''
    if querysets.has_previous():
        p_ele = '''
        <li><a href="?_page=%s" aria-label="Previous">&laquo;上一页</a></li>
        ''' % (querysets.previous_page_number())
        ele += p_ele
    # querysets.paginator=paginator,page_range:页数范围
    for i in querysets.paginator.page_range:
        # querysets.number:当前页码

        if abs(querysets.number - i) < 4:# 只显示相邻页码,最多3页
            active = ''
            # 当前页
            if querysets.number ==i:
                active = 'active'

            p_ele = '''
            <li class="%s"><a href="?_page=%s">%s</a></li>
            '''% (active, i, i)
            ele += p_ele
        # 是否有下一页
    if querysets.has_next():
        p_ele = '''
            <li><a href="?_page=%s" aria-label="Next">下一页&raquo;</a></li>
             ''' % (querysets.next_page_number())
        ele += p_ele

        # querysets.paginator.num_pages:总页数
    p_ele = '''
        <li>
            <a href="?_page=%s" aria-label="weiye">
                <span aria-hidden="true">尾页</span>
            </a>
        </li>
    '''%(querysets.paginator.num_pages)
    ele += p_ele

    ele += "</ul></nav>"
    return mark_safe(ele)
自定义分页标签:crm_tags.py

 

转载于:https://www.cnblogs.com/Eric15/articles/10966188.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值