예제 #1
0
파일: handler.py 프로젝트: bgschust/baserow
    def get_token(self, user, token_id, base_queryset=None):
        """
        Fetches a single token and checks if the user belongs to the group.

        :param user: The user on whose behalf the token is requested.
        :type user: User
        :param token_id: The id of the requested token.
        :type token_id: int
        :param base_queryset: The base queryset from where to select the token
            object from. This can for example be used to do a `select_related`.
        :type base_queryset: Queryset
        :raises TokenDoesNotExist: Raised when the requested token was not found or
            if it does not belong to the user.
        :raises UserNotInGroupError: When the user does not belong to the group
            anymore.
        :return: The fetched token.
        :rtype: Token
        """

        if not base_queryset:
            base_queryset = Token.objects

        try:
            token = base_queryset.select_related('group').get(
                id=token_id,
                user=user
            )
        except Token.DoesNotExist:
            raise TokenDoesNotExist(f'The token with id {token_id} does not exist.')

        group = token.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        return token
예제 #2
0
    def get_filter(self, user, view_filter_id, base_queryset=None):
        """
        Returns an existing view filter by the given id.

        :param user: The user on whose behalf the view filter is requested.
        :type user: User
        :param view_filter_id: The id of the view filter.
        :type view_filter_id: int
        :param base_queryset: The base queryset from where to select the view filter
            object. This can for example be used to do a `select_related`.
        :type base_queryset: Queryset
        :raises ViewFilterDoesNotExist: The the requested view does not exists.
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :return: The requested view filter instance.
        :type: ViewFilter
        """

        if not base_queryset:
            base_queryset = ViewFilter.objects

        try:
            view_filter = base_queryset.select_related(
                'view__table__database__group').get(pk=view_filter_id)
        except ViewFilter.DoesNotExist:
            raise ViewFilterDoesNotExist(
                f'The view filter with id {view_filter_id} does not exist.')

        group = view_filter.view.table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        return view_filter
예제 #3
0
파일: handler.py 프로젝트: bgschust/baserow
    def get_row(self, user, table, row_id, model=None):
        """
        Fetches a single row from the provided table.

        :param user: The user of whose behalf the row is requested.
        :type user: User
        :param table: The table where the row must be fetched from.
        :type table: Table
        :param row_id: The id of the row that must be fetched.
        :type row_id: int
        :param model: If the correct model has already been generated it can be
            provided so that it does not have to be generated for a second time.
        :type model: Model
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :raises RowDoesNotExist: When the row with the provided id does not exist.
        :return: The requested row instance.
        :rtype: Model
        """

        if not model:
            model = table.get_model()

        group = table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        try:
            row = model.objects.get(id=row_id)
        except model.DoesNotExist:
            raise RowDoesNotExist(f'The row with id {row_id} does not exist.')

        return row
예제 #4
0
    def update_view(self, user, view, **kwargs):
        """
        Updates an existing view instance.

        :param user: The user on whose behalf the view is updated.
        :type user: User
        :param view: The view instance that needs to be updated.
        :type view: View
        :param kwargs: The fields that need to be updated.
        :type kwargs: object
        :raises ValueError: When the provided view not an instance of View.
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :return: The updated view instance.
        :rtype: View
        """

        if not isinstance(view, View):
            raise ValueError('The view is not an instance of View.')

        group = view.table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        view_type = view_type_registry.get_by_model(view)
        allowed_fields = ['name'] + view_type.allowed_fields
        view = set_allowed_attrs(kwargs, allowed_fields, view)
        view.save()

        return view
예제 #5
0
파일: handler.py 프로젝트: bgschust/baserow
    def delete_row(self, user, table, row_id):
        """
        Deletes an existing row of the given table and with row_id.

        :param user: The user of whose behalf the change is made.
        :type user: User
        :param table: The table for which the row must be deleted.
        :type table: Table
        :param row_id: The id of the row that must be deleted.
        :type row_id: int
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :raises RowDoesNotExist: When the row with the provided id does not exist.
        """

        group = table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        model = table.get_model(field_ids=[])

        try:
            row = model.objects.get(id=row_id)
        except model.DoesNotExist:
            raise RowDoesNotExist(f'The row with id {row_id} does not exist.')

        row_id = row.id
        row.delete()

        row_deleted.send(self, row_id=row_id, row=row, user=user, table=table,
                         model=model)
