日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

[django项目] 后台菜单管理功能

發(fā)布時(shí)間:2024/3/26 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [django项目] 后台菜单管理功能 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

后臺(tái)菜單管理功能

菜單的管理功能其實(shí)就是, 對(duì)菜單的增刪改查

I. 業(yè)務(wù)功能分析

1>業(yè)務(wù)需求分析

后臺(tái)首頁(yè)菜單根據(jù)用戶權(quán)限動(dòng)態(tài)生成,不同菜單對(duì)應(yīng)不同的功能視圖。

菜單的增刪改查。

2>功能分析

  • 菜單列表
  • 添加菜單
  • 修改菜單
  • 刪除菜單

3>模型設(shè)計(jì)

3.1>字段分析

  • name, 菜單名
  • url, 菜單的路由
  • parent, 父菜單的id
  • order, 排序
  • permission, 訪問(wèn)該菜單的權(quán)限名
  • icon, 菜單顯示的icon
  • codename, 菜單的權(quán)限碼
  • is_visible, 是否可見(jiàn)

3.2>模型定義

# 在myadmin/models.py中定義如下模型 from django.db import models from django.contrib.auth.models import Permissionfrom utils.models import BaseModel # Create your models here.class Menu(BaseModel):name = models.CharField('菜單名', max_length=48, help_text='菜單名')url = models.CharField('url', max_length=256, null=True, blank=True, help_text='url')parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')order = models.SmallIntegerField('排序', default=0)permission = models.OneToOneField(Permission, on_delete=models.SET_NULL, null=True)icon = models.CharField('圖標(biāo)', max_length=48, default='fa-link')codename = models.CharField('權(quán)限碼', max_length=48, help_text='權(quán)限碼', unique=True)is_visible = models.BooleanField('是否可見(jiàn)', default=False)class Meta:ordering = ['-order']db_table = 'tb_menu'verbose_name = '菜單'verbose_name_plural = verbose_namedef __str__(self):return self.name

模型創(chuàng)建后記得遷移, 項(xiàng)目到目前的階段, 數(shù)據(jù)庫(kù)的內(nèi)容開(kāi)始變得比較復(fù)雜了, 所以該遷移的時(shí)候一定不要落下

II. 菜單列表

