Update queries

Queryable properties offer the option to use the names of properties in batch updates (i.e. when using the update method of querysets). To achieve this, the update argument for a queryable property will simply be translated into update values for actual model fields.

Implementation

Let’s use the version_str of the ApplicationVersion model as an example once again.

To allow the usage of this queryable property in queryset updates using the decorator-based approach, the property’s updater method must be used.

from queryable_properties.properties import queryable_property


class ApplicationVersion(models.Model):
    ...
    
    @queryable_property
    def version_str(self):
        """Return the combined version info as a string."""
        return '{major}.{minor}'.format(major=self.major, minor=self.minor)
    
    @version_str.updater
    @classmethod
    def version_str(cls, value):
        # Don't implement any validation to keep the example simple.
        major, minor = value.split('.')
        return {'major': major, 'minor': minor}

Note

The classmethod decorator is not required, but makes the function look more natural since it takes the model class as its first argument.

Using the class-based approach, the same thing can be achieved by implementing the get_update_kwargs method of the property class. It is recommended to use the UpdateMixin for class-based queryable properties that are supposed to be used in queryset updates because it defines the actual stub for the get_update_kwargs method. However, using this mixin is not required - a queryable property can be used for queryset updates as long as the get_update_kwargs method is implemented correctly.

from queryable_properties.properties import QueryableProperty, UpdateMixin


class VersionStringProperty(UpdateMixin, QueryableProperty):

    def get_value(self, obj):
        """Return the combined version info as a string."""
        return '{major}.{minor}'.format(major=obj.major, minor=obj.minor)
    
    def get_update_kwargs(self, cls, value):
        # Don't implement any validation to keep the example simple.
        major, minor = value.split('.')
        return {'major': major, 'minor': minor}

In both cases, the function/method to implement takes 2 arguments:

  • cls: The model class. Mainly useful to implement custom logic in inheritance scenarios.
  • value: The value to update the database rows with.

Using either approach, the function/method is expected to return a dict object that contains the model field/value combinations that are actually required to perform the update correctly.

Note

The returned dict object may contain name/value pairs referring to other queryable properties on the same model, which will be resolved accordingly in the same manner.

Usage

With both implementations, the queryable property can be used in queryset updates like this:

ApplicationVersion.objects.update(version_str='1.1')

The specified value is then translated into actual field values by the implemented function/method and the real, underlying update call will take place with these values.

Limitations

Expression-based update values

Using expression-based values (like F objects or conditional updates) are generally not supported when updating via a queryable property. This is because the queryable property updater is simply a preprocessor for the .update(...) keyword arguments on the python side, while expression-based updates rely on other values in the query, which are only evaluated in SQL when the query actually runs.

However, django-queryable-properties doesn’t technically prevent to use expressions as update values. This means that if an expression is used as an update value, it will be passed through to the method decorated with updater (decorator-based approach) or the get_update_kwargs implementation (class-based approach). Therefore it would technically be possible to process an expression in the updater’s implementation as long the expression can be preprocessed in a sensible way before the query runs.