예제 #6
0
    def create_row(self, user, table, values=None, model=None):
        """
        Creates a new row for a given table with the provided values.

        :param user: The user of whose behalf the row is created.
        :type user: User
        :param table: The table for which to create a row for.
        :type table: Table
        :param values: The values that must be set upon creating the row. The keys must
            be the field ids.
        :type values: dict
        :param model: If a model is already generated it can be provided here to avoid
            having to generate the model again.
        :type model: Model
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :return: The created row instance.
        :rtype: Model
        """

        if not values:
            values = {}

        group = table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        if not model:
            model = table.get_model()

        kwargs = self.prepare_values(model._field_objects, values)
        return model.objects.create(**kwargs)
예제 #7
0
    def delete_field(self, user, field):
        """
        Deletes an existing field if it is not a primary field.

        :param user: The user on whose behalf the table is created.
        :type user: User
        :param field: The field instance that needs to be deleted.
        :type field: Field
        :raises ValueError: When the provided field is not an instance of Field.
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :raises CannotDeletePrimaryField: When we try to delete the primary field
            which cannot be deleted.
        """

        if not isinstance(field, Field):
            raise ValueError('The field is not an instance of Field')

        group = field.table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        if field.primary:
            raise CannotDeletePrimaryField(
                'Cannot delete the primary field of a '
                'table.')

        # Remove the field from the table schema.
        connection = connections[settings.USER_TABLE_DATABASE]
        with connection.schema_editor() as schema_editor:
            from_model = field.table.get_model(field_ids=[], fields=[field])
            model_field = from_model._meta.get_field(field.db_column)
            schema_editor.remove_field(from_model, model_field)

        field.delete()
예제 #8
0
파일: handler.py 프로젝트: bgschust/baserow
    def create_token(self, user, group, name):
        """
        Creates a new API token.

        :param user: The user of whose behalf the token is created.
        :type user: User
        :param group: The group object of which the token is related to.
        :type group: Group
        :param name: The name of the token.
        :type name: str
        :raises UserNotInGroupError: If the user does not belong to the group.
        :return: The created token instance.
        :rtype: Token
        """

        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        token = Token.objects.create(
            name=name,
            key=self.generate_unique_key(),
            user=user,
            group=group
        )

        # The newly created token should have access to all the tables in the group
        # when it is created.
        self.update_token_permissions(user, token, create=True, read=True, update=True,
                                      delete=True)

        return token
예제 #9
0
파일: handler.py 프로젝트: jechaviz/baserow
    def get_sort(self, user, view_sort_id):
        """
        Returns an existing view sort with the given id.

        :param user: The user on whose behalf the view sort is requested.
        :type user: User
        :param view_sort_id: The id of the view sort.
        :type view_sort_id: int
        :raises ViewSortDoesNotExist: The the requested view does not exists.
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :return: The requested view sort instance.
        :type: ViewSort
        """

        try:
            view_sort = ViewSort.objects.select_related(
                'view__table__database__group'
            ).get(
                pk=view_sort_id
            )
        except ViewSort.DoesNotExist:
            raise ViewSortDoesNotExist(
                f'The view sort with id {view_sort_id} does not exist.'
            )

        group = view_sort.view.table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        return view_sort
예제 #10
0
    def get_table(self, user, table_id):
        """
        Selects a table with a given id from the database.

        :param user: The user on whose behalf the table is requested.
        :type user: User
        :param table_id: The identifier of the table that must be returned.
        :type table_id: int
        :raises TableDoesNotExist: When the table with the provided id does not exist.
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :return: The requested table of the provided id.
        :rtype: Table
        """

        try:
            table = Table.objects.select_related('database__group').get(
                id=table_id)
        except Table.DoesNotExist:
            raise TableDoesNotExist(
                f'The table with id {table_id} doe not exist.')

        group = table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        return table
예제 #11
0
    def delete_table(self, user, table):
        """
        Deletes an existing table instance.

        :param user: The user on whose behalf the table is deleted.
        :type user: User
        :param table: The table instance that needs to be deleted.
        :type table: Table
        :raises ValueError: When the provided table is not an instance of Table.
        :raises UserNotInGroupError: When the user does not belong to the related group.
        """

        if not isinstance(table, Table):
            raise ValueError('The table is not an instance of Table')

        if not table.database.group.has_user(user):
            raise UserNotInGroupError(user, table.database.group)

        # Delete the table schema from the database.
        connection = connections[settings.USER_TABLE_DATABASE]
        with connection.schema_editor() as schema_editor:
            model = table.get_model()
            schema_editor.delete_model(model)

        table.delete()
