前文
用惯了Django的orm,在用原生sql的时候发现要分页并转换为分页对象再格式化成json传给前端还挺麻烦的,这里总结下方法,方便后续的借鉴和参考!
Django的orm能解决几乎所有的单表查询,唯独在连表查询和子查询等复杂的查询上无能为力,特别是连表查询,在工程上的使用还是非常频繁的,而Django仅支持外键(或者伪外键,即在Django的model里仍旧定义外键,但是在数据库却不加外键约束,在某种程度上也能实现双表查询,但是也相对复杂了操作),所以如何处理连表查询并把结果分页转为json返回前段也成为必须要解决的问题!
调用原生SQL
原生sql在DJango的python的调用里我们可以用pymysql这个库来实现,也可以通过官方推介的mysql库(mysql.connector),不过相对官方的更推介pymysql这个库,因为这个库集成了更多的功能和问题解决方法,调试起来也非常方便!所以可以基于这两个库封装一个python类对象,在类对象下定义相应的DDL、DML实例方法,当实例调用时直接调用对应的实例方法即可。
那除了上述封装成类对象的形式,如果仅仅是想简单地调用sql原生方法一次,并不需要封装这么复杂的操作还可以使用如下两种方式!方式1,调用model的原生语句,比如model—>Person,可以这么写:
Person.models.raw("select * from person where id = 3")
然后你会发现上述的原生sql基本等同于orm的单表查询,所以存在的意义不大,好处是不用去和mysql建立连接、指定对应的库而已。那方式二也可以不建立连接和指定对应的库,但能实现pymysql的功能,那就是调用django.db这个库来实现,比如我要实现Person和Class两个库的链表查询,而db库已经在setting里定义好了,那么可以这么写:
from django.db import connection
cursor = connection.cursor()
cursor.execute("select * from Person a,Class b where a.id=b.id")
result = cursor.fetchall()
cursor.close()
通过上述就能轻而易举地实现sql语句的调用,不过得到的结果result是个元祖对象,那再web后端里我们一般是返回list里嵌套dict这样的数据格式,有什么方法实现吗?可以基于fetchall再封装一个方法:
# 服务于转换fetchall结果为列表嵌套字典
def dictfetchall(cursor):
columns = [col[0] for col in cursor.description] #拿到对应的字段列表
return [
dict(zip(columns, row))
for row in cursor.fetchall()
]
然后将result=cursor.fetchal()替换成result = dictfetchall(cursor)即可。不过前端的返回是要分页对象,如何分页呢?这里可以调用paginator来封装,具体如下:
def get_result(self,request):
page = request.GET.get("page") # 第几页
limit = request.GET.get("limit") # 每页多少
if page is None or limit is None: # 默认返回
page = 1
limit = 10
cursor = connection.cursor()
cursor.execute("select * from Person a,Class b where a.id=b.id")
result = dictfetchall(cursor)
cursor.close()
paginator = Paginator(result, limit) #转为限制行数的paginator对象
total = paginator.count #计算总行数
queryset = paginator.page(page) #根据前端的页数选择对应的返回结果
items = json.loads(json.dumps(queryset))
return JsonResponse(items)
而如果是上述方法,会看到报错如下:
Object of type 'Page' is not JSON serializable解决方法
即page对象无法转换为json格式,在用orm的时候可以通过serializers.serialize(‘json’, queryset)来转换为对应的json,不过原生的就不行,因为page对象json无法识别。一开始我的想法是自定义一个json对象,然后作为cls参数传入来识别,后面发现应该是想麻烦了,直接将page对象转为list对象即可,代码修改如下即可:
items = json.dumps(list(queryset))
return JsonResponse(items)
总结
记录笔记到此~