Example #1
0
def sync_entity(model_obj, is_deleted, entity_cache=None):
    """
    Given a model object, either delete it from the entity table (if
    is_deleted=False) or update its entity values and relationships.
    """
    entity_type = ContentType.objects.get_for_model(model_obj)

    if is_deleted:
        # Delete the entity, taking in the case of where the entity may not exist
        # before hand
        Entity.objects.filter(entity_type=entity_type, entity_id=model_obj.id).delete()
    else:
        entity_cache = entity_cache if entity_cache is not None else {}

        if not entity_cache.get((entity_type, model_obj.id)):
            # Create or update the entity
            entity = Entity.objects.upsert(
                entity_type=entity_type, entity_id=model_obj.id, updates={
                    'entity_meta': model_obj.get_entity_meta(),
                    'is_active': model_obj.is_entity_active(),
                })[0]

            # Delete all of the existing relationships super to this entity
            sync(entity.super_relationships.all(), [
                EntityRelationship(
                    super_entity=sync_entity(super_model_obj, False, entity_cache),
                    sub_entity=entity,
                )
                for super_model_obj in model_obj.get_super_entities()
            ], ['super_entity_id', 'sub_entity_id'])

            entity_cache[(entity_type, model_obj.id)] = entity

        return entity_cache[(entity_type, model_obj.id)]
Example #2
0
    def save(self, *args, **kwargs):
        """
        Builds the objects managed by the template before saving the template.
        """
        super(SmartManager, self).save(*args, **kwargs)

        smart_manager = import_string(self.smart_manager_class)(self.template)
        primary_built_obj = smart_manager.build()

        # Do an update of the primary object type and id after it has been built. We use an update since
        # you can't call save in a save method. We may want to put this in post_save as well later.
        if primary_built_obj:
            self.primary_obj_type = ContentType.objects.get_for_model(primary_built_obj)
            self.primary_obj_id = primary_built_obj.id
            SmartManager.objects.filter(id=self.id).update(
                primary_obj_type=self.primary_obj_type, primary_obj_id=primary_built_obj.id)

        # Sync all of the objects from the built template
        sync(self.smartmanagerobject_set.all(), [
            SmartManagerObject(
                smart_manager=self,
                model_obj_id=built_obj.id,
                model_obj_type=ContentType.objects.get_for_model(built_obj, for_concrete_model=False),
            )
            for built_obj in smart_manager.built_objs
        ], ['smart_manager_id', 'model_obj_id', 'model_obj_type_id'])
Example #3
0
    def save(self, *args, **kwargs):
        """
        Builds the objects managed by the template before saving the template.
        """
        super(SmartManager, self).save(*args, **kwargs)

        smart_manager = import_string(self.smart_manager_class)(self.template)
        primary_built_obj = smart_manager.build()

        # Do an update of the primary object type and id after it has been built. We use an update since
        # you can't call save in a save method. We may want to put this in post_save as well later.
        if primary_built_obj:
            self.primary_obj_type = ContentType.objects.get_for_model(
                primary_built_obj)
            self.primary_obj_id = primary_built_obj.id
            SmartManager.objects.filter(id=self.id).update(
                primary_obj_type=self.primary_obj_type,
                primary_obj_id=primary_built_obj.id)

        # Sync all of the objects from the built template
        sync(self.smartmanagerobject_set.all(), [
            SmartManagerObject(
                smart_manager=self,
                model_obj_id=built_obj.id,
                model_obj_type=ContentType.objects.get_for_model(
                    built_obj, for_concrete_model=False),
            ) for built_obj in smart_manager.built_objs
        ], ['smart_manager_id', 'model_obj_id', 'model_obj_type_id'])
Example #4
0
 def sync_perm_sets(self, perm_sets):
     """
     Syncs the provided dictionary of perm sets to PermSet models.
     """
     sync(self.get_queryset(), [
         PermSet(name=name, display_name=config.get('display_name', ''))
         for name, config in perm_sets.items()
     ], ['name'], ['display_name'])
Example #5
0
 def sync_perms(self, perms):
     """
     Syncs the perms to Perm models.
     """
     sync(self.get_queryset(), [
         Perm(name=name, display_name=config.get('display_name', ''))
         for name, config in perms.items()
     ], ['name'], ['display_name'])