예제 #12
0
    def update_table(self, user, table, **kwargs):
        """
        Updates an existing table instance.

        :param user: The user on whose behalf the table is updated.
        :type user: User
        :param table: The table instance that needs to be updated.
        :type table: Table
        :param kwargs: The fields that need to be updated.
        :type kwargs: object
        :raises ValueError: When the provided table is not an instance of Table.
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :return: The updated table instance.
        :rtype: Table
        """

        if not isinstance(table, Table):
            raise ValueError('The table is not an instance of Table')

        if not table.database.group.has_user(user):
            raise UserNotInGroupError(user, table.database.group)

        table = set_allowed_attrs(kwargs, ['name'], table)
        table.save()

        return table
예제 #13
0
    def create_view(self, user, table, type_name, **kwargs):
        """
        Creates a new view based on the provided type.

        :param user: The user on whose behalf the view is created.
        :type user: User
        :param table: The table that the view instance belongs to.
        :type table: Table
        :param type_name: The type name of the view.
        :type type_name: str
        :param kwargs: The fields that need to be set upon creation.
        :type kwargs: object
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :return: The created view instance.
        :rtype: View
        """

        group = table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        # Figure out which model to use for the given view type.
        view_type = view_type_registry.get(type_name)
        model_class = view_type.model_class
        allowed_fields = ['name'] + view_type.allowed_fields
        view_values = extract_allowed(kwargs, allowed_fields)
        last_order = model_class.get_last_order(table)

        instance = model_class.objects.create(table=table,
                                              order=last_order,
                                              **view_values)

        return instance
예제 #14
0
    def get_view(self, user, view_id, view_model=None):
        """
        Selects a view and checks if the user has access to that view. If everything
        is fine the view is returned.

        :param user: The user on whose behalf the view is requested.
        :type user: User
        :param view_id: The identifier of the view that must be returned.
        :type view_id: int
        :param view_model: If provided that models objects are used to select the
            view. This can for example be useful when you want to select a GridView or
            other child of the View model.
        :raises ViewDoesNotExist: When the view with the provided id does not exist.
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :type view_model: View
        :return:
        """

        if not view_model:
            view_model = View

        try:
            view = view_model.objects.select_related(
                'table__database__group').get(pk=view_id)
        except View.DoesNotExist:
            raise ViewDoesNotExist(
                f'The view with id {view_id} does not exist.')

        group = view.table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        return view
예제 #15
0
파일: handler.py 프로젝트: maktec/baserow
    def create_filter(self, user, view, field, type_name, value):
        """
        Creates a new view filter. The rows that are visible in a view should always
        be filtered by the related view filters.

        :param user: The user on whose behalf the view filter is created.
        :type user: User
        :param view: The view for which the filter needs to be created.
        :type: View
        :param field: The field that the filter should compare the value with.
        :type field: Field
        :param type_name: The filter type, allowed values are the types in the
            view_filter_type_registry `equal`, `not_equal` etc.
        :type type_name: str
        :param value: The value that the filter must apply to.
        :type value: str
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :raises ViewFilterNotSupported: When the provided view does not support
            filtering.
        :raises ViewFilterTypeNotAllowedForField: When the field does not support the
            filter type.
        :raises FieldNotInTable:  When the provided field does not belong to the
            provided view's table.
        :return: The created view filter instance.
        :rtype: ViewFilter
        """

        group = view.table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        # Check if view supports filtering
        view_type = view_type_registry.get_by_model(view.specific_class)
        if not view_type.can_filter:
            raise ViewFilterNotSupported(
                f'Filtering is not supported for {view_type.type} views.'
            )

        view_filter_type = view_filter_type_registry.get(type_name)
        field_type = field_type_registry.get_by_model(field.specific_class)

        # Check if the field is allowed for this filter type.
        if field_type.type not in view_filter_type.compatible_field_types:
            raise ViewFilterTypeNotAllowedForField(
                f'The view filter type {type_name} is not supported for field type '
                f'{field_type.type}.'
            )

        # Check if field belongs to the grid views table
        if not view.table.field_set.filter(id=field.pk).exists():
            raise FieldNotInTable(f'The field {field.pk} does not belong to table '
                                  f'{view.table.id}.')

        return ViewFilter.objects.create(
            view=view,
            field=field,
            type=view_filter_type.type,
            value=value
        )