1>業(yè)務(wù)流程分析

  • 獲取未刪除,的一級(jí)菜單
  • 根據(jù)一級(jí)菜單獲取未刪除的二級(jí)菜單
  • 渲染頁(yè)面
  • 2>接口設(shè)計(jì)

  • 接口說(shuō)明
  • 類目說(shuō)明
    請(qǐng)求方法GET
    url定義/admin/menus/
    參數(shù)格式無(wú)參數(shù)
  • 返回結(jié)果

    html

  • 3>后端代碼

    3.1>視圖

    # myadmin/views.py下定義如下視圖: class MenuListView(View):"""菜單列表視圖url:/admin/menu_list/"""def get(self, request):# 為了便于后續(xù)的修改, 需要展示被邏輯刪除的菜單,# 因此filter的is_delete屬性就不許要加了menus = models.Menu.objects.only('name', 'url', 'icon', 'is_visible', 'order', 'codename').filter(parent=None)# parent=None表示沒(méi)有父菜單, 即一級(jí)菜單return render(request, 'myadmin/menu/menu_list.html', context={'menus': menus}) # myadmin/views.py中修改菜單管理對(duì)應(yīng)的路由: class IndexView(View):"""后臺(tái)首頁(yè)視圖"""def get(self, request):menus = [{...},{...},{...},{...},{...},{"name": "系統(tǒng)設(shè)置","icon": "fa-cogs","children": [{...},{...},{"name": "菜單管理","url": "myadmin:menu_list"},{...}]}]return render(request, 'myadmin/index.html', context={'menus': menus})

    3.2>路由

    # admin/urls.py中添加如下路由 path('menu_list/', views.MenuListView.as_view(), name='menu_list'),

    4>前端代碼

    4.1>html

    咱們先簡(jiǎn)單的寫一下前端, 然后去看一下頁(yè)面的情況

    <!-- 創(chuàng)建templates/myadmin/menu/menu_list.html--> {% extends 'myadmin/base/content_base.html' %} {% load static %} {% block page_header %}系統(tǒng)設(shè)置{% endblock %} {% block page_option %}菜單管理{% endblock %}

    創(chuàng)建好后記得重啟一下django的服務(wù)哦

    頁(yè)面效果:

    可以打開(kāi)這個(gè)界面, 就說(shuō)明路由和視圖配置好了

    接下來(lái)將列表填充到這個(gè)頁(yè)面, 我們可以使用AdminLTE為用戶提供的表格模板:

    這個(gè)頁(yè)面上有很多類型的表格, 修改前端代碼

    {% extends 'myadmin/base/content_base.html' %} {% load static %} {% block page_header %}系統(tǒng)設(shè)置{% endblock %} {% block page_option %}菜單管理{% endblock %}{% block content %}<div class="box"><div class="box-header"><h3 class="box-title">菜單列表</h3><div class="box-tools"><button type="button" class="btn btn-primary btn-sm">添加菜單</button></div></div><!-- /.box-header --><div class="box-body"><table class="table table-bordered "><tbody><tr role="row"><!-- 列表字段 --><th>菜單</th><th>子菜單</th><th>路由地址</th><th>圖標(biāo)</th><th>權(quán)限碼</th><th>順序</th><th>是否可見(jiàn)</th><th>邏輯刪除</th><th>操作</th></tr>{% for menu in menus %}<!-- 循環(huán)遍歷列表內(nèi)容 --><tr><!-- 遍歷父菜單屬性 --><td>{{ menu.name }}</td><td><!-- 子菜單為空 --></td><td>{{ menu.url|default:'' }}</td><td>{{ menu.icon }}</td><td>{{ menu.codename }}</td><td>{{ menu.order }}</td><td>{% if menu.is_visible %}是{% else %}否{% endif %}</td><td style="width: 100px" data-id="{{ menu.id }}" data-name="{{ menu.name }}">{% if menu.children.all %}<button type="button" class="btn btn-info btn-xs edit">編輯</button>{% else %}<button type="button" class="btn btn-info btn-xs edit">編輯</button><button type="button" class="btn btn-danger btn-xs delete">刪除</button>{% endif %}</td></tr>{% if menu.children.all %}{% for child in menu.children.all %}<!-- 遍歷子菜單屬性 --><tr><td><!-- 父菜單為空 --></td><td>{{ child.name }}</td><td>{{ child.url }}</td><td>{{ child.icon }}</td><td>{{ child.codename }}</td><td>{{ child.order }}</td><td style="width: 80px">{% if child.is_visible %}是{% else %}否{% endif %}</td><td style="width: 100px" data-id="{{ child.id }}" data-name="{{ child.name }}"><button type="button" class="btn btn-info btn-xs edit">編輯</button><button type="button" class="btn btn-danger btn-xs delete">刪除</button></td></tr>{% endfor %}{% endif %}{% endfor %}</tbody></table></div><!-- /.box-body --></div> {% endblock %}

    注意父菜單和子菜單循環(huán)體中的那兩個(gè)<td></td>標(biāo)簽, 這樣做得目的是為了更好的分辨父菜單和子菜單

    III. 添加菜單頁(yè)面

    功能概述: 點(diǎn)擊添加菜單按鈕, 彈出新增菜單窗口, 輸入信息提交, 即可添加到菜單列表中

    1>接口設(shè)計(jì)

  • 接口說(shuō)明:
  • 類目說(shuō)明
    請(qǐng)求方法GET
    url定義/admin/menu/
    參數(shù)格式無(wú)參數(shù)
  • 返回?cái)?shù)據(jù)

    html

  • 2>后端代碼

    2.1>視圖

    # 在myadmin/views.py中添加如下視圖 class MenuAddView(View):"""添加菜單視圖url:/admin/menu/"""def get(self, request):form = MenuModelForm()return render(request, 'myadmin/menu/add_menu.html', context={'form': form})

    2.2>路由

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

    2.3>表單

    # 在myadmin/forms.py中定義如下表單 from django import formsfrom .models import Menuclass MenuModelForm(forms.ModelForm):parent = forms.ModelChoiceField(queryset=None, required=False, help_text='父菜單')def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)self.fields['parent'].queryset = Menu.objects.filter(is_delete=False, is_visible=True, parent=None)# https://docs.djangoproject.com/en/2.2/ref/forms/fields/#fields-which-handle-relationshipsclass Meta:model = Menufields = ['name', 'url', 'order', 'parent', 'icon', 'codename', 'is_visible']

    2.4>自定義標(biāo)簽

    為了在渲染表單是能加入自定義css樣式,在應(yīng)用admin中定義自定義標(biāo)簽,在admin下創(chuàng)建templatetags包,在其中創(chuàng)建admin_customer_tags.py模塊

    # myadmin/tamplatetags/admin_customer_tags.py from django.template import Libraryregister = Library()@register.simple_tag() def add_class(field, class_str):return field.as_widget(attrs={'class': class_str})

    3>前端代碼

    3.1>html

    <!-- 修改 templates/myadmin/menu/menu_list.html --> {% extends 'myadmin/base/content_base.html' %} {% load static %} {% block page_header %}系統(tǒng)設(shè)置{% endblock %} {% block page_option %}菜單管理{% endblock %} {% block content %}<div class="box"><div class="box-header with-border">...</div><!-- /.box-header --><div class="box-body">...</div></div><!-- add modle --><div class="modal fade" id="modal-add" role="dialog" ><div class="modal-dialog"><div class="modal-content"></div><!-- /.modal-content --></div><!-- /.modal-dialog --></div><!-- /.modal -->{% endblock %}

    彈出窗口我們選用表單的形式來(lái)完成, 因此這里單獨(dú)創(chuàng)建一個(gè)模型, 方便渲染

    這里的模型來(lái)自Bootstarp

    <!-- 新建 templates/myadmin/menu/add_menu.html -->{% load admin_customer_tags %}{% load static %}<div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button><h4 class="modal-title">添加菜單</h4></div><div class="modal-body"><form class="form-horizontal" id="add-menu">{% csrf_token %}<div class="box-body">{% for field in form %}{% if field.name == 'is_visible' %}<div class="form-group"><div class="col-sm-offset-2 col-sm-10"><div class="checkbox"><label for="{{ field.id_for_label }}">{{ field }}{{ field.label }}</label></div></div></div>{% else %}<div class="form-group {% if field.errors %}has-error{% endif %}"><label for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{ field.label }}</label><div class="col-sm-10">{% 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 %}</div></form></div><div class="modal-footer"><button type="button" class="btn btn-default pull-left" data-dismiss="modal">取消</button><button type="button" class="btn btn-primary add">添加</button></div>

    根據(jù)官方文檔我們還需要加一個(gè)href=#foo(路由地址)屬性到添加菜單按鈕標(biāo)簽中

    <div class="box-tools"><button type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#modal-add" href="/admin/add_menu/">添加菜單</button> </div>

    頁(yè)面效果:

    IIII. 添加菜單

    1>業(yè)務(wù)流程分析

    • 接收表單參數(shù)
    • 校驗(yàn)表單參數(shù)
    • 校驗(yàn)成功保存菜單數(shù)據(jù),創(chuàng)建菜單一對(duì)一關(guān)聯(lián)權(quán)限對(duì)象,返回創(chuàng)建成功的json數(shù)據(jù)
    • 校驗(yàn)失敗,返回渲染了錯(cuò)誤信息的表單

    2>接口設(shè)計(jì)

    2.1>接口說(shuō)明:

    類目說(shuō)明
    請(qǐng)求方法POST
    url定義/admin/menu/
    參數(shù)格式表單參數(shù)

    2.2>參數(shù)說(shuō)明:

    參數(shù)名類型是否必須描述
    name字符串菜單名
    url字符串當(dāng)前文章頁(yè)數(shù)
    order整數(shù)排序
    parent整數(shù)父菜單id
    icon字符串渲染圖標(biāo)類名
    codename字符串權(quán)限碼
    is_visible整數(shù)是否可見(jiàn)

    2.3>返回?cái)?shù)據(jù)

    # 添加正常返回json數(shù)據(jù) { "errno": "0", "errmsg": "菜單添加成功!" }

    如果有錯(cuò)誤,返回html表單

    3>后端代碼

    3.1>視圖

    # 在myadmin/views.py中的MenuAddView視圖中添加post方法 class MenuAddView(View):"""添加菜單視圖url:/admin/menu/"""def get(self, request):form = MenuModelForm()return render(request, 'myadmin/menu/add_menu.html', context={'form': form})def post(self, request):form = MenuModelForm(request.POST)if form.is_valid():new_menu = form.save()content_type = ContentType.objects.filter(app_label='myadmin', model='menu').first()permission = Permission.objects.create(name=new_menu.name, content_type=content_type, codename=new_menu.codename)new_menu.permission = permissionnew_menu.save(update_fields=['permission'])return json_response(errmsg='菜單添加成功!')else:return render(request, 'myadmin/menu/add_menu.html', context={'form': form})

    4>前端代碼

    4.1>js

    // 創(chuàng)建static/js/myadmin/menu/add_menu.js $(() => {let $addBtn = $('button.add'); // 模態(tài)框中的添加按鈕let $form = $('#add-menu'); // 模態(tài)礦中的表單let data = {};$addBtn.click(function () {$.ajax({url: '/admin/menu/',type: 'POST',data: $form.serialize(),// dataType: "json"}).done((res) => {if (res.errno === '0') {// 添加成功,關(guān)閉模態(tài)框,并刷新菜單列表$('#modal-add').modal('hide').on('hidden.bs.modal', function (e) {$('#content').load($('.sidebar-menu li.active a').data('url'),(response, status, xhr) => {if (status !== 'success') {message.showError('服務(wù)器超時(shí),請(qǐng)重試!')}});});message.showSuccess(res.errmsg);} else {message.showError('添加菜單失敗!');// 更新模特框中的表單信息$('#modal-add .modal-content').html(res)}}).fail(() => {message.showError('服務(wù)器超時(shí),請(qǐng)重試');});});});

    4.2>html

    <!-- 在 templates/myadmin/menu/add_menu.html 中引入js --> ... <script src="{% static 'js/myadmin/menu/add_menu.js' %}"></script>

    V. 刪除菜單

    1>接口設(shè)計(jì)

    1.1>接口說(shuō)明:

    類目說(shuō)明
    請(qǐng)求方法DELETE
    url定義/admin/menu/<int:menu_id>/
    參數(shù)格式路徑參數(shù)

    1.2>參數(shù)說(shuō)明

    參數(shù)名類型是否必須描述
    menu_id整數(shù)菜單id

    1.3>返回值

    { "errno": "0", "errmsg": "刪除菜單成功!" }

    后臺(tái)展示的菜單不重要,所以我們不用邏輯刪除菜單,而是采用真刪除

    2>后端代碼

    2.1>視圖

    # 在admin/views.py中創(chuàng)建一個(gè)MenuUpdateView視圖 class MenuUpdateView(View):"""菜單管理視圖,delete刪除菜單url:/admin/menu/<int:menu_id>/"""def delete(self, request, menu_id):# 獲取到需要?jiǎng)h除的菜單menu = models.Menu.objects.only('name').filter(id=menu_id)if menu:menu = menu[0]# 判斷是是否為父菜單if menu.children.filter(is_delete=False).exists():return json_response(errno=Code.DATAERR, errmsg='父菜單不能刪除!')# 將menu模型中的permission設(shè)置為CASCADE級(jí)聯(lián)刪除, 就可以使其在被刪除的時(shí)候同時(shí)刪除當(dāng)條菜單menu.permission.delete()# menu.delete()return json_response(errmsg='刪除菜單:%s成功' % menu.name)else:return json_response(errno=Code.NODATA, errmsg='菜單不存在!')

    2.2>路由

    # 在admin/urls.py中添加如下路由 path('menu/<int:menu_id>/', views.MenuUpdateView.as_view(), name='menu_manage'),

    3>前端代碼

    3.1>html

    <!-- 修改 templates/admin/menu/menu_list.html 在content中,添加刪除模態(tài)框 然后引入menu.js --> {% block content %} ... ...<div class="modal modal-danger fade" id="modal-delete"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button><h4 class="modal-title">警告</h4></div><div class="modal-body"><p>One fine body&hellip;</p></div><div class="modal-footer"><button type="button" class="btn btn-outline pull-left" data-dismiss="modal">取消</button><button type="button" class="btn btn-outline delete-confirm">刪除</button></div></div><!-- /.modal-content --></div><!-- /.modal-dialog --></div><!-- /.modal --> {% endblock %} {% block script %}<script src="{% static 'js/admin/menu/menu_list.js' %}"></script> {% endblock %}

    記得再去content_base里挖一個(gè)script的block

    3.2>js

    // 創(chuàng)建 static/js/admin/menu/menu_list.js $(() => {let $deleteBtns = $('button.delete'); // 刪除按鈕menuId = 0; // 被點(diǎn)擊菜單idlet $currentMenu = null; // 當(dāng)前被點(diǎn)擊菜單對(duì)象, 也就是對(duì)應(yīng)的tr標(biāo)簽(表格中的一行)$deleteBtns.click(function () {let $this = $(this);$currentMenu = $this.parent().parent();menuId = $this.parent().data('id'); // 菜單idlet menuName = $this.parent().data('name'); // 菜單名// 改變模態(tài)框的顯示內(nèi)容$('#modal-delete .modal-body p').html('確定刪除菜單: " + menuName + " ?');// 顯示 模態(tài)框$('#modal-delete').modal('show');});$('#modal-delete button.delete-confirm').click(() => {deleteMenu()});// 刪除菜單的函數(shù)function deleteMenu() {$.ajax({url: '/admin/menu/' + menuId + '/',type: 'DELETE',dataType: 'json'}).done((res) => {if (res.errno === '0') { // errno為0代表成功// 關(guān)閉模態(tài)框$('#modal-delete').modal('hide');// 刪除菜單元素$currentMenu.remove();message.showSuccess('刪除成功!');} else { // 否則就表示出現(xiàn)了問(wèn)題message.showError(res.errmsg)}}).fail(() => {message.showError('服務(wù)器超時(shí)請(qǐng)重試!')})} });

    因?yàn)閐elete方法會(huì)改變數(shù)據(jù)庫(kù),所以需要csrftoken,在js/myadmin/menu.js中添加如下代碼

    function getCookie(name) {var cookieValue = null;if (document.cookie && document.cookie !== '') {var cookies = document.cookie.split(';');for (var i = 0; i < cookies.length; i++) {var cookie = cookies[i].trim();// Does this cookie string begin with the name we want?if (cookie.substring(0, name.length + 1) === (name + '=')) {cookieValue = decodeURIComponent(cookie.substring(name.length + 1));break;}}}return cookieValue;}function csrfSafeMethod(method) {// these HTTP methods do not require CSRF protectionreturn (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));}$.ajaxSetup({beforeSend: function (xhr, settings) {if (!csrfSafeMethod(settings.type) && !this.crossDomain) {xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));}}});

    記得前端要加上{% csrf_token %}, 關(guān)于cookie還有一點(diǎn)就是

    如果你先執(zhí)行添加菜單再執(zhí)行刪除的話, 即使不加{% csrf_token %}也會(huì)執(zhí)行刪除

    這是因?yàn)閳?zhí)行添加的話就會(huì)生成cookie, 已經(jīng)有了當(dāng)前頁(yè)面的cookie, 其他需要cookie的請(qǐng)求(post,put,delete)都可以執(zhí)行

    VI. 編輯菜單頁(yè)面

    1>接口設(shè)計(jì)

  • 接口說(shuō)明:
  • 類目說(shuō)明
    請(qǐng)求方法GET
    url定義/admin/menu/<int:menu_id>/
    參數(shù)格式路徑參數(shù)
  • 參數(shù)說(shuō)明
  • 參數(shù)名類型是否必須描述
    menu_id整數(shù)菜單id
  • 返回?cái)?shù)據(jù)

    html

  • 2>后端代碼

    # 在admin/views.py中的MenuUpdateView視圖中添加一個(gè)get方法 class MenuUpdateView(View):"""菜單管理視圖url:/admin/menu/<int:menu_id>/"""def get(self, request, menu_id):# 找到需要編輯的菜單menu = models.Menu.objects.filter(id=menu_id).first()# 使用之前定義的表單form = MenuModelForm(instance=menu)return render(request, 'myadmin/menu/update_menu.html', context={'form': form})

    3>前端代碼

    3.1>html

    <!-- 修改 templates/admin/menu/menu_list.html 在content中,添加修改模態(tài)框 --> {% block content %}...<!-- update modle --><div class="modal fade" id="modal-update" role="dialog" aria-labelledby="myLargeModalLabel"><div class="modal-dialog"><div class="modal-content"></div><!-- /.modal-content --></div><!-- /.modal-dialog --></div><!-- /.modal --> {% endblock %}

    編輯的模態(tài)框其實(shí)和添加模態(tài)框很相似, 是可以進(jìn)行代碼復(fù)用的, 這個(gè)我們后面再說(shuō), 先完成功能

    <!-- 新建 templates/admin/menu/update_menu.html -->{% load admin_customer_tags %}{% load static %}<div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button><h4 class="modal-title">修改菜單</h4></div><div class="modal-body"><form class="form-horizontal" id="update-menu">{% csrf_token %}<div class="box-body">{% for field in form %}{% if field.name == 'is_visible' %}<div class="form-group"><div class="col-sm-offset-2 col-sm-10"><div class="checkbox"><label for="{{ field.id_for_label }}">{{ field }}{{ field.label }}</label></div></div></div>{% else %}<div class="form-group {% if field.errors %}has-error{% endif %}"><label for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{ field.label }}</label><div class="col-sm-10">{% 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 %}</div></form></div><div class="modal-footer"><button type="button" class="btn btn-default pull-left" data-dismiss="modal">取消</button><button type="button" class="btn btn-primary update">修改</button></div>

    3.2>js

    // 修改 static/js/admin/menu/menu_list.js $(() => {let $editBtns = $('button.edit'); // 編輯按鈕let $deleteBtns = $('button.delete'); // 刪除按鈕menuId = 0; // 被點(diǎn)擊菜單id,要設(shè)置為全局變量, 不加let就行let $currentMenu = null; // 當(dāng)前被點(diǎn)擊菜單對(duì)象$deleteBtns.click(function () {...});$('#modal-delete button.delete-confirm').click(() => {...});// 刪除菜單的函數(shù)function deleteMenu() {...}// 編輯菜單$editBtns.click(function () {let $this = $(this);$currentMenu = $this.parent().parent();menuId = $this.parent().data('id');$.ajax({url: '/admin/menu/' + menuId + '/',type: 'GET'}).done((res)=>{ // res是ajax請(qǐng)求回來(lái)的數(shù)據(jù)// 改變模態(tài)框的html$('#modal-update .modal-content').html(res);// 顯示模態(tài)框$('#modal-update').modal('show')}).fail(()=>{message.showError('服務(wù)器超時(shí),請(qǐng)重試!')})}) });

    VII. 編輯菜單

    完成了頁(yè)面, 接下來(lái)就是點(diǎn)擊修改, 再發(fā)送一個(gè)ajax回去, 我們需要再寫一個(gè)后臺(tái)接收編輯數(shù)據(jù)

    1>業(yè)務(wù)流程分析

    • 接收表單參數(shù)
    • 校驗(yàn)表單參數(shù)
    • 校驗(yàn)成功, 保存菜單,判斷改動(dòng)字段是否影響了權(quán)限,如果有影響,修改權(quán)限,返回json信息
    • 校驗(yàn)失敗,返回包含錯(cuò)誤信息的html

    2>接口設(shè)計(jì)

  • 接口說(shuō)明:
  • 類目說(shuō)明
    請(qǐng)求方法PUT
    url定義/admin/menu/<int:menu_id>
    參數(shù)格式路徑參數(shù)+表單參數(shù)
  • 參數(shù)說(shuō)明:
  • 參數(shù)名類型是否必須描述
    menu_id整數(shù)菜單id
    name字符串菜單名
    url字符串當(dāng)前文章頁(yè)數(shù)
    order整數(shù)排序
    parent整數(shù)父菜單id
    icon字符串渲染圖標(biāo)類名
    codename字符串權(quán)限碼
    is_visible整數(shù)是否可見(jiàn)
  • 返回?cái)?shù)據(jù)

    # 添加正常返回json數(shù)據(jù) { "errno": "0", "errmsg": "菜單修改成功!" }

    如果有錯(cuò)誤,返回html表單

  • 3>后端代碼

    3.1>視圖

    # 在admin/views.py中的MenuUpdateView視圖中添加一個(gè)put方法 class MenuUpdateView(View):"""菜單管理視圖url:/admin/menu/<int:menu_id>/""" ...def put(self, request, menu_id):# 獲取到需要修改的菜單menu = models.Menu.objects.filter(id=menu_id).first()if not menu:return json_response(errno=Code.NODATA, errmsg='菜單不存在!')# 獲取put請(qǐng)求的數(shù)據(jù), request.body就是前端表單中所有的輸入put_data = QueryDict(request.body)# QueryDict這個(gè)方法會(huì)將body轉(zhuǎn)換成一個(gè)類字典對(duì)象form = MenuModelForm(put_data, instance=menu)if form.is_valid():obj = form.save() # 拿到一個(gè)新的menu對(duì)象,命名為obj# 檢查修改了的字段是否和權(quán)限有關(guān)flag = Falseif 'name' in form.changed_data:# changed_data會(huì)返回一個(gè)列表,包含當(dāng)前修改了的字段# 將已有的name更新為最新的obj.permission.name = obj.nameflag = Trueif 'codename' in form.changed_data:obj.permission.codename = obj.nameflag = Trueif flag:obj.permission.save()return json_response(errmsg='菜單修改成功!')else:return render(request, 'admin/menu/update_menu.html', context={'form': form}) ...

    4>前端代碼

    4.1>html

    <!-- 在 templates/myadmin/menu/update_menu.html 中引入update_menu.js --> ... <script src="{% static 'js/myadmin/menu/update_menu.js' %}"></script>

    4.2>js

    // 這里的代碼跟添加的js寫法很相似,又是一個(gè)代碼復(fù)用點(diǎn) $(()=>{let $updateBtn = $('#modal-update button.update');let $form = $('#update-menu');$updateBtn.click(function () {$.ajax({url: '/admin/menu/' + menuId + '/',type: 'PUT',data: $form.serialize(),// dataType: "json"}).done((res) => {if (res.errno === '0') {// 關(guān)閉模態(tài)框$('#modal-update').modal('hide').on('hidden.bs.modal', function (e) {$('#content').load($('.sidebar-menu li.active a').data('url'),(response, status, xhr) => {if (status !== 'success') {message.showError('服務(wù)器超時(shí),請(qǐng)重試!')}});});message.showSuccess(res.errmsg);} else {message.showError('修改菜單失敗!');// 修改模態(tài)框的內(nèi)容為, 后端返回的帶有錯(cuò)誤信息的表單$('#modal-update .modal-content').html(res)}}).fail(() => {message.showError('服務(wù)器超時(shí),請(qǐng)重試');});}); });

    VIII. 整合后臺(tái)首頁(yè)面菜單加載

    我們來(lái)填之前菜單列表的坑, 之前我們使用list寫的硬編碼, 我們來(lái)到數(shù)據(jù)庫(kù)中拿可用的菜單

    1>后端代碼

    1.1>視圖

    class IndexView(LoginRequiredMixin, View):"""后臺(tái)首頁(yè)視圖"""def get(self, request):objs = models.Menu.objects.only('name', 'url', 'icon', 'permission__codename','permission__content_type__app_label').select_related('permission__content_type').filter(is_delete=False, is_visible=True, parent=None)has_permissions = request.user.get_all_permissions()menus = []for menu in objs:if '%s.%s' % (menu.permission.content_type.app_label, menu.permission.codename) in has_permissions:temp = {'name': menu.name,'icon': menu.icon}children = menu.children.filter(is_delete=False, is_visible=True)if children:temp['children'] = []for child in children:if '%s.%s' % (child.permission.content_type.app_label, child.permission.codename) in has_permissions:temp['children'].append({'name': child.name,'url': child.url})else:if not menu.url:continuetemp['url'] = menu.urlmenus.append(temp)print(menus)return render(request, 'admin/index.html', context={'menus': menus})

    再運(yùn)行之前, 一定要先添加一個(gè)菜單管理, 這樣才可以看到我們的菜單列表效果

    但是有一個(gè)bug, 當(dāng)我們添加了一個(gè)新菜單之后, 雖然可以展示出來(lái), 但是點(diǎn)擊訪問(wèn)時(shí)就會(huì)提示我們沒(méi)有定義路由,

    這個(gè)可以設(shè)計(jì)一個(gè)自定義標(biāo)簽, 使用異常處理的方式, 若使用的使錯(cuò)誤的路由, 就展示wait界面, 代碼如下:

    # 在myadmin/templatetags/admin_customer_tags.py中添加如下過(guò)濾器 @register.simple_tag() def my_url(pattern, *args):try:url = reverse(pattern, *args)except Exception as e:url = reverse('myadmin:wait')return url

    然后在前端代碼中引用:

    <!-- 在templates/myadmin/index.html中修改如下代碼 --><!-- Sidebar Menu --><ul class="sidebar-menu" data-widget="tree">{% for menu in menus %}{% if 'children' in menu %}<li class="treeview"><a href="#"><i class="fa {{ menu.icon }}"></i> <span>{{ menu.name }}</span><span class="pull-right-container"><i class="fa fa-angle-left pull-right"></i></span></a><ul class="treeview-menu">{% for child in menu.children %}<li><a href="#" data-url="{% my_url child.url %}"><!-- 使用my_url過(guò)濾器 -->{{ child.name }}</a></li>{% endfor %}</ul></li>{% else %}<li><a href="#" data-url="{% my_url menu.url %}"><!-- 使用my_url過(guò)濾器 --><i class="fa {{ menu.icon }}"></i><span>{{ menu.name }}</span></a></li>{% endif %}{% endfor %}</ul>

    總結(jié)

    以上是生活随笔為你收集整理的[django项目] 后台菜单管理功能的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。