Example #6
0
 def sync_perm_sets(self, perm_sets):
     """
     Syncs the provided dictionary of perm sets to PermSet models.
     """
     sync(
         self.get_queryset(),
         [PermSet(name=name, display_name=config.get('display_name', '')) for name, config in perm_sets.items()],
         ['name'], ['display_name'])
Example #7
0
 def sync_perms(self, perms):
     """
     Syncs the perms to Perm models.
     """
     sync(
         self.get_queryset(),
         [Perm(name=name, display_name=config.get('display_name', '')) for name, config in perms.items()],
         ['name'], ['display_name'])
Example #8
0
 def _sync_entity_relationships(self):
     """
     After all entities have been synced, the entity relationships of every synced entity is still
     stored in the _entity_relationships_to_sync variable. Sync these relationships.
     """
     manager_utils.sync(
         EntityRelationship.objects.filter(
             sub_entity__in=self._entity_relationships_to_sync.keys()),
         chain(*self._entity_relationships_to_sync.values()),
         ['super_entity_id', 'sub_entity_id'])
Example #9
0
 def _sync_entity_relationships(self):
     """
     After all entities have been synced, the entity relationships of every synced entity is still
     stored in the _entity_relationships_to_sync variable. Sync these relationships.
     """
     manager_utils.sync(
         EntityRelationship.objects.filter(sub_entity__in=self._entity_relationships_to_sync.keys()),
         chain(*self._entity_relationships_to_sync.values()),
         ['super_entity_id', 'sub_entity_id']
     )
Example #10
0
    def update(self, **updates):
        """
        Takes a template of updates and updates the data schema. The following are possible keyword
        arguments.

        'model_content_type': The string path of a model that this schema represents (or None),
        'fieldschema_set': [{
            'field_key': The field key of the field,
            'display_name': The display name for the field (or the 'field_key' by default),
            'field_type': The FieldSchemaType type of the field,
            'uniqueness_order': The order this field is in the uniquessness constraint (or None by default),
            'field_position': The position of this field if it can be parsed by an array (or None by default),
            'field_format': The format of this field (or None by default),
            'default_value': The default value of this field (or None by default),
            'fieldoption_set': The set of options for the field schema (optional),
        }, {
            Additional field schemas...
        }]
        """
        if 'model_content_type' in updates:
            self.model_content_type = updates['model_content_type']

        self.save()

        if 'fieldschema_set' in updates:
            # Sync the field schema models
            sync(self.fieldschema_set.all(), [
                FieldSchema(
                    data_schema=self,
                    field_key=fs_values['field_key'],
                    display_name=fs_values.get('display_name', ''),
                    field_type=fs_values['field_type'],
                    uniqueness_order=fs_values.get('uniqueness_order', None),
                    field_position=fs_values.get('field_position', None),
                    field_format=fs_values.get('field_format', None),
                    default_value=fs_values.get('default_value', None),
                    has_options='fieldoption_set' in fs_values and fs_values['fieldoption_set'],
                )
                for fs_values in updates['fieldschema_set']
            ], ['field_key'], [
                'display_name', 'field_key', 'field_type', 'uniqueness_order', 'field_position',
                'field_format', 'default_value', 'has_options'
            ])

            # Sync the options of the field schema models if they are present
            for fs_values in updates['fieldschema_set']:
                if 'fieldoption_set' in fs_values:
                    fs = self.fieldschema_set.get(field_key=fs_values['field_key'])
                    sync(fs.fieldoption_set.all(), [
                        FieldOption(
                            field_schema=fs,
                            value=f_option,
                        )
                        for f_option in fs_values['fieldoption_set']
                    ], ['value'], ['value'])
Example #11
0
    def sync_perm_levels(self, perms):
        """
        Given a dictionary of perms that map to perm levels, sync the perm levels
        to PermLevel objects in the database.
        """
        perm_objs = {p.name: p for p in Perm.objects.all()}
        perm_levels = []
        for perm, perm_config in perms.items():
            assert(perm_config['levels'])
            for l, level_config in perm_config['levels'].items():
                perm_levels.append(
                    PermLevel(perm=perm_objs[perm], name=l, display_name=level_config.get('display_name', '')))

        sync(self.get_queryset(), perm_levels, ['name', 'perm'], ['display_name'])
    def save(self, *args, **kwargs):
        """
        Builds the objects managed by the template before saving the template.
        """
        super(SmartManager, self).save(*args, **kwargs)

        # Built the template
        smart_manager = import_by_path(self.smart_manager_class)(self.template)
        smart_manager.build()

        # Sync all of the objects from the built template
        sync(self.smartmanagerobject_set.all(), [
            SmartManagerObject(
                smart_manager=self,
                model_obj_id=built_obj.id,
                model_obj_type=ContentType.objects.get_for_model(built_obj, for_concrete_model=False),
            )
            for built_obj in smart_manager.built_objs
        ], ['smart_manager_id', 'model_obj_id', 'model_obj_type_id'])
