您现在的位置是: 网站首页 >Django >DjangoCRM客户关系管理 Django

【CRM客户关系管理】19.对象删除功能,显示删除的关联对象和确认

admin2018年12月23日 15:40 Django | JavaScript | JQuery | Python 1077人已围观

DjangoCRM客户关系管理简介 使用Django2.1.3+Bootstrap实现CRM系统,仿照Django Admin重写后台 Github地址:https://github.com/xyliurui/DjangoCRM Django版本:2.1.3

## 对象删除功能 ### 基础配置 #### 创建删除对象模板 在djadmin应用下templates/djadmin中新建table_delete.html ```html {% extends 'djadmin/base.html' %} {% load djadmin_tags %} {% block title %} 数据表删除 - 后台管理 {% endblock %} {% block content %} <h2 class="page-header">{{ model_name }}</h2> <div> 删除<a href="{% url 'djadmin:table_change' app_name model_name obj.id %}">{{ obj }}</a> </div> {% endblock %} ``` #### 创建删除对象视图 在djadmin应用的views.py视图文件中,增加删除数据视图 ```python # 数据删除 @login_required def table_delete(request, app_name, model_name, obj_id): admin_class = site.enable_admins[app_name][model_name] obj = admin_class.model.objects.get(id=obj_id) return render(request, 'djadmin/table_delete.html', locals()) ``` #### 创建删除对象url 修改djadmin中的urls.py,增加删除路由 ```python from django.urls import path from djadmin.views import index, user_login, user_logout, table_detail, table_change, table_add, table_delete app_name = 'djadmin' urlpatterns = [ path('login/', user_login, name='user_login'), # djAdmin登录 path('logout/', user_logout, name='user_logout'), # djAdmin登出 path('', index, name='index'), # djAdmin主页 path('<str:app_name>/<str:model_name>/', table_detail, name='table_detail'), # 数据表详情 path('<str:app_name>/<str:model_name>/<int:obj_id>/change/', table_change, name='table_change'), # 数据表修改 path('<str:app_name>/<str:model_name>/add/', table_add, name='table_add'), # 数据增加 path('<str:app_name>/<str:model_name>/<int:obj_id>/delete/', table_delete, name='table_delete'), # 数据表删除 ] ``` #### 配置列表页删除跳转模板 在table_detal.html中增加删除的链接`<td><a href="{% url 'djadmin:table_delete' app_name model_name obj.id %}">删除</a></td>` ```html <table class="table table-striped"> <thead> <tr> {% if admin_class.list_display %} {% for display_field in admin_class.list_display %} <th> <a href="?_order={% get_sorted_data display_field current_order_field forloop.counter0 %}{% render_filter_args admin_class %}"> {{ display_field }} {% get_sorted_arrow display_field current_order_field forloop.counter0 %} </a> </th> {% endfor %} {% else %} {% build_table_head_name admin_class %} {% endif %} <th>操作</th> <!-- {% build_table_head_name admin_class %} --> </tr> </thead> <tbody> {% for obj in queryset %} <tr> {% build_table_body obj admin_class %} <td><a href="{% url 'djadmin:table_delete' app_name model_name obj.id %}">删除</a></td> </tr> {% endfor %} </tbody> </table> ``` ![BLOG_20181223_154136_32](/media/blog/images/2018/12/BLOG_20181223_154136_32.png "博客图集BLOG_20181223_154136_32.png") 当点击这个删除会跳转到 http://127.0.0.1:8000/djadmin/crm/customerinfo/1/delete/ ![BLOG_20181223_154201_46](/media/blog/images/2018/12/BLOG_20181223_154201_46.png "博客图集BLOG_20181223_154201_46.png") #### 配置编辑页删除跳转模板 修改table_edit.html模板,增加删除按钮。但要求当为数据增加时,不能显示删除按钮 ```html <div class="form-group"> <div class="col-sm-offset-2 col-sm-4"> <button type="submit" class="btn btn-primary">提交</button> </div> {% url 'djadmin:table_add' app_name model_name as table_add %} {% if request.path != table_add %} <div class="col-sm-offset-2 col-sm-4"> <a class="btn btn-danger" href="{% url 'djadmin:table_delete' app_name model_name obj_id %}">删除</a> </div> {% endif %} </div> ``` ![BLOG_20181223_154216_71](/media/blog/images/2018/12/BLOG_20181223_154216_71.png "博客图集BLOG_20181223_154216_71.png") ### 显示要被删除的对象 #### 获取反向关联方法 ```python from crm.models import CustomerInfo obj = CustomerInfo.objects.first() obj._meta # <Options for CustomerInfo> obj._meta.related_objects # (<ManyToOneRel: crm.customerinfo>, <ManyToOneRel: crm.customerfollowup>, <OneToOneRel: crm.student>) obj._meta.related_objects[1] # <ManyToOneRel: crm.customerfollowup> # 反向获取表明 obj._meta.related_objects[1].name # 'customerfollowup' obj._meta.related_objects[0].name # 'customerinfo' obj._meta.related_objects[2].name # 'student' # 获取类型 obj._meta.related_objects[2].get_internal_type() # 'OneToOneField' obj._meta.related_objects[1].get_internal_type() # 'ForeignKey' obj._meta.related_objects[0].get_internal_type() # 'ForeignKey' Student.objects.first() <Student: 测试> # fs = Student.objects.first() fs # <Student: 测试(None)> fs.customer # <CustomerInfo: 测试> fsc = fs.customer obj # <CustomerInfo: 测试> obj == fsc # True ``` #### 创建模板标签显示关联对象 在djadmin应用的templatetags包djadmin_tags.py增加一个模板标签,用于显示要被删除的对象所有关联对象 测试 ```python AttributeError: 'CustomerInfo' object has no attribute 'student' # 增加关联名 customer = models.OneToOneField(CustomerInfo, verbose_name='客户', on_delete=models.CASCADE, related_name='student') 'CustomerInfo' object has no attribute 'customerinfo' # 增加关联名 referral_from = models.ForeignKey('self', blank=True, null=True, on_delete=models.SET_NULL, verbose_name='转介绍客户', related_name='customerinfo') 'CustomerInfo' object has no attribute 'customerfollowup' # 增加关联名 customer = models.ForeignKey(CustomerInfo, on_delete=models.CASCADE, verbose_name='客户', related_name='customerfollowup') ``` **然后把所有外键都添加related_name属性为该模型名称小写** 修改后的模型所有如下: ```python from django.db import models from django.contrib.auth.models import User class Menu(models.Model): """动态菜单""" Url_Type_Choices = ( (0, '绝对URL'), (1, '动态URL') ) name = models.CharField(max_length=100, verbose_name='菜单名称') url_type = models.SmallIntegerField(choices=Url_Type_Choices, default=0, verbose_name='菜单类型') url = models.CharField(max_length=200, verbose_name='URL地址') class Meta: unique_together = ('name', 'url') verbose_name_plural = verbose_name = '动态菜单' def __str__(self): return self.name class Role(models.Model): """角色表""" name = models.CharField(max_length=50, unique=True, verbose_name='角色名称') menus = models.ManyToManyField(Menu, blank=True, verbose_name='动态菜单', related_name='role') # 一个角色可以访问多个菜单,一个菜单可以被多个角色访问 class Meta: verbose_name_plural = verbose_name = '角色' def __str__(self): return self.name class UserProfile(models.Model): """用户信息表""" user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='userprofile', verbose_name='关联系统User') # 扩展user模型 name = models.CharField(max_length=50, verbose_name='姓名') role = models.ManyToManyField(Role, related_name='userprofile', verbose_name='角色列表') class Meta: verbose_name_plural = verbose_name = '用户' def __str__(self): return self.name class Branch(models.Model): """校区分支""" name = models.CharField(max_length=50, unique=True, verbose_name='校区名') address = models.CharField(max_length=200, blank=True, null=True, verbose_name='地址') def __str__(self): return self.name class Meta: verbose_name_plural = verbose_name = '校区' class Course(models.Model): """课程表""" name = models.CharField(max_length=50, unique=True, verbose_name='课程名称') price = models.PositiveSmallIntegerField(verbose_name='价格') # 整数 period = models.PositiveSmallIntegerField(verbose_name='课程周期(月)', default=5) outline = models.TextField(verbose_name='大纲') def __str__(self): return self.name class Meta: verbose_name_plural = verbose_name = '课程' class ClassInfo(models.Model): """班级信息""" Class_Type_Choices = ( (1, '工作日'), (2, '周末'), (3, '网络班') ) branch = models.ForeignKey(Branch, on_delete=models.CASCADE, verbose_name='所属校区', related_name='classinfo') course = models.ForeignKey(Course, on_delete=models.CASCADE, verbose_name='课程', related_name='classinfo') class_type = models.SmallIntegerField(choices=Class_Type_Choices, verbose_name='班级类型') semester = models.SmallIntegerField(verbose_name='学期') teachers = models.ManyToManyField(UserProfile, verbose_name='讲师', related_name='classinfo') start_date = models.DateField(verbose_name='开班日期') graduate_date = models.DateField(blank=True, null=True, verbose_name='毕业日期') # 结束日期不固定,可为空 class Meta: verbose_name_plural = verbose_name = '班级信息' unique_together = ('branch', 'course', 'class_type', 'semester') # 联合唯一,班级不能重复 def __str__(self): return '{}({})期'.format(self.course.name, self.semester) class CourseRecord(models.Model): """上课记录""" class_grade = models.ForeignKey(ClassInfo, on_delete=models.CASCADE, verbose_name='班级', related_name='courserecord') day_num = models.PositiveSmallIntegerField(verbose_name='课程节次') teacher = models.ForeignKey(UserProfile, on_delete=models.CASCADE, verbose_name='讲师', related_name='courserecord') title = models.CharField(max_length=200, verbose_name='本节主题') content = models.TextField(verbose_name='本节内容') has_homework = models.BooleanField(default=False, verbose_name='本节是否有作业') homework = models.TextField(blank=True, null=True, verbose_name='作业内容') created_time = models.DateField(auto_now_add=True, verbose_name='创建时间') def __str__(self): return '{}第({})节'.format(self.class_grade, self.day_num) class Meta: verbose_name_plural = verbose_name = '上课记录' unique_together = ('class_grade', 'day_num') class CustomerInfo(models.Model): """客户信息表""" Contact_Type_Choices = ( (1, 'qq'), (2, '微信'), (3, '手机'), (4, '其他') ) Source_Choice = ( (1, 'qq群'), (2, '微信'), (3, '转介绍'), (4, '其它'), ) Status_Choice = ( (1, '未报名'), (2, '已报名'), (3, '结业') ) name = models.CharField(max_length=50, verbose_name='客户姓名') contact_type = models.SmallIntegerField(choices=Contact_Type_Choices, default=1, verbose_name='联系媒介') contact = models.CharField(max_length=50, unique=True, verbose_name='联系方式') source = models.SmallIntegerField(choices=Source_Choice, verbose_name='客户来源') # 如果是转介绍,介绍人是学员,介绍别人来学习,需要关联到学员本人,如果不是,可为空 referral_from = models.ForeignKey('self', blank=True, null=True, on_delete=models.SET_NULL, verbose_name='转介绍客户', related_name='customerinfo') consult_courses = models.ManyToManyField(Course, verbose_name='咨询课程', related_name='customerinfo') # 多对多关联课程 consult_content = models.TextField(verbose_name='咨询内容') status = models.SmallIntegerField(choices=Status_Choice, verbose_name='客户状态') consultant = models.ForeignKey(UserProfile, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='课程顾问', related_name='customerinfo') created_time = models.DateField(auto_now_add=True, verbose_name='创建时间') def __str__(self): return self.name class Meta: verbose_name_plural = verbose_name = '客户信息' class CustomerFollowUp(models.Model): """客户跟进记录""" Status_Choices = ( (0, '近期无报名计划'), (1, '一个月内报名'), (2, '半个月报名'), (3, '已报名') ) customer = models.ForeignKey(CustomerInfo, on_delete=models.CASCADE, verbose_name='客户', related_name='customerfollowup') content = models.TextField(verbose_name='跟进内容') user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, verbose_name='跟进人', related_name='customerfollowup') status = models.SmallIntegerField(choices=Status_Choices, verbose_name='客户状态') created_time = models.DateField(auto_now_add=True, verbose_name='创建时间') def __str__(self): return "{}跟进{}状态:{}".format(self.user.name, self.customer.name, self.get_status_display()) class Meta: verbose_name_plural = verbose_name = '跟进记录' class Student(models.Model): customer = models.OneToOneField(CustomerInfo, verbose_name='客户', on_delete=models.CASCADE, related_name='student') class_grades = models.ManyToManyField(ClassInfo, verbose_name='班级', related_name='student') class Meta: verbose_name_plural = verbose_name = '学员' def __str__(self): return '{}'.format(self.customer) class StudyRecord(models.Model): """学习记录""" Score_Choices = ( (100, 'A+'), (90, 'A'), (85, 'B+'), (80, 'B'), (75, 'B-'), (70, 'C+'), (60, 'C'), (40, 'C-'), (-50, 'D'), (0, 'N/A'), # not avaliable (-100, 'COPY'), # 抄作业 ) Show_Choices = ( (0, '缺勤'), (1, '已签到'), (2, '迟到'), (3, '早退'), ) course_record = models.ForeignKey(CourseRecord, on_delete=models.CASCADE, verbose_name='课程', related_name='studyrecord') student = models.ForeignKey(Student, verbose_name='学生', on_delete=models.CASCADE, related_name='studyrecord') score = models.SmallIntegerField(choices=Score_Choices, default=0, verbose_name='得分') show_status = models.SmallIntegerField(choices=Show_Choices, default=1, verbose_name='出勤') note = models.TextField(blank=True, null=True, verbose_name='成绩备注') created_time = models.DateField(auto_now_add=True, verbose_name='创建时间') class Meta: verbose_name_plural = verbose_name = '学习记录' def __str__(self): return '{} {} {}'.format(self.course_record, self.student, self.score) ``` 然后再djadmin_tags.py中添加获取关联对象的模板标签 ```python @register.simple_tag def display_all_related_objs(obj): """获取要被删除的对象的所有关联对象""" ele = "<ul><b style='color:red'>%s</b>" % obj # 获取所有反向关联的对象 for reversed_fk_obj in obj._meta.related_objects: # 获取关联对象的表名 related_table_name = reversed_fk_obj.name print('\n\n', related_table_name) ele += "<li>{}<ul>".format(related_table_name) # 通过表明反向查所有关联的数据 related_lookup_key = '{}'.format(related_table_name) # 原文用的{}_set,也许Django版本不同,在我这就用不了,直接用的related_name表名 from django.urls import reverse if reversed_fk_obj.get_internal_type() == 'OneToOneField': try: related_objs = getattr(obj, related_lookup_key) print('一对一', related_objs) ele += "<li><a href='{}'>{}</a> 记录里与[{}]相关的的数据将被删除</li>".format( reverse('djadmin:table_change', kwargs={'app_name': related_objs._meta.app_label, 'model_name': related_objs._meta.model_name, 'obj_id': related_objs.id}), related_objs, obj ) except: # 这儿做一个异常捕获:crm.models.CustomerInfo.student.RelatedObjectDoesNotExist: CustomerInfo has no student. # 当客户还未变成学员时,他并没有student的属性 pass elif reversed_fk_obj.get_internal_type() == "ManyToManyField": # 不需要深入查找 related_objs = getattr(obj, related_lookup_key).all() for item in related_objs: ele += "<li><a href='{}'>{}</a> 记录里与[{}]相关的的数据将被删除</li>".format( reverse('djadmin:table_change', kwargs={'app_name': item._meta.app_label, 'model_name': item._meta.model_name, 'obj_id': item.id}), item, obj ) elif reversed_fk_obj.get_internal_type() == 'ForeignKey': # 如果不是m2m,就递归查找所有关联的数据 related_objs = getattr(obj, related_lookup_key).all() print('一对多', related_objs) for item in related_objs: ele += "<li><a href='{}'>{}</a></li>".format( reverse('djadmin:table_change', kwargs={'app_name': item._meta.app_label, 'model_name': item._meta.model_name, 'obj_id': item.id}), item, ) # 递归查找 ele += display_all_related_objs(item) ele += "</ul></li>" ele += '</ul>' return ele ``` #### 修改删除模板页面显示table_delete.html 处理从后端返回的html代码需要加上`|safe`。 ```html {% extends 'djadmin/base.html' %} {% load djadmin_tags %} {% block title %} 数据表删除 - 后台管理 {% endblock %} {% block content %} <h2 class="page-header">{{ model_name }}</h2> <h3 class="page-header alert alert-danger">你确定要删除吗?</h3> <div> 删除<a href="{% url 'djadmin:table_change' app_name model_name obj.id %}">【{{ obj }}】</a> </div> {% display_all_related_objs obj as all_related_obj_eles %} {{ all_related_obj_eles|safe }} {# 需要加上|safe,否则会直接显示源代码 #} {% endblock %} ``` 现在需要向数据库中添加一些测试数据,另外在crm应用的djadmin.py文件中注册一些app,例如 ```python from djadmin.sites import site from crm import models from djadmin.djadmin_base import BaseDjAdmin print('crm models...') # 注册model class CustomerInfoAdmin(BaseDjAdmin): # 不使用object,直接继承BaseDjAdmin list_display = ['name', 'contact_type', 'contact', 'consultant', 'consult_content', 'status', 'created_time'] list_filter = ['source', 'consultant', 'status', 'created_time'] search_fields = ['contact', 'consultant__name', 'consult_content'] readonly_fields = ['contact', 'status'] filter_horizontal = ['consult_courses'] site.register(models.CustomerInfo, CustomerInfoAdmin) site.register(models.Role) site.register(models.Menu) site.register(models.UserProfile) site.register(models.Course) site.register(models.Student) site.register(models.ClassInfo) site.register(models.Branch) site.register(models.CourseRecord) site.register(models.CustomerFollowUp) site.register(models.StudyRecord) ``` 现在选择删除一个课程,例如 http://127.0.0.1:8000/djadmin/crm/course/2/delete/ ![BLOG_20181223_154543_37](/media/blog/images/2018/12/BLOG_20181223_154543_37.png "博客图集BLOG_20181223_154543_37.png") 就会显示这个页面, 删除的时候会提示所有关联对象 ### 删除确认按钮及逻辑 #### 删除模板中增加删除确认按钮 修改table_delete.htm模板 ```html {% extends 'djadmin/base.html' %} {% load djadmin_tags %} {% block title %} 数据表删除 - 后台管理 {% endblock %} {% block content %} <h2 class="page-header">{{ model_name }}</h2> <h3 class="page-header alert alert-danger">你确定要删除吗?</h3> <div> 删除<a href="{% url 'djadmin:table_change' app_name model_name obj.id %}">【{{ obj }}】</a> </div> {% display_all_related_objs obj as all_related_obj_eles %} {{ all_related_obj_eles|safe }} {# 需要加上|safe,否则会直接显示源代码 #} <form method="post"> {% csrf_token %} <input class="hidden" value="yes" name="delete_confirm"> <input type="submit" class="btn btn-danger" value="确认删除"> <a class="btn btn-info" href="{% url 'djadmin:table_change' app_name model_name obj.id %}">返回编辑</a> </form> {% endblock %} ``` ![BLOG_20181223_154555_99](/media/blog/images/2018/12/BLOG_20181223_154555_99.png "博客图集BLOG_20181223_154555_99.png") #### 处理删除post逻辑 修改djadmin应用下的views.py中的删除数据视图 ```python # 数据删除 @login_required def table_delete(request, app_name, model_name, obj_id): admin_class = site.enable_admins[app_name][model_name] obj = admin_class.model.objects.get(id=obj_id) if request.method == 'POST': if request.POST.get('delete_confirm') == 'yes': admin_class.model.objects.filter(id=obj_id).delete() return redirect(reverse('djadmin:table_detail', args=(app_name, model_name))) return render(request, 'djadmin/table_delete.html', locals()) ```

很赞哦! (0)

文章交流

  • emoji
0人参与,0条评论

当前用户

未登录,点击   登录

站点信息

  • 建站时间:网站已运行2247天
  • 系统信息:Linux
  • 后台程序:Python: 3.8.10
  • 网站框架:Django: 3.2.6
  • 文章统计:257 篇
  • 文章评论:60 条
  • 腾讯分析网站概况-腾讯分析
  • 百度统计网站概况-百度统计
  • 公众号:微信扫描二维码,关注我们
  • QQ群:QQ加群,下载网站的学习源码
返回
顶部
标题 换行 登录
网站