예제 #16
0
    def create_sort(self, user, view, field, order):
        """
        Creates a new view sort.

        :param user: The user on whose behalf the view sort is created.
        :type user: User
        :param view: The view for which the sort needs to be created.
        :type: View
        :param field: The field that needs to be sorted.
        :type field: Field
        :param order: The desired order, can either be ascending (A to Z) or
            descending (Z to A).
        :type order: str
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :raises ViewSortNotSupported: When the provided view does not support sorting.
        :raises FieldNotInTable:  When the provided field does not belong to the
            provided view's table.
        :return: The created view sort instance.
        :rtype: ViewSort
        """

        group = view.table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        # Check if view supports sorting.
        view_type = view_type_registry.get_by_model(view.specific_class)
        if not view_type.can_sort:
            raise ViewSortNotSupported(
                f'Sorting is not supported for {view_type.type} views.')

        # Check if the field supports sorting.
        field_type = field_type_registry.get_by_model(field.specific_class)
        if not field_type.can_order_by:
            raise ViewSortFieldNotSupported(
                f'The field {field.pk} does not support '
                f'sorting.')

        # Check if field belongs to the grid views table
        if not view.table.field_set.filter(id=field.pk).exists():
            raise FieldNotInTable(
                f'The field {field.pk} does not belong to table '
                f'{view.table.id}.')

        # Check if the field already exists as sort
        if view.viewsort_set.filter(field_id=field.pk).exists():
            raise ViewSortFieldAlreadyExist(
                f'A sort with the field {field.pk} '
                f'already exists.')

        view_sort = ViewSort.objects.create(view=view,
                                            field=field,
                                            order=order)

        view_sort_created.send(self, view_sort=view_sort, user=user)

        return view_sort
예제 #17
0
    def create_field(self, user, table, type_name, primary=False, **kwargs):
        """
        Creates a new field with the given type for a table.

        :param user: The user on whose behalf the field is created.
        :type user: User
        :param table: The table that the field belongs to.
        :type table: Table
        :param type_name: The type name of the field. Available types can be found in
            the field_type_registry.
        :type type_name: str
        :param primary: Every table needs at least a primary field which cannot be
            deleted and is a representation of the whole row.
        :type primary: bool
        :param kwargs: The field values that need to be set upon creation.
        :type kwargs: object
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :raises PrimaryFieldAlreadyExists: When we try to create a primary field,
            but one already exists.
        :return: The created field instance.
        :rtype: Field
        """

        group = table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        # Because only one primary field per table can exist and we have to check if one
        # already exists. If so the field cannot be created and an exception is raised.
        if primary and Field.objects.filter(table=table,
                                            primary=True).exists():
            raise PrimaryFieldAlreadyExists(
                f'A primary field already exists for the '
                f'table {table}.')

        # Figure out which model to use and which field types are allowed for the given
        # field type.
        field_type = field_type_registry.get(type_name)
        model_class = field_type.model_class
        allowed_fields = ['name'] + field_type.allowed_fields
        field_values = extract_allowed(kwargs, allowed_fields)
        last_order = model_class.get_last_order(table)

        instance = model_class.objects.create(table=table,
                                              order=last_order,
                                              primary=primary,
                                              **field_values)

        # Add the field to the table schema.
        connection = connections[settings.USER_TABLE_DATABASE]
        with connection.schema_editor() as schema_editor:
            to_model = table.get_model(field_ids=[], fields=[instance])
            model_field = to_model._meta.get_field(instance.db_column)
            schema_editor.add_field(to_model, model_field)

        return instance
예제 #18
0
파일: handler.py 프로젝트: maktec/baserow
    def update_sort(self, user, view_sort, **kwargs):
        """
        Updates the values of an existing view sort.

        :param user: The user on whose behalf the view sort is updated.
        :type user: User
        :param view_sort: The view sort that needs to be updated.
        :type view_sort: ViewSort
        :param kwargs: The values that need to be updated, allowed values are
            `field` and `order`.
        :type kwargs: dict
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :raises FieldNotInTable: When the field does not support sorting.
        :return: The updated view sort instance.
        :rtype: ViewSort
        """

        group = view_sort.view.table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        field = kwargs.get('field', view_sort.field)
        order = kwargs.get('order', view_sort.order)

        # If the field has changed we need to check if the field belongs to the table.
        if (
            field.id != view_sort.field_id and
            not view_sort.view.table.field_set.filter(id=field.pk).exists()
        ):
            raise FieldNotInTable(f'The field {field.pk} does not belong to table '
                                  f'{view_sort.view.table.id}.')

        # If the field has changed we need to check if the new field type supports
        # sorting.
        field_type = field_type_registry.get_by_model(field.specific_class)
        if (
            field.id != view_sort.field_id and
            not field_type.can_order_by
        ):
            raise ViewSortFieldNotSupported(f'The field {field.pk} does not support '
                                            f'sorting.')

        # If the field has changed we need to check if the new field doesn't already
        # exist as sort.
        if (
            field.id != view_sort.field_id and
            view_sort.view.viewsort_set.filter(field_id=field.pk).exists()
        ):
            raise ViewSortFieldAlreadyExist(f'A sort with the field {field.pk} '
                                            f'already exists.')

        view_sort.field = field
        view_sort.order = order
        view_sort.save()

        return view_sort
