一、抽象模型类设计
1、抽象模型类设计的作用:
1、定义所有模型类的公共属性,当其他的模型类继承该抽象模型类时,就具备了模型类中的属性了;在项目开发中,减少代码的编写
2、抽象模型类中经常定义的字段包括:id、创建时间、更新时间
3、class Meta类中需要定义abstract=True:表示抽象模型类,不需要映射表
4、模型类需要继承models.Model
2、auto_now_add和auto_now的区别:
auto_now_add:表示创建时间,只在创建该条数据的时候会生成当前时间保存在数据库中,之后任何操作不会改变时间
auto_now:表示更新时间,数据的更新、修改,时间会变化
from django.db import models
class BaseModel(models.Model):
'''
所有模型的抽象类,定义所有表共有的属性
'''
create_time=models.DateTimeField(auto_now_add=True,
verbose_name='创建时间',
help_text='创建时间',
)
update_time=models.DateTimeField(auto_now=True,
help_text='更新时间',
verbose_name='更新时间')
class Meta:
abstract=True #todo 表示抽象模型类,不需要映射表
二、功能菜单模型类的设计
功能菜单模型类需要继承模型类BaseModel
1、菜单都需要那些字段
1、排序数字:例如下图,菜单的顺序由排序数字决定,数字小的在最上面展示
2、菜单名称
3、url:父级菜单没有url,点击父级菜单展示显示子菜单;子菜单才有url
4、delete_flag:当对菜单进行删除操作时,不会真正的把菜单进行删除,默认为0,如果是1当前记录删除
5、parent:外键字段
2、一些注意点
1、当模型类自己与自己关联时:关联字段设置为"self"
2、related_name=‘children’:设置反向关联的属性;
当父表获取子表数据的时候时:父表模型对象.children
子表获取父表数据的时候时:子表模型对象.parent
3、on_delete=models.CASCADE:表示当父表的数据删除时,子表的数据怎么变化;models.CASCADE:表示父表中关联的子表数据也将删除
4、CharField中必须指定max_length的属性
3、blank=True和null=True的区别
blank=True:表示提交表单的时候可以为空
null=True:表示数据库中该字段可以为空
from django.db import models
from erp_project.utils.base_model import BaseModel
from django.contrib.auth.models import AbstractUser
# Create your models here.
class MenuModel(BaseModel):
'''功能菜单的模型类'''
number = models.IntegerField(help_text='排序数字',
verbose_name='排序数字',
blank=True,
null=True)
url = models.CharField(help_text='菜单访问的url',
verbose_name='菜单访问的url',
max_length=256,
blank=True,
null=True)
name = models.CharField(help_text='菜单名字',
verbose_name='菜单名字',
unique=True,
max_length=50)
# 不会真正的把数据库中的数据删除,默认为0,如果是1当前记录删除
delete_flag = models.CharField(help_text='删除标记',
verbose_name='删除标记',
max_length=1,
default='0') # 0表示没有删除
parent = models.ForeignKey('self', #todo self:自己和自己关联
blank=True,
null=True,
related_name='children', #反向关联的属性
on_delete=models.CASCADE) #当删除的时候作级联删除
class Meta:
db_table = 't_menu'
verbose_name = '功能菜单表'
verbose_name_plural = verbose_name
ordering = ['number']
def __str__(self):
return self.name
三、功能菜单视图类设计
1、都有那些视图?
1、新增-create
2、查询单个功能菜单 retrieve
3、查询所有功能菜单-list
4、查询某个父菜单下面的所有子菜单
5、查询所有的顶级菜单列表(一级菜单)
6、删除某一个功能菜单-destroy
7、批量删除多个功能菜单,传参:ids=[1,2,3]
8、修改功能菜单-update
查询所有菜单列表,不需要传递参数
查询某个父菜单下面的所有子菜单,需要传递一个参数,pid(父菜单id)
pid=0表示查询所有顶级菜单列表
class MenuView(ModelViewSet):
"""
create:
新增菜单,
参数:为Menu对象,其中delete_flag,create_time,update_time不用传参,return:添加之后的对象
retrieve:
查询单个菜单对象
list:
查询所有的菜单
如果参数中有pid,则查询某一个父菜单下的所有子菜单列表,pid=0表示查询顶级菜单列表
update:
修改菜单
destroy:
删除某个菜单
partial_update:
局部修改菜单,修改任意的某个或者某几个属性
"""
queryset = MenuModel.objects.filter(delete_flag=0).all()
serializer_class = MenuSerializer
# permission_classes = [IsAuthenticated]
def get_queryset(self):
print(self.request.user)
logger.info(f'当前登录的用户是:{self.request.user}')
# query_params = self.request.query_params.get('pid', None)
query_params = self.request.GET.get('pid', None)
if query_params:
pid = int(query_params)
if pid == 0: # 查询所有顶级菜单
return MenuModel.objects.filter(parent__isnull=True, delete_flag=0).all()
else: # 查询某个父菜单下面的所有子菜单
return MenuModel.objects.filter(parent_id=pid, delete_flag=0).all()
else:
return MenuModel.objects.filter(delete_flag=0).all()
2、删除单个菜单的注意点
1、删除数据并不是真正的将数据删除(数据库中的数据清掉),而是将该数据的delete_flag设置为1;
而原始的ModelViewSet视图类中提供的destroy方法是将数据从数据库中删除。所以需要重写该方法;
2、当删除父级菜单时,当前父级菜单下的所有子级菜单也需要删除
3、menu.save():容易忽视
def destroy(self, request, *args, **kwargs):
'''修改下删除的标记为:delete_flag=1'''
menu = self.get_object()
menu.delete_flag = 1 # 修改属性
menu.save()
# 可能该菜单下面还有很多子菜单,这些子菜单也需要修改
MenuModel.objects.filter(parent_id=menu.id).update(delete_flag=1)
return Response(status=status.HTTP_204_NO_CONTENT)
3、action装饰器的使用
action装饰器接收2个参数:methods和detail
methods:声明该action对应的请求方式,列表传递;传递的方法包含post、delete、update、put
detail:声明该action的路径是否与单一资源对应
当detail为True:表示的路径为:xxx/<pk>/action方法名称
当detail为False:表示的路径为:xxx/action方法名称
4、批量删除多个菜单的注意点
1、前端以json格式的数据传递,后端需要用request.data.get()去接收
2、传递的参数格式是列表,后端需要校验数据类型
3、删除父级菜单,同时也需要删除父级菜单下的所有子级菜单
@action(methods=['delete'], detail=False)
def multiple_delete(self, request, *args, **kwargs):
delete_ids = request.data.get('ids') #json格式传递过来的
if not delete_ids:
return Response(data={'detail': '参数错误,ids为必填参数'}, status=status.HTTP_204_NO_CONTENT)
elif not isinstance(delete_ids, list):
return Response(data={'detail': '参数错误,ids必须为列表'}, status=status.HTTP_204_NO_CONTENT)
# 先删除传递过来的菜单
MenuModel.objects.filter(id__in=delete_ids).update(delete_flag='1')
# 后删除所有的子菜单
for m_id in delete_ids:
MenuModel.objects.filter(parent_id=m_id).update(delete_flag='1')
return Response(status=status.HTTP_204_NO_CONTENT)
四、路由的配置
from django.urls import path,re_path,include
from . import views
from rest_framework import routers
from erp_system.views.menu import MenuView
router=routers.DefaultRouter()
router.register(r'menus',MenuView)
urlpatterns = [
re_path(r'',include(router.urls)),
]