美文网首页
Django Manager

Django Manager

作者: manbug | 来源:发表于2017-11-07 18:06 被阅读0次

以前只知道项目一般分为dao层,service层,api层,不知道原因,因为初学时没人引导都混在一起习惯了。直到这次本来打算对内开放的平台要对外开放,需要创建demo用户展现混淆后的数据。这时候庆幸对项目进行过分层设计。

1. 思路

首先对用户进行分组,api返回数据时进行用户组判断来决定接口返回的数据。
考虑过三种办法:

  1. 直接改数据库,把原数据备份,然后修改原表。
    优点: 方便。
    缺点: 涉及到的表比较多;如果这么修改就相当于屏蔽掉了正常用户。
  2. 加装饰器
    既然已经分了service层,只需把service层中每个结果进行混淆就可以。
    优点:清晰明了,易于今后修改。
    缺点: 每个返回的result都是一个三到四层的无规律的列表字典包含式,如:{"x": [{"xx": {"aa": 1}, "xx2": {...}}, {}, ...], "y": ...},而且还涉及到返回数据中包括城市编码,排名等不需要混淆的数据。很难写(╯﹏╰)。
  3. 修改Model的Manager。
    优点:清晰明了
    缺点: 涉及到的每张表都要修改,而且QuerySet的内置方法很多也要修改,还有一些关联表(如city.Radiation.xxx)不是很好修改。不过最后还是采用这种方法。

2. Manager,QuerySet,Model

Manager,QuerySet,Model是django的ORM用到的三个类。

  1. 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
  1. 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. 具体做法

  1. 定义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
  1. 定义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)
  1. Models
class YearlyCityInfo(models.Model):
    ...
    objects = FixManager()
    faker_objects = FakerManager()

4. 未完的事

  • exclude()方法也会影响faker_objects返回的结果,估计也要看下源码如first()般定制。
  • 关联表的具体问题。可以考虑把关联表也制定不同的manager,然后在原表设置两种关联方式。

相关文章

网友评论

      本文标题:Django Manager

      本文链接:https://www.haomeiwen.com/subject/wzbjmxtx.html