예제 #19
0
    def get_table(user, table_id):
        table = get_object_or_404(
            Table.objects.select_related('database__group'),
            id=table_id
        )

        group = table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        return table
예제 #20
0
파일: handler.py 프로젝트: maktec/baserow
    def update_row(self, user, table, row_id, values, model=None):
        """
        Updates one or more values of the provided row_id.

        :param user: The user of whose behalf the change is made.
        :type user: User
        :param table: The table for which the row must be updated.
        :type table: Table
        :param row_id: The id of the row that must be updated.
        :type row_id: int
        :param values: The values that must be updated. The keys must be the field ids.
        :type values: dict
        :param model: If the correct model has already been generated it can be
            provided so that it does not have to be generated for a second time.
        :type model: Model
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :raises RowDoesNotExist: When the row with the provided id does not exist.
        :return: The updated row instance.
        :rtype: Model
        """

        group = table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        if not model:
            field_ids = self.extract_field_ids_from_dict(values)
            model = table.get_model(field_ids=field_ids)

        # Because it is possible to have a different database for the user tables we
        # need to start another transaction here, otherwise it is not possible to use
        # the select_for_update function.
        with transaction.atomic(settings.USER_TABLE_DATABASE):
            try:
                row = model.objects.select_for_update().get(id=row_id)
            except model.DoesNotExist:
                raise RowDoesNotExist(
                    f'The row with id {row_id} does not exist.')

            values = self.prepare_values(model._field_objects, values)
            values, manytomany_values = self.extract_manytomany_values(
                values, model)

            for name, value in values.items():
                setattr(row, name, value)

            row.save()

            for name, value in manytomany_values.items():
                getattr(row, name).set(value)

        return row
예제 #21
0
파일: handler.py 프로젝트: maktec/baserow
    def update_filter(self, user, view_filter, **kwargs):
        """
        Updates the values of an existing view filter.

        :param user: The user on whose behalf the view filter is updated.
        :type user: User
        :param view_filter: The view filter that needs to be updated.
        :type view_filter: ViewFilter
        :param kwargs: The values that need to be updated, allowed values are
            `field`, `value` and `type_name`.
        :type kwargs: dict
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :raises ViewFilterTypeNotAllowedForField: When the field does not supports the
            filter type.
        :raises FieldNotInTable: When the provided field does not belong to the
            view's table.
        :return: The updated view filter instance.
        :rtype: ViewFilter
        """

        group = view_filter.view.table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        type_name = kwargs.get('type_name', view_filter.type)
        field = kwargs.get('field', view_filter.field)
        value = kwargs.get('value', view_filter.value)
        view_filter_type = view_filter_type_registry.get(type_name)
        field_type = field_type_registry.get_by_model(field.specific_class)

        # Check if the field is allowed for this filter type.
        if field_type.type not in view_filter_type.compatible_field_types:
            raise ViewFilterTypeNotAllowedForField(
                f'The view filter type {type_name} is not supported for field type '
                f'{field_type.type}.'
            )

        # If the field has changed we need to check if the field belongs to the table.
        if (
            field.id != view_filter.field_id and
            not view_filter.view.table.field_set.filter(id=field.pk).exists()
        ):
            raise FieldNotInTable(f'The field {field.pk} does not belong to table '
                                  f'{view_filter.view.table.id}.')

        view_filter.field = field
        view_filter.value = value
        view_filter.type = type_name
        view_filter.save()

        return view_filter
예제 #22
0
파일: handler.py 프로젝트: maktec/baserow
    def delete_sort(self, user, view_sort):
        """
        Deletes an existing view sort.

        :param user: The user on whose behalf the view sort is deleted.
        :type user: User
        :param view_sort: The view sort instance that needs to be deleted.
        :type view_sort: ViewSort
        :raises UserNotInGroupError: When the user does not belong to the related group.
        """

        group = view_sort.view.table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        view_sort.delete()
