def get_as_specialization(self, final_specialization=True): """ Get the specialized model instance which corresponds to the general case. :param final_specialization: Whether the specialization returned is the most specialized specialization or whether the direct specialization is used :type final_specialization: :class:`bool` :return: The specialized model corresponding to the current model """ path = self.specialization_type if not final_specialization: # We need to find the path which is only one-step down from the # current level of specialization. path = find_next_path_down( self._meta.specialization, path, PATH_SEPERATOR ) return self._meta.specializations[path].objects.get( pk=self.pk )
def iterator(self): """ Override the iteration to ensure what's returned are Specialized Model instances. """ # Determine whether there are any extra fields which are also required # to order the queryset. This is needed as Django's implementation of # ValuesQuerySet cannot cope with fields being omitted which are used in # the ordering and originating from an extra select extra_fields = set(self.query.extra.keys()) ordering_fields = set(field.lstrip("-") for field in self.query.order_by) extra_ordering_fields = list(extra_fields & ordering_fields) values_query_fields = ["specialization_type", "id"] + extra_ordering_fields # Get the resource ids and types together specializations_data = self._clone().values(*values_query_fields) # Transform this into a dictionary of IDs by type: ids_by_specialization = defaultdict(list) # and keep track of the IDs which respect the ordering specified in the # queryset: specialization_ids = [] for specialization_data in specializations_data: specialization_type = specialization_data["specialization_type"] specialization_id = specialization_data["id"] ids_by_specialization[specialization_type].append(specialization_id) specialization_ids.append(specialization_id) specialized_model_instances = {} # Add the sub-class instances into a single look-up for specialization, ids in ids_by_specialization.items(): if not self._final_specialization: # Coerce the specialization to only be the direct child of the # general model (self.model): specialization = find_next_path_down(self.model.model_specialization, specialization, PATH_SEPERATOR) sub_queryset = self.model._meta.specializations[specialization].objects.all() # Copy any deferred loading over to the new querysets: sub_queryset.query.deferred_loading = self.query.deferred_loading # Copy any extra select statements to the new querysets. NB: It # doesn't make sense to copy any of the "where", "tables" or # "order_by" options as these have already been applied in the # parent queryset sub_queryset.query._extra = self.query._extra sub_instances = sub_queryset.in_bulk(ids) specialized_model_instances.update(sub_instances) for resource_id in specialization_ids: yield specialized_model_instances[resource_id]
def get(self, *args, **kwargs): """ Override get to ensure a specialized model instance is returned. :return: A specialized model instance """ if "specialization_type" in kwargs: # if the specialization is explicitly specified, use this to work out # which sub-class of the general model we'll use: specialization = kwargs.pop("specialization_type") else: try: specialization = ( super(SpecializedQuerySet, self) .filter(*args, **kwargs) .values_list("specialization_type", flat=True)[0] ) except IndexError: raise self.model.DoesNotExist("%s matching query does not exist." % self.model._meta.object_name) if not self._final_specialization: # Coerce the specialization to only be the direct child of the # general model (self.model): specialization = find_next_path_down(self.model.model_specialization, specialization, PATH_SEPERATOR) try: return self.model._meta.specializations[specialization].objects.get(*args, **kwargs) except KeyError: raise self.model.DoesNotExist("%s matching query does not exist." % self.model._meta.object_name)
def test_non_root(self): """Test the path below a non-root path can be identified correctly""" non_root = '/home/' full_path = '/home/barry/dev/' eq_(find_next_path_down(non_root, full_path, '/'), '/home/barry/')
def test_root_down(self): """Test the path below the root path can be identified correctly.""" root = '/' full_path = '/home/barry/dev/' eq_(find_next_path_down(root, full_path, '/'), '/home/')
def iterator(self): """ Override the iteration to ensure what's returned are Specialized Model instances. """ # Get the resource ids and types together: specializations_by_id = self._clone().values_list( 'specialization_type', 'id' ) # Transform this into a dictionary of IDs by type: ids_by_specialization = defaultdict(list) # and keep track of the IDs which respect the ordering specified in the # queryset: specialization_ids = [] for specialization, id in specializations_by_id: ids_by_specialization[specialization].append(id) specialization_ids.append(id) specialized_model_instances = {} # Add the sub-class instances into a single look-up for specialization, ids in ids_by_specialization.items(): if not self._final_specialization: # Coerce the specialization to only be the direct child of the # general model (self.model): specialization = find_next_path_down( self.model._meta.specialization, specialization, PATH_SEPERATOR ) sub_queryset = self.model._meta.specializations[ specialization ].objects.all() # Copy any deferred loading over to the new querysets: sub_queryset.query.deferred_loading = self.query.deferred_loading sub_instances = sub_queryset.in_bulk(ids) specialized_model_instances.update(sub_instances) for resource_id in specialization_ids: yield specialized_model_instances[resource_id]
def iterator(self): """ Override the iteration to ensure what's returned are Specialized Model instances. """ # Get the resource ids and types together: specializations_by_id = self._clone().values_list( 'specialization_type', 'id') # Transform this into a dictionary of IDs by type: ids_by_specialization = defaultdict(list) # and keep track of the IDs which respect the ordering specified in the # queryset: specialization_ids = [] for specialization, id in specializations_by_id: ids_by_specialization[specialization].append(id) specialization_ids.append(id) specialized_model_instances = {} # Add the sub-class instances into a single look-up for specialization, ids in ids_by_specialization.items(): if not self._final_specialization: # Coerce the specialization to only be the direct child of the # general model (self.model): specialization = find_next_path_down( self.model.model_specialization, specialization, PATH_SEPERATOR) sub_queryset = self.model._meta.specializations[ specialization].objects.all() # Copy any deferred loading over to the new querysets: sub_queryset.query.deferred_loading = self.query.deferred_loading sub_instances = sub_queryset.in_bulk(ids) specialized_model_instances.update(sub_instances) for resource_id in specialization_ids: yield specialized_model_instances[resource_id]
def get_as_specialization(self, final_specialization=True): """ Get the specialized model instance which corresponds to the general case. :param final_specialization: Whether the specialization returned is the most specialized specialization or whether the direct specialization is used :type final_specialization: :class:`bool` :return: The specialized model corresponding to the current model """ path = self.specialization_type if not final_specialization: # We need to find the path which is only one-step down from the # current level of specialization. path = find_next_path_down(self.__class__.model_specialization, path, PATH_SEPERATOR) return self._meta.specializations[path].objects.get(pk=self.pk)
def get(self, *args, **kwargs): """ Override get to ensure a specialized model instance is returned. :return: A specialized model instance """ if 'specialization_type' in kwargs: # if the specialization is explicitly specified, use this to work out # which sub-class of the general model we'll use: specialization = kwargs.pop('specialization_type') else: try: specialization = super(SpecializedQuerySet, self)\ .filter(*args, **kwargs).values_list( 'specialization_type', flat=True )[0] except IndexError: raise self.model.DoesNotExist( "%s matching query does not exist." % self.model._meta.object_name) if not self._final_specialization: # Coerce the specialization to only be the direct child of the # general model (self.model): specialization = find_next_path_down( self.model.model_specialization, specialization, PATH_SEPERATOR) try: return self.model._meta.specializations[specialization]\ .objects.get(*args, **kwargs) except KeyError: raise self.model.DoesNotExist("%s matching query does not exist." % self.model._meta.object_name)