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 LoginFormdef crm_login(request):"""crm 登录"""if request.method == 'GET':# 如果在前端已经登录,可以直接进入后台管理系统try:if request.user.is_authenticated:return redirect('/crm/index/')except:passlogin_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 pathfrom .views import crm_login, indexurlpatterns = [path("login/", crm_login, name='crm_login'), # crm 管理后台path("index/", index, name='crm_index'), # crm 管理后台 ]
crm/urls.py
2、后台登录权限限制:
限制只有后台权限的用户才能登录(django自带员工识别装饰器,识别只有is_staff 的用户才能登录后台系统),如后台首页登录限制,代码如下:
# crm/views.pyfrom 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 pathfrom .views import indexurlpatterns = [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 pathfrom crm.views import new_categoryurlpatterns = [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_namedef 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 Library4、实例化:register = Library() 。注:命名必须‘register’,不能改 """
templatetags 自定义标签步骤
在crm 中新建 templatetags 包,在 templatetags 包中新建crm_tags.py文件,分页代码如下:
from django.template import Library from django.utils.safestring import mark_saferegister = Library()@register.simple_tag def render_paginator(querysets):"""分页功能从views中拿到querysetspaginator = 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">«上一页</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">下一页»</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_eleele += "</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,EmptyPagefrom news.models import NewsCategorycategories = 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 categoryfunction 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 pathfrom crm.views import add_new_categoryurlpatterns = [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 categoryfunction 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 categoryfunction editAction(self) {var tr = $(self).parent().parent();var uid = tr.attr('data-pk'); // idvar 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 categoryfunction deleteAction(self) {var tr = $(self).parent().parent();var uid = tr.attr('data-pk'); // idvar 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 submitfunction 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 = Noneif exists:exists.delete()return JsonResponse({"status": True, "message": "该分类已被删除"})else:return JsonResponse({"status": False, "message": "该分类不存在!"})
新闻分类 新增/修改/删除
urls.py:
from django.urls import pathfrom crm.views import new_category, add_new_category, edit_new_category, delete_new_categoryurlpatterns = [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_namedef 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_saferegister = Library()@register.simple_tag def render_paginator(querysets):"""分页功能从views中拿到querysetspaginator = 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">«上一页</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">下一页»</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_eleele += "</ul></nav>"return mark_safe(ele)
自定义分页标签:crm_tags.py
转载于:https://www.cnblogs.com/Eric15/articles/10966188.html
Django打造大型企业官网-项目实战(三)相关推荐
- Django打造大型企业官网-项目实战(四)
Django打造大型企业官网-项目实战(四) 一.新闻相关功能 在项目实战三中,我们完成了新闻分类相关的一些功能,现在我们来完成新闻列表.发布新闻.编辑新闻.删除新闻的功能 1.发布新闻/编辑新闻 功 ...
- Django打造大型企业官网-项目部署
Django打造大型企业官网-项目部署 一.准备工作 1.在开发机上的准备工作 1)确认项目没有bug. 2)打开终端,进入虚拟环境,再 cd 到项目根目录下,执行命令:pip freeze > ...
- Django打造大型企业官网(五)
4.6.切换轮播图的箭头样式以及显示和隐藏 templates/news/index.html <span class="arrow left-arrow">‹< ...
- 鲜花网项目实战(一)
鲜花网项目实战(一) 1.开发环境和技术栈 2.安装NodeJS环境 一.环境的作用 二.下载地址 三.查看安装 3.安装Vue-CLI 一.脚手架工具介绍 二.安装命令 三.查看安装 4.创建前端项 ...
- 仿照小米官网项目具体操作与细节
本项目已上传github 有需要的可以下载 点这里前往下载 小米官网项目具体操作 1.gulp的搭建 一 , 打开node控制台 命令行输入 cd 加文件夹路径 进入当前文件夹 命令行输入 cnpm ...
- 学院官网项目三级页面总结
学院官网项目三级页面总结 (撰写时间:2019年7月6作者:李梦熙) 这个六月我来到了这个项目班,在这一个多月的时间里,虽然有时候还是不知道去做些什么,但是我在这里也学习到了很多项目管理和分配工作之类 ...
- 06.简书项目实战三:详情页面和登录功能实现
简书项目实战三:详情页面和登录功能实现 1. 详情页面布局 这部分的布局比之前的简单多了,就一个标题加上主要内容而已. export default class Detail extends Comp ...
- 新巴巴运动网 项目第三天
新巴巴运动网 项目第三天 今天内容 Dubbo优化 搭建后台管理页面 品牌管理 列表查询(带条件 + 分页) 品牌管理 去修改页面 品牌管理 异步上传图片 Dubbo优化 超时 服务消费方通过注册中心 ...
- 视频教程-Ruby on Rails打造企业级RESTful API项目实战我的云音乐-Ruby/Rails
Ruby on Rails打造企业级RESTful API项目实战我的云音乐 任苹蜻,爱学啊创始人 & CEO,曾就职于某二车手公司担任Android工程师后离职创办爱学啊,我们的宗旨是:人生 ...
最新文章
- GOF23设计模式(创建型模式) 原型模式
- VUE 笔记(持续更新中...)
- 关于计算机组装的作文,电脑小白组装电脑,能写出这样的配置,在下佩服!
- 【MongoDB数据库】怎样安装、配置MongoDB
- 虽然得了第三名,但依然很快乐...
- Amdahl’s law (阿姆达尔定律)的演化和思考
- Zabbix监控可视化
- java 判断是否包含中文_java判断字符串中是否包含中文并过滤中文
- AI攻城狮,你需要那个数据集的种子么?
- parsley.js自定义验证规则之大小写
- 在html5网页中录音解决方案
- div绑定onblur事件
- 基于RV1126 Video分析-----驱动各模块总览
- 技法の穴をふさぐ:コスト編 --人月単価は案件ごとにバラバラ公表データで相場を知る
- 06-树(tree)
- 转 大数据量下载解决方案
- 实验报告微型计算机拆卸顺序表,实验一线性表的顺序存储结构实验报告页.doc...
- sftp文件上传下载改名压缩解压
- 还不快收藏起来!何恺明全网最全论文合集
- Matlab平滑处理记录
热门文章
- 什么是UML,UML类图
- 于Mozilla平台的扩展开发
- [附源码]Python计算机毕业设计SSM流浪动物救助及领养平台(程序+LW)
- VSCode lua插件LuaHelper
- Bonobo Git Server的使用
- 云上数据保护,你以为挡住黑客就够了?
- 21世纪东方美女标准[男士参考女士学习]
- mysql 分钟_MySQL如何获取一个指定时间中的分钟数(MINUTE函数)呢?
- UART串行通信模式
- 全网最详细的介绍ChatGPT:包括ChatGPT原理、应用、如何试用以及回答ChatGPT能否让程序员失业