예제 #23
0
파일: handler.py 프로젝트: bgschust/baserow
    def delete_field(self, user, field):
        """
        Deletes an existing field if it is not a primary field.

        :param user: The user on whose behalf the table is created.
        :type user: User
        :param field: The field instance that needs to be deleted.
        :type field: Field
        :raises ValueError: When the provided field is not an instance of Field.
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :raises CannotDeletePrimaryField: When we try to delete the primary field
            which cannot be deleted.
        """

        if not isinstance(field, Field):
            raise ValueError('The field is not an instance of Field')

        group = field.table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        if field.primary:
            raise CannotDeletePrimaryField(
                'Cannot delete the primary field of a '
                'table.')

        field = field.specific
        field_type = field_type_registry.get_by_model(field)

        # Remove the field from the table schema.
        connection = connections[settings.USER_TABLE_DATABASE]
        with connection.schema_editor() as schema_editor:
            from_model = field.table.get_model(field_ids=[], fields=[field])
            model_field = from_model._meta.get_field(field.db_column)
            schema_editor.remove_field(from_model, model_field)

        field_id = field.id
        field.delete()

        # After the field is deleted we are going to to call the after_delete method of
        # the field type because some instance cleanup might need to happen.
        field_type.after_delete(field, from_model, user, connection)

        field_deleted.send(self, field_id=field_id, field=field, user=user)
예제 #24
0
    def create_table(self, user, database, fill_initial=False, **kwargs):
        """
        Creates a new table and a primary text field.

        :param user: The user on whose behalf the table is created.
        :type user: User
        :param database: The database that the table instance belongs to.
        :type database: Database
        :param fill_initial: Indicates whether an initial view, some fields and
            some rows should be added.
        :type fill_initial: bool
        :param kwargs: The fields that need to be set upon creation.
        :type kwargs: object
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :return: The created table instance.
        :rtype: Table
        """

        if not database.group.has_user(user):
            raise UserNotInGroupError(user, database.group)

        table_values = extract_allowed(kwargs, ['name'])
        last_order = Table.get_last_order(database)
        table = Table.objects.create(database=database,
                                     order=last_order,
                                     **table_values)

        # Create a primary text field for the table.
        TextField.objects.create(table=table,
                                 order=0,
                                 primary=True,
                                 name='Name')

        # Create the table schema in the database database.
        connection = connections[settings.USER_TABLE_DATABASE]
        with connection.schema_editor() as schema_editor:
            model = table.get_model()
            schema_editor.create_model(model)

        if fill_initial:
            self.fill_initial_table_data(user, table)

        return table
예제 #25
0
    def delete_view(self, user, view):
        """
        Deletes an existing view instance.

        :param user: The user on whose behalf the view is deleted.
        :type user: User
        :param view: The view instance that needs to be deleted.
        :type view: View
        :raises ViewDoesNotExist: When the view with the provided id does not exist.
        :raises UserNotInGroupError: When the user does not belong to the related group.
        """

        if not isinstance(view, View):
            raise ValueError('The view is not an instance of View')

        group = view.table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        view.delete()
예제 #26
0
파일: handler.py 프로젝트: bgschust/baserow
    def get_field(self, user, field_id, field_model=None, base_queryset=None):
        """
        Selects a field with a given id from the database.

        :param user: The user on whose behalf the field is requested.
        :type user: User
        :param field_id: The identifier of the field that must be returned.
        :type field_id: int
        :param field_model: If provided that model's objects are used to select the
            field. This can for example be useful when you want to select a TextField or
            other child of the Field model.
        :type field_model: Field
        :param base_queryset: The base queryset from where to select the field.
            object. This can for example be used to do a `select_related`. Note that
            if this is used the `field_model` parameter doesn't work anymore.
        :type base_queryset: Queryset
        :raises FieldDoesNotExist: When the field with the provided id does not exist.
        :raises UserNotInGroupError: When the user does not belong to the field.
        :return: The requested field instance of the provided id.
        :rtype: Field
        """

        if not field_model:
            field_model = Field

        if not base_queryset:
            base_queryset = field_model.objects

        try:
            field = base_queryset.select_related('table__database__group').get(
                id=field_id)
        except Field.DoesNotExist:
            raise FieldDoesNotExist(
                f'The field with id {field_id} does not exist.')

        group = field.table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        return field
