def __init__(self, source, attribute=None, empty_value=None, func=None, help_text=None, html_extra_classes=None, include_label=False, is_attribute_absolute_url=False, is_object_absolute_url=False, is_identifier=False, is_sortable=False, kwargs=None, label=None, order=None, sort_field=None, views=None, widget=None, widget_condition=None): self._label = label self._help_text = help_text self.source = source self.attribute = attribute self.empty_value = empty_value self.exclude = () self.func = func self.html_extra_classes = html_extra_classes self.is_attribute_absolute_url = is_attribute_absolute_url self.is_object_absolute_url = is_object_absolute_url self.is_identifier = is_identifier self.is_sortable = is_sortable self.kwargs = kwargs or {} self.include_label = include_label self.order = order or 0 self.sort_field = sort_field self.views = views or [] self.widget = widget self.widget_condition = widget_condition if self.is_attribute_absolute_url or self.is_object_absolute_url: if not self.widget: self.widget = SourceColumnLinkWidget self.__class__._registry.setdefault(source, []) self.__class__._registry[source].append(self) self._calculate_label() self._calculate_help_text() if self.is_sortable: field_name = self.sort_field or self.attribute try: get_related_field(model=self.source, related_field_name=field_name) except FieldDoesNotExist as exception: raise ImproperlyConfigured( '"{}" is not a field of "{}", cannot be used as a ' 'sortable column.'.format(field_name, self.source)) from exception
def register_inheritance(cls, model, related): model_reverse = get_related_field( model=model, related_field_name=related).related_model cls._inheritances_reverse.setdefault(model_reverse, []) cls._inheritances_reverse[model_reverse].append(model) cls._inheritances[model] = related
def _initialize(self): for search_field in self.search_fields: related_model = get_related_field( model=self.model, related_field_name=search_field.field).model if related_model != self.model: self.__class__._model_search_relationships.setdefault( self.model, set()) self.__class__._model_search_relationships[self.model].add( related_model) self.__class__._model_search_relationships.setdefault( related_model, set()) self.__class__._model_search_relationships[related_model].add( self.model)
def _get_acl_filters(self, queryset, stored_permission, user, related_field_name=None): """ This method does the bulk of the work. It generates filters for the AccessControlList model to determine if there are ACL entries for the members of the queryset's model provided. """ # Determine which of the cases we need to address # 1: No related field # 2: Related field # 3: Related field that is Generic Foreign Key # 4: No related field, but has an inherited related field, solved by # recursion, branches to #2 or #3. # 5: Inherited field of a related field # 6: Inherited field of a related field that is Generic Foreign Key # -- Not addressed yet -- # 7: Has a related function result = [] if related_field_name: related_field = get_related_field( model=queryset.model, related_field_name=related_field_name) if isinstance(related_field, GenericForeignKey): # Case 3: Generic Foreign Key, multiple ContentTypes + object # id combinations # Also handles case #6 using the parent related field # reference template. # Craft a double underscore reference to a previous related # field in the case where multiple related fields are # associated. # Example: object_layer__content_type recuisive_related_reference = '__'.join( related_field_name.split('__')[0:-1]) # If there is at least one parent related field we add a # double underscore to make it a valid filter template. if recuisive_related_reference: recuisive_related_reference = '{}__'.format( recuisive_related_reference) content_type_object_id_queryset = queryset.annotate( ct_fk_combination=Concat( '{}{}'.format(recuisive_related_reference, related_field.ct_field), Value('-'), '{}{}'.format(recuisive_related_reference, related_field.fk_field), output_field=CharField())).values('ct_fk_combination') acl_filter = self.annotate(ct_fk_combination=Concat( 'content_type', Value('-'), 'object_id', output_field=CharField())).filter( permissions=stored_permission, role__groups__user=user, ct_fk_combination__in=content_type_object_id_queryset ).values('object_id') field_lookup = '{}object_id__in'.format( recuisive_related_reference) result.append(Q(**{field_lookup: acl_filter})) else: # Case 2: Related field of a single type, single ContentType, # multiple object id content_type = ContentType.objects.get_for_model( model=related_field.related_model) field_lookup = '{}_id__in'.format(related_field_name) acl_filter = self.filter( content_type=content_type, permissions=stored_permission, role__groups__user=user).values('object_id') # Don't add empty filters otherwise the default AND operator # of the Q object will return an empty queryset when reduced # and filter out objects that should be in the final queryset. if acl_filter: result.append(Q(**{field_lookup: acl_filter})) # Case 5: Related field, has an inherited related field itself # Bubble up permssion check # Recurse and reduce # TODO: Add relationship support: OR or AND # TODO: OR for document pages, version, doc, and types # TODO: AND for new cabinet levels ACLs try: related_field_model_related_fields = ( ModelPermission.get_inheritance( model=related_field.related_model), ) except KeyError: pass else: relation_result = [] for related_field_model_related_field_name in related_field_model_related_fields: related_field_name = '{}__{}'.format( related_field_name, related_field_model_related_field_name) related_field_inherited_acl_queries = self._get_acl_filters( queryset=queryset, stored_permission=stored_permission, user=user, related_field_name=related_field_name) if related_field_inherited_acl_queries: relation_result.append( reduce(operator.and_, related_field_inherited_acl_queries)) if relation_result: result.append(reduce(operator.or_, relation_result)) else: # Case 1: Original model, single ContentType, multiple object id content_type = ContentType.objects.get_for_model( model=queryset.model) field_lookup = 'id__in' acl_filter = self.filter( content_type=content_type, permissions=stored_permission, role__groups__user=user).values('object_id') result.append(Q(**{field_lookup: acl_filter})) # Case 4: Original model, has an inherited related field try: related_fields = (ModelPermission.get_inheritance( model=queryset.model), ) except KeyError: pass else: relation_result = [] for related_field_name in related_fields: inherited_acl_queries = self._get_acl_filters( queryset=queryset, stored_permission=stored_permission, related_field_name=related_field_name, user=user) if inherited_acl_queries: relation_result.append( reduce(operator.and_, inherited_acl_queries)) if relation_result: result.append(reduce(operator.or_, relation_result)) # Case 7: Has a function try: field_query_function = ModelPermission.get_field_query_function( model=queryset.model) except KeyError: pass else: function_results = field_query_function() # Filter by the model's content type content_type = ContentType.objects.get_for_model( model=queryset.model) acl_filter = self.filter( content_type=content_type, permissions=stored_permission, role__groups__user=user).values('object_id') # Obtain an queryset of filtered, authorized model instances acl_queryset = queryset.model._meta.default_manager.filter( id__in=acl_filter).filter(**function_results['acl_filter']) if 'acl_values' in function_results: acl_queryset = acl_queryset.values( *function_results['acl_values']) # Get the final query using the filtered queryset as the # reference result.append( Q(**{function_results['field_lookup']: acl_queryset})) return result
def get_model_field(self): return get_related_field(model=self.get_model(), related_field_name=self.field)