Example #13
0
    def upsert_entity_relationships(self, queryset, entity_relationships):
        """
        Upsert entity relationships to the database
        :param queryset: The base queryset to use
        :param entity_relationships: The entity relationships to ensure exist in the database
        """

        # Select the relationships for update
        if entity_relationships:
            list(queryset.select_for_update().values_list(
                'id',
                flat=True
            ))

        # Sync the relationships
        return manager_utils.sync(
            queryset=queryset,
            model_objs=entity_relationships,
            unique_fields=['sub_entity_id', 'super_entity_id'],
            update_fields=[],
            return_upserts=True
        )
Example #14
0
    def upsert_entities(self, entities, sync=False):
        """
        Upsert a list of entities to the database
        :param entities: The entities to sync
        :param sync: Do a sync instead of an upsert
        """

        # Select the entities we are upserting for update to reduce deadlocks
        if entities:
            # Default select for update query when syncing all
            select_for_update_query = (
                'SELECT FROM {table_name} FOR NO KEY UPDATE'
            ).format(
                table_name=Entity._meta.db_table
            )
            select_for_update_query_params = []

            # If we are not syncing all, only select those we are updating
            if not sync:
                select_for_update_query = (
                    'SELECT FROM {table_name} WHERE (entity_type_id, entity_id) IN %s FOR NO KEY UPDATE'
                ).format(
                    table_name=Entity._meta.db_table
                )
                select_for_update_query_params = [tuple(
                    (entity.entity_type_id, entity.entity_id)
                    for entity in entities
                )]

            # Select the items for update
            with connection.cursor() as cursor:
                cursor.execute(select_for_update_query, select_for_update_query_params)

        # Compute the initial queryset and the initial state of the entities we are syncing
        # We need the initial state so we can compare it to the new state to determine any
        # entities that were activated or deactivated
        initial_queryset = Entity.all_objects.all()
        if not sync:
            initial_queryset = Entity.all_objects.extra(
                where=['(entity_type_id, entity_id) IN %s'],
                params=[tuple(
                    (entity.entity_type_id, entity.entity_id)
                    for entity in entities
                )]
            )
        initial_entity_activation_state = {
            entity[0]: entity[1]
            for entity in initial_queryset.values_list('id', 'is_active')
        }

        # Sync all the entities if the sync flag is passed
        if sync:
            upserted_entities = manager_utils.sync(
                queryset=initial_queryset,
                model_objs=entities,
                unique_fields=['entity_type_id', 'entity_id'],
                update_fields=['entity_kind_id', 'entity_meta', 'display_name', 'is_active'],
                return_upserts=True
            )
        # Otherwise we want to upsert our entities
        else:
            upserted_entities = manager_utils.bulk_upsert(
                queryset=initial_queryset,
                model_objs=entities,
                unique_fields=['entity_type_id', 'entity_id'],
                update_fields=['entity_kind_id', 'entity_meta', 'display_name', 'is_active'],
                return_upserts=True
            )

        # Compute the current state of the entities
        current_entity_activation_state = {
            entity.id: entity.is_active
            for entity in upserted_entities
        }

        # Computed the changed activation state of the entities
        changed_entity_activation_state = {}
        all_entity_ids = set(initial_entity_activation_state.keys()) | set(current_entity_activation_state.keys())
        for entity_id in all_entity_ids:
            # Get the initial activation state of the entity
            # Default to false so we only detect when the model has actually changed
            initial_activation_state = initial_entity_activation_state.get(entity_id, False)

            # Get the current state of the entity
            # Default to false here since the upserts do not return is_active=False due
            # to the default object manager excluding these
            current_activation_state = current_entity_activation_state.get(entity_id, False)

            # Check if the state changed and at it to the changed entity state
            if initial_activation_state != current_activation_state:
                changed_entity_activation_state[entity_id] = current_activation_state

        # Return the upserted entities
        return upserted_entities, changed_entity_activation_state