예제 #27
0
    def delete_filter(self, user, view_filter):
        """
        Deletes an existing view filter.

        :param user: The user on whose behalf the view filter is deleted.
        :type user: User
        :param view_filter: The view filter instance that needs to be deleted.
        :type view_filter: ViewFilter
        :raises UserNotInGroupError: When the user does not belong to the related group.
        """

        group = view_filter.view.table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        view_filter_id = view_filter.id
        view_filter.delete()

        view_filter_deleted.send(self,
                                 view_filter_id=view_filter_id,
                                 view_filter=view_filter,
                                 user=user)
예제 #28
0
파일: handler.py 프로젝트: bgschust/baserow
    def update_field_select_options(self, user, field, select_options):
        """
        Brings the select options in the desired provided state in a query efficient
        manner.

        Example: select_options = [
            {'id': 1, 'value': 'Option 1', 'color': 'blue'},
            {'value': 'Option 2', 'color': 'red'}
        ]

        :param user: The user on whose behalf the change is made.
        :type user: User
        :param field: The field of which the select options must be updated.
        :type field: Field
        :param select_options: A list containing dicts with the desired select options.
        :type select_options: list
        :raises UserNotInGroupError: When the user does not belong to the related group.
        """

        group = field.table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        existing_select_options = field.select_options.all()

        # Checks which option ids must be selected by comparing the existing ids with
        # the provided ids.
        to_delete = [
            existing.id for existing in existing_select_options
            if existing.id not in
            [desired['id'] for desired in select_options if 'id' in desired]
        ]

        if len(to_delete) > 0:
            SelectOption.objects.filter(field=field, id__in=to_delete).delete()

        # Checks which existing instances must be fetched using a single query.
        to_select = [
            select_option['id'] for select_option in select_options
            if 'id' in select_option
        ]

        if len(to_select) > 0:
            for existing in field.select_options.filter(id__in=to_select):
                for select_option in select_options:
                    if select_option.get('id') == existing.id:
                        select_option['instance'] = existing

        to_create = []

        for order, select_option in enumerate(select_options):
            if 'instance' in select_option:
                instance = select_option['instance']
                instance.order = order
                instance.value = select_option['value']
                instance.color = select_option['color']
                instance.save()
            else:
                to_create.append(
                    SelectOption(field=field,
                                 order=order,
                                 value=select_option['value'],
                                 color=select_option['color']))

        if len(to_create) > 0:
            SelectOption.objects.bulk_create(to_create)
