項目一:CRM(客戶關系管理系統)--8
添加、修改頁面都已經搞定,就差刪除功能啦!刪除這裏就比較麻煩了,麻煩在那些表之間的關系。
1. 添加刪除功能
1.1 刪除頁面路由
為用戶的良好體驗,我們新增加一個刪除顯示頁面,路由:
1 urlpatterns = [ 2 url(r‘^$‘, views.index, name=‘table_index‘), 3 url(r‘^(\w+)/(\w+)/$‘, views.display_objects, name=‘display_objects‘), 4 url(r‘^(\w+)/(\w+)/(\d+)/edit/$‘, views.table_object_edit,name="table_object_edit"), 5 url(r‘^(\w+)/(\w+)/add/$‘, views.table_object_add,name="table_object_add"), 6 url(r‘^(\w+)/(\w+)/(\d+)/delete/$‘, views.table_object_delete,name="table_object_delete"),#刪除路由 7 ]
1.2 添加刪除頁面模板
在templates/king_admin
目錄下新建table_object_delete.html
文件,內容如下:
1 {% extends ‘king_admin/table_index.html‘ %} 2 3 {% block body-content %} 4 5 {# 表單提交 #} 6 <form method="post"> 7 {% csrf_token %} 8 <input type="submit" class="btn btn-danger" value="Yes,I‘m sure"> 9 <input type="hidden" value="yes" name="delete_confirm"> 10 <a class="btn btn-info" href="{% url ‘king_admin:table_object_edit‘ app_name table_name object_id %}">No,Take me back</a> 11 </form> 12 {% endblock %}
1.3 視圖函數
1 def table_object_delete(request, app_name, table_name, object_id): 2 admin_class = site.enabled_admins[app_name][table_name] 3 4 object_list = admin_class.model.objects.filter(id=object_id) 5 if request.method == ‘POST‘: 6 if request.POST.get(‘delete_confirm‘) == ‘yes‘: 7 object_list.delete() 8 return redirect(‘/king_admin/{app_name}/{table_name}‘.format(app_name=app_name, table_name=table_name)) 9 10 11 return render(request, ‘king_admin/table_object_delete.html‘, {‘app_name‘: app_name, 12 ‘table_name‘: table_name, 13 ‘object_id‘: object_id, 14 ‘object_list‘: object_list})
1.4 為刪除頁面添加入口
在table_object_edit.html
文件中,只需要添加一個按鈕,但是這個按鈕很是關鍵 ,要經過一些特別的處理,避免一些不必要的問題發生。
1 ... 2 3 {% endfor %} 4 {# 添加保存按鈕 #} 5 <div class="form-group"> 6 {# 添加刪除功能,並獨立出來,以免影響其他功能 #} 7 {% block obj_delete %} 8 <div class="col-sm-2"> 9 <a class="btn btn-danger" href="{% url ‘king_admin:table_object_delete‘ app_name table_name object_id %}">刪除</a> 10 </div> 11 {% endblock %} 12 <div class="col-sm-10 "> 13 <button type="submit" class="btn btn-success pull-right">保存</button> 14 </div> 15 </div> 16 </form> 17 18 ...
還記得,我們的添加功能是繼承自編輯頁面吧,那裏同樣也要加上繼承標簽,以免二者出現一些關聯錯誤。
在table_object_add.html
頁面添加一個塊標簽即可,且不加任何內容:
1 ... 2 3 {% block obj_delete %} 4 5 {% endblock %}
渲染後的效果:
刪除後跳轉到顯示頁面的功能在視圖函數中已經寫好,不在贅述,和之前的編輯頁面添加跳轉是類似的。
2. 添加映射關系顯示
上面我們完成了刪除的基本功能,但是在Django
的admin
中還額外的顯示了表與表之間的關系,這裏我們同樣來實現這個功能。在實現是之前,我們需要先搞清楚一些關系。
在這裏我將關聯關系分為三大類:主動關聯、被動關聯與特殊關聯。
-
主動關聯和被動關聯都包含特殊關聯。
-
主動關聯
在當前表中有ForeignKey
或者OneToOne
關鍵字段,主動去關聯其他表的方式為主動關聯 -
被動關聯
當前表中有ForeignKey
或者OneToOne
或者沒有其中的任何一個,但是其他表通過ForeignKey
或者OneToOne
關聯到該表的方式為被動關聯。 -
特殊關聯
當前表中有ManyToMany
關聯其他表格,或其他表中有ManyToMany
關聯到該表,或有第三張額外的表中包括兩個ForeignKey
,將兩個表關聯起來的方式為特殊關聯。
這裏只是本人自己區分的一種方式!
這裏的刪除功能需要的是被動關聯和特殊關聯的關系顯示。
2.1 shell中查詢關系
這裏以Customer
表和UserProfile
表為例:
1 >>> python manage.py shell 2 3 >>> from CRM import models 4 >>> a = models.Customer 5 >>> a 6 <class ‘CRM.models.Customer‘> 7 >>> a._meta 8 <Options for Customer> 9 >>> a._meta.get_fields(include_hidden=True) 10 (<ManyToOneRel: CRM.customer_tags>, <ManyToOneRel: CRM.customerfollowup>, <ManyToOneRel: CRM.enrollment>, <ManyToOneRel: CRM.payment>, <django.db.models.fields.AutoField: id>, <djan 11 go.db.models.fields.CharField: name>, <django.db.models.fields.CharField: qq>, <django.db.models.fields.CharField: qq_name>, <django.db.models.fields.CharField: phone>, <django.db.m 12 odels.fields.SmallIntegerField: source>, <django.db.models.fields.CharField: referral_from>, <django.db.models.fields.related.ForeignKey: consult_course>, <django.db.models.fields.T 13 extField: content>, <django.db.models.fields.SmallIntegerField: status>, <django.db.models.fields.related.ForeignKey: consultant>, <django.db.models.fields.TextField: memo>, <django 14 .db.models.fields.DateTimeField: date>, <django.db.models.fields.related.ManyToManyField: tags>)
上面我們能看到所有的字段信息,我們想看的信息如下:
1 #被動關聯 2 <ManyToOneRel: CRM.customer_tags> 3 <ManyToOneRel: CRM.customerfollowup> 4 <ManyToOneRel: CRM.enrollment> 5 <ManyToOneRel: CRM.payment> 6 #主動關聯 7 <django.db.models.fields.related.ForeignKey: consult_course> 8 <django.db.models.fields.related.ForeignKey: consultant> 9 <django.db.models.fields.related.ManyToManyField: tags> 10 #特殊關聯 11 <django.db.models.fields.related.ManyToManyField: tags>
我們需要找到的就是被動關聯與特殊關聯,其中特殊關聯的另外一種形式,我們可以看看UserProfile
表:
1 >>> c = models.UserProfile 2 >>> c._meta.get_fields(include_hidden=True) 3 (<ManyToOneRel: CRM.customer>, <ManyToOneRel: CRM.customerfollowup>, <ManyToOneRel: CRM.classlist_teachers>, <ManyToManyRel: CRM.classlist>, <ManyToOneRel: CRM.courserecord>, <ManyT 4 oOneRel: CRM.enrollment>, <ManyToOneRel: CRM.payment>, <ManyToOneRel: CRM.userprofile_roles>, <django.db.models.fields.AutoField: id>, <django.db.models.fields.related.OneToOneField 5 : user>, <django.db.models.fields.CharField: name>, <django.db.models.fields.related.ManyToManyField: roles>)
提取需要的信息如下:
1 #被動關聯 2 <ManyToOneRel: CRM.customer> 3 <ManyToOneRel: CRM.customerfollowup> 4 <ManyToOneRel: CRM.classlist_teachers> 5 <ManyToManyRel: CRM.classlist> 6 <ManyToOneRel: CRM.courserecord> 7 <ManyToOneRel: CRM.enrollment> 8 <ManyToOneRel: CRM.payment> 9 <ManyToOneRel: CRM.userprofile_roles> 10 #主動關聯 11 <django.db.models.fields.related.OneToOneField: user> 12 <django.db.models.fields.related.ManyToManyField: roles> 13 #特殊關聯 14 <ManyToManyRel: CRM.classlist>
上面兩個表,為我們展示了幾種關聯的基本樣式。接下來工作就是如何找到具體的關聯樣式。
下面演示的操作以Customer
表和UserProfile
表為例。
由於被動關系包括特殊關系,先來查詢被動關系:
1.查詢被動關系
1 Customer表 2 3 >>> models.Customer._meta.related_objects 4 (<ManyToOneRel: CRM.customerfollowup>, <ManyToOneRel: CRM.enrollment>, <ManyToOneRel: CRM.payment>) 5 UserProfile表 6 7 >>> models.UserProfile._meta.related_objects 8 (<ManyToOneRel: CRM.customer>, <ManyToOneRel: CRM.customerfollowup>, <ManyToManyRel: CRM.classlist>, <ManyToOneRel: CRM.courserecord>, <ManyToOneRel: CRM.enrollment>, <ManyToOneRel: CRM.payment>)
特殊關系是被動和主動關系都有的,這裏所查詢的應該是主動關系裏面的特殊關系:
2. 查詢特殊關系
1 Customer表 2 3 >>> models.Customer._meta.many_to_many 4 (<django.db.models.fields.related.ManyToManyField: tags>,) #元組形式 5 >>> models.Customer._meta.local_many_to_many 6 [<django.db.models.fields.related.ManyToManyField: tags>] #列表形式 7 UserProfile表 8 9 >>> models.UserProfile._meta.local_many_to_many 10 [<django.db.models.fields.related.ManyToManyField: roles>] #列表 11 >>> models.UserProfile._meta.many_to_many 12 (<django.db.models.fields.related.ManyToManyField: roles>,) #元組 13 2.2 關聯數據獲取
2.2 關聯數據獲取
上面找到了具體的關聯,該怎麽獲取到相關聯的數據呢?
答:請看下面。
通常情況下,我們跨表查詢是使用關聯的表名_set
進行反向查詢,跳轉到關聯表中,然後在獲取數據。在這裏也是同樣的道理,演示還是在shell
中操作。
以Customer
表為例:
1 >>> models.Customer._meta.related_objects 2 (<ManyToOneRel: CRM.customerfollowup>, <ManyToOneRel: CRM.enrollment>, <ManyToOneRel: CRM.payment>) 3 >>> a= models.Customer._meta.related_objects 4 >>> for i in a:print(i,‘----‘,i.get_accessor_name()) 5 ... 6 <ManyToOneRel: CRM.customerfollowup> ---- customerfollowup_set 7 <ManyToOneRel: CRM.enrollment> ---- enrollment_set 8 <ManyToOneRel: CRM.payment> ---- payment_set
這裏的關聯的表名_set
就是我們所需要的。
由於在Customer
表中沒有被動關聯中的特殊關聯,所以我們以Tag
表來反查到Customer
表中數據:
(你的數據庫中一定要有數據)
1 >>> a = models.Tag.objects.filter(id=‘4‘) 2 >>> a 3 <QuerySet [<Tag: dsfgb>]> 4 5 #遍歷出QuerySet集合裏面的每個對象 6 >>> for obj in a: 7 #獲取到每個對象對應的被動關聯 8 ... for i in obj._meta.related_objects: 9 # 通過反查方式獲取到關聯數據的對象集合 10 ... b = getattr(obj,i.get_accessor_name()).select_related() 11 #遍歷對象集合 12 ... for data in b: 13 ... print(data) 14 ... 15 4567467
好了,我們的目標實現了!下面的工作就交給神器:自定義標簽。
2.3 創建標簽函數
在templatetags/tags.py
下創建功能函數:
1 ... 2 3 <-------------------獲取刪除映射關系-------------------------------- 4 5 @register.simple_tag 6 def display_object_related(object_list): 7 return mark_safe(recursive_related_objs_lookup(object_list)) 8 9 10 <-----------------遞歸獲取映射關系-------------------------------- 11 12 def recursive_related_objs_lookup(object_list): 13 #標簽的拼接 14 #最外層ul 15 print(object_list) 16 ul_ele = "<ul style=‘color: pink‘>" 17 for obj in object_list: 18 print(obj._meta.verbose_name, obj.__str__().strip("<>")) 19 li_ele = ‘‘‘<li>{0}:{1}</li>‘‘‘.format(obj._meta.verbose_name, obj.__str__().strip("<>")) 20 ul_ele += li_ele 21 22 #映射關系處理 23 <---------------------------特殊關聯處理----------------------------------- 24 #多對多關系 25 for m2m_field in obj._meta.local_many_to_many: #local_many_to_many返回列表,many_to_many返回元祖 26 sub_ul_ele = "<ul style=‘color: red‘>" 27 m2m_field_obj = getattr(obj, m2m_field.name) 28 for m2m_data in m2m_field_obj.select_related(): 29 sub_li_ele = ‘‘‘<li>{0}:{1}</li>‘‘‘.format(m2m_field.verbose_name, m2m_data.__str__().strip("<>")) 30 sub_ul_ele += sub_li_ele 31 32 sub_ul_ele += ‘</ul>‘ 33 #與外層拼接 34 ul_ele += sub_ul_ele 35 36 <---------------------------被動關聯處理------------------------------------ 37 #被動關聯中的特殊關聯處理 38 for related_obj in obj._meta.related_objects: 39 #判斷是否存在特殊關聯,特殊關聯處理 40 if ‘ManyToManyRel‘ in related_obj.__repr__(): 41 #判斷對象中是否包含反查屬性 42 if hasattr(obj, related_obj.get_accessor_name()): 43 #獲取反查對應的對象 44 accessor_obj = getattr(obj, related_obj.get_accessor_name()) 45 #判斷有沒有獲取數據的方法或屬性 46 if hasattr(accessor_obj, ‘select_related‘): 47 target_object = accessor_obj.select_related() 48 #標簽拼接 49 sub_ul_ele = ‘<ul style="color: green">‘ 50 for data in target_object: 51 sub_li_ele = ‘‘‘<li>{0}:{1}</li>‘‘‘.format(data._meta.verbose_name, 52 data.__str__().strip("<>")) 53 sub_ul_ele += sub_li_ele 54 sub_ul_ele += ‘</ul>‘ 55 #與外層拼接 56 ul_ele += sub_ul_ele 57 #被動關聯處理 58 elif hasattr(obj, related_obj.get_accessor_name()): 59 accessor_obj = getattr(obj, related_obj.get_accessor_name()) 60 if hasattr(accessor_obj, ‘select_related‘): 61 target_object = accessor_obj.select_related() 62 <---------------由於使用遞歸,下面的標簽樣會發生重復,就不需要使用了-------------------- 63 # sub_ul_ele = ‘<ul style="color: grey">‘ 64 # for data in target_data: 65 # sub_li_ele = ‘‘‘<li>{0}:{1}</li>‘‘‘.format(data._meta.verbose_name, 66 # data.__str__().strip("<>")) 67 # sub_ul_ele += sub_li_ele 68 # sub_ul_ele += ‘</ul>‘ 69 # # 與外層拼接 70 # ul_ele += sub_ul_ele 71 else: 72 print(‘One-To-One:‘,accessor_obj) 73 target_object = accessor_obj 74 #判斷有無下級對象存在 75 if len(target_object) > 0: 76 node = recursive_related_objs_lookup(target_object) 77 ul_ele += node 78 79 ul_ele += ‘</ul>‘ 80 return ul_ele
2.4 模板調用標簽
在編輯好的table_object_delete.html
文件中添加:
1 {% extends ‘king_admin/table_index.html‘ %} 2 {% load tags %} 3 4 {% block body-content %} 5 6 {# 顯示映射關系 #} 7 {% display_object_related object_list %} 8 9 {# 表單提交 #} 10 <form method="post"> 11 {% csrf_token %} 12 <input type="submit" class="btn btn-danger" value="Yes,I‘m sure"> 13 <input type="hidden" value="yes" name="delete_confirm"> 14 <a class="btn btn-info" href="{% url ‘king_admin:table_object_edit‘ app_name table_name object_id %}">No,Take me back</a> 15 </form> 16 {% endblock %}
刪除效果如下:
其他的修飾內容,可自行添加,反正我是最後套模板。
項目一:CRM(客戶關系管理系統)--8