Django轮子

Django轮子
**

model_extra_fields.py

**

import abc
from functools import partial

from bmiss.db.models import prefetch_related_objects


class InitialDataBase(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def get_data(self):
        pass


class ModelExtraFieldsBase(object):
    """
    可配置扩展字段基类
    """

    def __init__(self, mappings, obj_list, ser_class=None):
        """
        :param mappings:
        {
            'display_field_name': [
                relation,
                field name/get field data callable,
                {
                    "convert": result convert callable,
                    "default": field default value, default None,
                    "initial": initial data class --> see example: ReportData
                }
            ],
            ---- examples ----
            'work_order_codes': ['scannerplanmerge_set__work_job__parent_order', self._get_merged_order_codes],
            'rgb': ['work_order__color', 'rgb', {'default': 0, 'convert': convert_func}],

            if you define:
            class ReportData:
                def __init__(self, work_order_ids):
                    ....

                # you must implement this method
                def get_data(self):
                    result = {
                        'abc': 1
                    }
                    return result

            in mappings you have:
            'report_data': ['some_relations', self._get_func, {'initial': ReportData}]

            then after initializing the child class of ModelExtraFieldsBase, you can use:
            b = self.initials['abc'] + 1
            now the value of variable b is 2
        }
        :param obj_list: objects list that need extra fields of its relational objects
        """
        self.expand_mappings = mappings
        self.objs = obj_list
        self.serializer_class = ser_class
        self.initials = {}
        self.global_initial()

    def global_initial(self):
        initial_classes = set()
        initials = []
        for name, config in self.expand_mappings.items():
            if isinstance(config, list) and len(config) > 2:
                c = config[2].get("initial")
                if c is None:
                    continue
                if isinstance(c, partial):
                    _class = c.func
                else:
                    _class = c
                if _class not in initial_classes:
                    initial_classes.add(_class)
                    initials.append(c)

        fields = set()
        for c in initials:
            if isinstance(c, partial):
                data = c().get_data()
            else:
                data = c(self.objs).get_data()
            for field_name, value in data.items():
                if field_name in fields:
                    raise ValueError(
                        "field name {} conflict, please rename initial data class {} "
                        "method get_data return values field name".format(
                            field_name, c.__name__
                        )
                    )
            self.initials.update(data)

    @property
    def data(self):
        objs = self.get_flatten_prefetch_related_objects()
        result = []
        for obj in objs:
            data = self.get_instance_fields(obj)
            result.append(data)
        return result

    def get_flatten_prefetch_related_objects(self, fields=None):
        if fields is None:
            fields = self.default_fields()
        if not fields or not isinstance(fields, list):
            fields = self.default_fields()
        args = self._get_query_args(fields)
        fields = self._get_relation_field_names(args)
        prefetch_related_objects(self.objs, *args)
        flatten_func = partial(self._flatten_instance, fields=fields)
        return list(map(flatten_func, self.objs))

    def _get_query_args(self, display_fields):
        args = set()
        expand_fields = []
        for field_name in display_fields:
            if not isinstance(self.expand_mappings[field_name], list):
                continue
            relations = self.expand_mappings[field_name][0]
            if relations == "self":
                continue
            elif isinstance(relations, list):
                expand_fields.extend(relations)
            elif isinstance(relations, str):
                expand_fields.append(relations)

        expand_fields.sort(key=lambda e: -len(e))
        for arg in expand_fields:
            if arg in args or any([k.startswith("{}__".format(arg)) for k in args]):
                continue
            else:
                args.add(arg)
        return args

    def _get_relation_field_names(self, relations):
        result = set()
        for e in relations:
            result.add(e)
            count = e.count("__")
            for i in range(count):
                elem, *_ = e.rsplit("__", i + 1)
                result.add(elem)
        return result

    def _flatten_instance(self, instance, fields):
        for f in fields:
            name = f
            relations = f.split("__")
            obj = instance
            for r in relations:
                if hasattr(obj, r):
                    obj = getattr(obj, r)
                else:
                    obj = None
                    break
            setattr(instance, name, obj)
        return instance

    def get_instance_fields(self, obj, fields=None):
        if fields is None:
            fields = self.default_fields()
        if self.serializer_class:
            result = self.serializer_class(obj).data
        else:
            result = {}
        for f in fields:
            if f not in self.expand_mappings:
                continue
            if not isinstance(self.expand_mappings[f], list):
                v = self.expand_mappings[f]
                if callable(v):
                    result[f] = v(obj)
                    continue
                result[f] = v
                continue
            name, get_name, *kwargs = self.expand_mappings[f]
            if isinstance(get_name, str):
                if name == "self":
                    result[f] = getattr(obj, get_name)
                else:
                    relate_obj = getattr(obj, name)
                    result[f] = getattr(relate_obj, get_name) if relate_obj else None
            elif callable(get_name):
                result[f] = get_name(obj)
            default_value = kwargs[0].get("default", None) if len(kwargs) else None
            if result[f] is None:
                result[f] = default_value
            convert = kwargs[0].get("convert") if len(kwargs) else None
            if result[f] is not None and callable(convert):
                result[f] = convert(result[f])
        return result

    def default_fields(self):
        return list(self.expand_mappings.keys())

**

query.py

**

-- coding: utf-8 --

from ..registry import foreignkeys


def prefetch_related_objects(instances, *lookups):
    _PrefetchManger(instances, lookups)


class _PrefetchManger(object):
    def __init__(self, instances, lookups):
        self._visit_instances(instances, lookups)

    def _visit_instances(self, instances, lookups):
        first_level, next_level = _split_lookup(lookups)
        if not first_level:
            return
        if not instances:
            return
        model = type(instances[0])
        fks = foreignkeys.get_foreign_keys(model)
        reverse_related_objs = foreignkeys.get_reverse_related_objects(model)
        for lookup in first_level:
            is_reverse = False
            if lookup.endswith("_set"):
                is_reverse = True
                reverse_relate_model_name = lookup[:-4]
                for from_model, rel_info in list(reverse_related_objs.items()):
                    if from_model.__name__.lower() == reverse_relate_model_name:
                        lookup_field = "id"
                        relation_model = from_model
                        relation_field = rel_info["from_field"]
                        break
                else:
                    continue
            else:
                lookup_field = lookup + "_id"
                relation_field = "id"
                if lookup_field not in fks:
                    continue
                relation_model = fks[lookup_field]["to_model"]
            lookup_ids = []
            for instance in instances:
                lookup_id = getattr(instance, lookup_field)
                if lookup_id is not None:
                    lookup_ids.append(lookup_id)
            relation_instances = list(
                relation_model.objects.filter(**{"%s__in" % relation_field: lookup_ids})
            )
            lookup_id_to_relation_instances = {}
            for relation_instance in relation_instances:
                lookup_id_to_relation_instances.setdefault(
                    getattr(relation_instance, relation_field), []
                ).append(relation_instance)
            # fill the cache
            for instance in instances:
                instance_lookup_result = lookup_id_to_relation_instances.get(
                    getattr(instance, lookup_field), []
                )
                if not is_reverse:
                    instance_lookup_result = (
                        instance_lookup_result[0] if instance_lookup_result else None
                    )
                setattr(instance, lookup, instance_lookup_result)
            self._visit_instances(relation_instances, next_level.get(lookup, []))


def _split_lookup(lookups):
    first_level_lookup = []
    second_level_lookup = {}
    for lookup in lookups:
        pieces = lookup.split("__", 1)
        first = pieces[0]
        children = pieces[1:]
        if first:
            first_level_lookup.append(first)
            if children:
                second_level_lookup.setdefault(first, []).append(children[0])
    first_level_lookup = list(set(first_level_lookup))
    return first_level_lookup, second_level_lookup

使用轮子

from diagnosis.utils.model_extra_fields import ModelExtraFieldsBase


class PersonDataFetcher(ModelExtraFieldsBase):

    def __init__(self, objs, request, detail=False):
        self.request = request
        mappings = {
            'id': ['self', 'id'],
            'name': ['self', 'name'],
            'engineer_level': ['self', 'engineer_level'],
            'inquiry_attitude': ['self', 'inquiry_attitude'],
            'inquiry_consultation': ['self', 'inquiry_consultation'],
            'inquiry_status': ['self', 'inquiry_status'],
            'tag': ['persontotag_set__tag', self._get_tag],
            'inquiry_points': ['self', 'inquiry_points'],
            'avatar_url': ['self', 'avatar_url'],
            'answer_count': ['answer_set', self._get_answer_count],
            'area': ['location_county__city__province', self._get_area],
        }
        if detail is True:
            mappings.update({
                'desc': ['self', 'desc'],
                'tag_list': ['persontotag_set__tag', self._get_tag_list],
                'inquiry_top3': ['evaluated_set__create_company', self._get_inquiry_top3],
                'good_consultation': ['evaluated_set', self._get_good_consultation],
                'other_consultation': ['evaluated_set', self._get_other_consultation],
            })

        super(PersonDataFetcher, self).__init__(mappings, objs)

    def _get_area(self, instance):
        area = ''
        try:
            province = instance.location_county.city.province.name
            city = instance.location_county.city.name
            county = instance.location_county.name
            area = province + '·' + city + '·' + county
        except AttributeError:
            return '获取地址信息失败'
            # raise NotAreaError('获取地址信息失败')
        finally:
            return area

    def _get_answer_count(self, instance):
        try:
            answer_count = instance.anwser_set
        except AttributeError:
            return 0
        return len(answer_count)

    def _get_inquiry_top3(self, instance):
        data = []
        try:
            evaluated_obj_top3 = list(reversed(instance.evaluated_set))[:3]
            for res in evaluated_obj_top3:
                company = res.create_company
                dic = {
                    'inquiry_attitude': res.inquiry_attitude,
                    'use_points': res.use_points,
                    'inquiry_consultation': res.inquiry_consultation,
                    'create_time': res.create_time,
                    'company_name': company.name,
                    'company_avatar_url': company.avatar_url,
                }
                data.append(dic)
        except Exception:
            return data
        finally:
            return data

    def _get_tag_list(self, instance):
        person_tag_list = instance.persontotag_set
        tag_list = []
        if person_tag_list:
            for person_tag_obj in person_tag_list:
                tag_obj = person_tag_obj.tag
                res = {
                    'tag_id': tag_obj.id,
                    'tag_name': tag_obj.name,
                    'tag_count': person_tag_obj.tag_count
                }
                tag_list.append(res)
        return tag_list

    def _get_tag(self, instance):
        person_tag_list = instance.persontotag_set
        tag_list = []
        if person_tag_list:
            for person_tag_obj in person_tag_list:
                tag_name = person_tag_obj.tag.name
                tag_list.append(tag_name)
        return tag_list

    def _get_good_consultation(self, instance):
        good_consultation_count = 0
        evaluated_obj_list = instance.evaluated_set
        if evaluated_obj_list:
            for res in evaluated_obj_list:
                if res.attitude_consultation_average >= 4:
                    good_consultation_count += 1
        return good_consultation_count

    def _get_other_consultation(self, instance):
        other_consultation_count = 0
        evaluated_obj_list = instance.evaluated_set
        if evaluated_obj_list:
            for res in evaluated_obj_list:
                if res.attitude_consultation_average < 4:
                    other_consultation_count += 1
        return other_consultation_count

在轮子中(表名__表名)双下划线可以在多表查询中代表正向查询
(表名_set)表名单下划线set呆逼啊反向查询
在轮子的使用中,不可以出现ORM查询数据的语句

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值