예제 #29
0
파일: handler.py 프로젝트: bgschust/baserow
    def update_field(self, user, field, new_type_name=None, **kwargs):
        """
        Updates the values of the given field, if provided it is also possible to change
        the type.

        :param user: The user on whose behalf the table is updated.
        :type user: User
        :param field: The field instance that needs to be updated.
        :type field: Field
        :param new_type_name: If the type needs to be changed it can be provided here.
        :type new_type_name: str
        :param kwargs: The field values that need to be updated
        :type kwargs: object
        :raises ValueError: When the provided field is not an instance of Field.
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :raises CannotChangeFieldType: When the database server responds with an
            error while trying to change the field type. This should rarely happen
            because of the lenient schema editor, which replaces the value with null
            if it ould not be converted.
        :return: The updated field instance.
        :rtype: Field
        """

        if not isinstance(field, Field):
            raise ValueError('The field is not an instance of Field.')

        group = field.table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        old_field = deepcopy(field)
        field_type = field_type_registry.get_by_model(field)
        old_field_type = field_type
        from_model = field.table.get_model(field_ids=[], fields=[field])
        from_field_type = field_type.type

        # If the provided field type does not match with the current one we need to
        # migrate the field to the new type. Because the type has changed we also need
        # to remove all view filters.
        if new_type_name and field_type.type != new_type_name:
            field_type = field_type_registry.get(new_type_name)

            if field.primary and not field_type.can_be_primary_field:
                raise IncompatiblePrimaryFieldTypeError(new_type_name)

            new_model_class = field_type.model_class
            field.change_polymorphic_type_to(new_model_class)

            # If the field type changes it could be that some dependencies,
            # like filters or sortings need to be changed.
            ViewHandler().field_type_changed(field)

        allowed_fields = ['name'] + field_type.allowed_fields
        field_values = extract_allowed(kwargs, allowed_fields)

        field_values = field_type.prepare_values(field_values, user)
        before = field_type.before_update(old_field, field_values, user)

        field = set_allowed_attrs(field_values, allowed_fields, field)
        field.save()

        connection = connections[settings.USER_TABLE_DATABASE]

        # If no converter is found we are going to convert to field using the
        # lenient schema editor which will alter the field's type and set the data
        # value to null if it can't be converted.
        to_model = field.table.get_model(field_ids=[], fields=[field])
        from_model_field = from_model._meta.get_field(field.db_column)
        to_model_field = to_model._meta.get_field(field.db_column)

        # Before a field is updated we are going to call the before_schema_change
        # method of the old field because some cleanup of related instances might
        # need to happen.
        old_field_type.before_schema_change(old_field, field, from_model,
                                            to_model, from_model_field,
                                            to_model_field, user)

        # Try to find a data converter that can be applied.
        converter = field_converter_registry.find_applicable_converter(
            from_model, old_field, field)

        if converter:
            # If a field data converter is found we are going to use that one to alter
            # the field and maybe do some data conversion.
            converter.alter_field(old_field, field, from_model, to_model,
                                  from_model_field, to_model_field, user,
                                  connection)
        else:
            # If no field converter is found we are going to alter the field using the
            # the lenient schema editor.
            with lenient_schema_editor(
                    connection,
                    old_field_type.get_alter_column_prepare_value(
                        connection, old_field, field),
                    field_type.get_alter_column_type_function(
                        connection, old_field, field)) as schema_editor:
                try:
                    schema_editor.alter_field(from_model, from_model_field,
                                              to_model_field)
                except (ProgrammingError, DataError) as e:
                    # If something is going wrong while changing the schema we will
                    # just raise a specific exception. In the future we want to have
                    # some sort of converter abstraction where the values of certain
                    # types can be converted to another value.
                    logger.error(str(e))
                    message = f'Could not alter field when changing field type ' \
                              f'{from_field_type} to {new_type_name}.'
                    raise CannotChangeFieldType(message)

        from_model_field_type = from_model_field.db_parameters(
            connection)['type']
        to_model_field_type = to_model_field.db_parameters(connection)['type']
        altered_column = from_model_field_type != to_model_field_type

        # If the new field doesn't support select options we can delete those
        # relations.
        if (old_field_type.can_have_select_options
                and not field_type.can_have_select_options):
            old_field.select_options.all().delete()

        field_type.after_update(old_field, field, from_model, to_model, user,
                                connection, altered_column, before)

        field_updated.send(self, field=field, user=user)

        return field
예제 #30
0
파일: handler.py 프로젝트: bgschust/baserow
    def create_row(self, user, table, values=None, model=None, before=None):
        """
        Creates a new row for a given table with the provided values.

        :param user: The user of whose behalf the row is created.
        :type user: User
        :param table: The table for which to create a row for.
        :type table: Table
        :param values: The values that must be set upon creating the row. The keys must
            be the field ids.
        :type values: dict
        :param model: If a model is already generated it can be provided here to avoid
            having to generate the model again.
        :type model: Model
        :param before: If provided the new row will be placed right before that row
            instance.
        :type before: Table
        :raises UserNotInGroupError: When the user does not belong to the related group.
        :return: The created row instance.
        :rtype: Model
        """

        if not values:
            values = {}

        group = table.database.group
        if not group.has_user(user):
            raise UserNotInGroupError(user, group)

        if not model:
            model = table.get_model()

        values = self.prepare_values(model._field_objects, values)
        values, manytomany_values = self.extract_manytomany_values(values, model)

        if before:
            # Here we calculate the order value, which indicates the position of the
            # row, by subtracting a fraction of the row that it must be placed
            # before. The same fraction is also going to be subtracted from the other
            # rows that have been placed before. By using these fractions we don't
            # have to re-order every row in the table.
            change = Decimal('0.00000000000000000001')
            values['order'] = before.order - change
            model.objects.filter(
                order__gt=floor(values['order']),
                order__lte=values['order']
            ).update(order=F('order') - change)
        else:
            # Because the row is by default added as last, we have to figure out what
            # the highest order is and increase that by one. Because the order of new
            # rows should always be a whole number we round it up.
            values['order'] = ceil(
                model.objects.aggregate(max=Max('order')).get('max') or Decimal('0')
            ) + 1

        instance = model.objects.create(**values)

        for name, value in manytomany_values.items():
            getattr(instance, name).set(value)

        row_created.send(self, row=instance, before=before, user=user, table=table,
                         model=model)

        return instance