Django Manager
2017-11-07 本文已影响0人
manbug
以前只知道项目一般分为dao层,service层,api层,不知道原因,因为初学时没人引导都混在一起习惯了。直到这次本来打算对内开放的平台要对外开放,需要创建demo用户展现混淆后的数据。这时候庆幸对项目进行过分层设计。
1. 思路
首先对用户进行分组,api返回数据时进行用户组判断来决定接口返回的数据。
考虑过三种办法:
- 直接改数据库,把原数据备份,然后修改原表。
优点: 方便。
缺点: 涉及到的表比较多;如果这么修改就相当于屏蔽掉了正常用户。 - 加装饰器
既然已经分了service层,只需把service层中每个结果进行混淆就可以。
优点:清晰明了,易于今后修改。
缺点: 每个返回的result都是一个三到四层的无规律的列表字典包含式,如:{"x": [{"xx": {"aa": 1}, "xx2": {...}}, {}, ...], "y": ...},而且还涉及到返回数据中包括城市编码,排名等不需要混淆的数据。很难写(╯﹏╰)。 - 修改Model的Manager。
优点:清晰明了
缺点: 涉及到的每张表都要修改,而且QuerySet的内置方法很多也要修改,还有一些关联表(如city.Radiation.xxx)不是很好修改。不过最后还是采用这种方法。
2. Manager,QuerySet,Model
Manager,QuerySet,Model是django的ORM用到的三个类。
- Manager定义表级方法,即我们可以继承models.Manager来定制Manager:
(官网demo)
class PollManager(models.Manager):
def with_counts(self):
from django.db import connection
with connection.cursor() as cursor:
cursor.execute("""
SELECT p.id, p.question, p.poll_date, COUNT(*)
FROM polls_opinionpoll p, polls_response r
WHERE p.id = r.poll_id
GROUP BY p.id, p.question, p.poll_date
ORDER BY p.poll_date DESC""")
result_list = []
for row in cursor.fetchall():
p = self.model(id=row[0], question=row[1], poll_date=row[2])
p.num_responses = row[3]
result_list.append(p)
return result_list
class OpinionPoll(models.Model):
question = models.CharField(max_length=200)
poll_date = models.DateField()
objects = PollManager()
简易定制filter, 可以直接在select时改变数据(select id + 10 as 'id' ...)
class FakerManagerV2(models.Manager):
def faker_filter(self, **kwargs):
from django.db import connection
if kwargs:
s = ""
for key in kwargs.keys():
v = kwargs[key]
s += "{}={}, ".format(key, v)
SQL_RAW = """SELECT id, year, ... FROM table_name WHERE {}""".format(s[:-2])
else:
SQL_RAW = """SELECT id, year, ... FROM table_name"""
with connection.cursor() as cursor:
cursor.execute(SQL_RAW)
result_list = []
for row in cursor.fetchall():
p = YearlyCityInfo.objects.get(id=row[0])
# p = self.model(id=row[0])
for k, v in p.__dict__.items():
if k == "id" or k == "city_id" or k == "province_id" or k =="year":
continue
if hasattr(p, k):
setattr(p, k, make_random(v))
result_list.append(p)
return result_list
- QuerySet是多个Model实例的 。。类似于列表一样的东西。
也包含表级方法,如filter, first, exclude...方法就封装在models.QuerySet里。
(官网demo)
class PersonQuerySet(models.QuerySet):
def authors(self):
return self.filter(role='A')
def editors(self):
return self.filter(role='E')
class PersonManager(models.Manager):
def get_queryset(self):
return PersonQuerySet(self.model, using=self._db)
可以更改自带的first()方法
这里是我修改了Manager返回的数据,把每个instance的参数都做了修改,但是没有保存,这时用QuerySet[0]就返回我修改后的数据,用QuerySet.first()就返回原数据。。所以只能重新定制。
class FixQuerySet(models.QuerySet):
def first(self):
# TODO: 为什么原版的first不行,还没搞懂
if self:
return self[0]
return None
class FixManager(models.Manager):
def get_queryset(self):
return FixQuerySet(self.model, using=self._db)
3. 具体做法
- 定义FakerManager
class FakerManager(models.Manager):
def faker_filter(self, **kwargs):
ycis = YearlyCityInfo.objects.filter(**kwargs)
for p in ycis:
for k, v in p.__dict__.items():
if k == "id" or k == "city_id" or k == "province_id" or k =="year":
continue
if hasattr(p, k):
setattr(p, k, make_fixed_mix(v))
return ycis
- 定义first()方法
class FixQuerySet(models.QuerySet):
def first(self):
# TODO: 为什么原版的first不行,还没搞懂
if self:
return self[0]
return None
class FixManager(models.Manager):
def get_queryset(self):
return FixQuerySet(self.model, using=self._db)
- Models
class YearlyCityInfo(models.Model):
...
objects = FixManager()
faker_objects = FakerManager()
4. 未完的事
- exclude()方法也会影响faker_objects返回的结果,估计也要看下源码如first()般定制。
- 关联表的具体问题。可以考虑把关联表也制定不同的manager,然后在原表设置两种关联方式。