Django的cmdb探索与开发(二)
先记下最坑的地方吧。关于Django调用原生语句RawQuerySet的一些问题。
官方文档上有一段话是这样写的:
警告
虽然RawQuerySet可以像普通的QuerySet一样迭代,RawQuerySet并没有实现可以在 QuerySet上使用的所有方法。例如,__bool__()和__len__()在RawQuerySet中没有被定义,所以所有RawQuerySet转化为布尔值的结果都是True。RawQuerySet中没有实现他们的原因是,在没有内部缓存的情况下会导致性能下降,而且增加内部缓存不向后兼容。
在些被坑了好久。因为对Django不够熟悉的原因,在多表联查、数据分页上被这个<class 'django.db.models.query.RawQuerySet'>的对象坑的不要不要的。
我的view里关于数据查询的一段代码是这样写的。
因为Devices的model里没有定义DataroomName这个名称的字段,当时想到的只能是通过多表联查,同时查询 Dataroom表和Devices表的数据然后显示出来。
#获取RawQuerySet结果的len值 def get_len(rawqueryset): def __len__(self): params = ["""'%s'""" % p for p in self.params] sql = 'SELECT COUNT(*) FROM (' + (rawqueryset.raw_query % tuple(params)) + ') B;' cursor = connection.cursor() cursor.execute(sql) row = cursor.fetchone() return row[0] return __len__ def DeviceList(request): user = request.session["UserData"]["user"] totalsec = len(Devices.objects.filter(deviceType=3)) totalnet = len(Devices.objects.filter(deviceType=2)) totalserver = len(Devices.objects.filter(deviceType=1)) totaldevices = len(Devices.objects.all()) page_num = 1 #给页码一个初始赋值 if request.method == "GET" and request.GET.get("page"): page_num = request.GET.get("page") page_num = int(page_num) page_numnext = page_num+1 page_numlast = page_num-1 #机房select下拉框 dataroom = ImportForm(request.POST) #查询语句 sql = "select users_devices.basemodel_ptr_id,users_devices.sn,users_devices.CabinetID,users_devices.deviceMap,users_devices.company,users_devices.model,users_devices.produceIP,users_devices.updatetime,users_dataroom.DataroomName from users_dataroom,users_devices where users_dataroom.basemodel_ptr_id = users_devices.DataRoomID" #获取多表联查结果 deviceData = Devices.objects.raw(sql) #获取结果表长度并强行定义给RawQuerySet对象以实现前端分面 setattr(type(deviceData), '__len__', get_len(deviceData)) page_total = len(deviceData)/10 if page_total != int(page_total): page_total += 1 page_total = int(page_total) page_list = range(1,page_total+1)#确定页码 page_device = deviceData[(int(page_num)-1)*10:int(page_num)*10] return render(request, "DevicesList.html", locals())
在此算是把Django原生语句的用法用了个遍。最关键的是还会出现一些小bug。比如用了
setattr(type(deviceData), '__len__', get_len(deviceData))之后。另一个页面的RawQuerySet查询的前端显示也会爱到影响。。。。
解决方法:(多表联查什么的理论上都可以通过这个实现)
model中定义函数直接通过QuerySet获取。。还是用ORM吧。。。原生的SQL不到成不得已,还是不要用。。。
models.py
class Cabinet(BaseModel): CabinetName = models.CharField(max_length=32, verbose_name="机柜名称") DataRoomID = models.IntegerField(verbose_name="机房ID") Capacity = models.CharField(max_length=32,verbose_name="机柜容量") # AbleCapacity = models.CharField(max_length=32,verbose_name="机柜可用量") CabinetDes = models.CharField(max_length=64,verbose_name="机柜描述") #获取设备数目 def getdevicescout(self): return len(Devices.objects.filter(CabinetID=self.basemodel_ptr_id)) #获取机房名称 def getDataRoomName(self): return Dataroom.objects.get(basemodel_ptr_id=self.DataRoomID).DataroomName #计算剩余可用容量 def AbleCapcity(self): deviceuse = 0 for device in Devices.objects.filter(CabinetID=self.basemodel_ptr_id): devicespace = int(device.deviceSize) + 1 deviceuse = deviceuse+devicespace ablecap = int(self.Capacity)-deviceuse return ablecap
view.py CabinetData = Cabinet.objects.all() html: {% for c in CabinetData %} <tr class="first"> <td> <input type="checkbox" style="margin-top: 0"/> </td> <td class="description"> {{ c.CabinetName }} </td> <td class="description"> {{ c.getDataRoomName }} </td> <td class="description"> {{ c.getdevicescout }} </td> <td class="description"> {{ c.Capacity }} </td> <td class="description"> {{ c.AbleCapcity }} </td> <td> <ul class="actions"> <li><i class="table-edit"></i></li> <li><i class="table-settings"></i></li> <li class="last"><i class="table-delete" id="{{ c.basemodel_ptr_id }}" onclick="del(this.id)"></i></li> </ul> </td> </tr> {% endfor %}