django_auxilium.models.signals module

class django_auxilium.models.signals.AutoSignals(getter=u'get_signals', signal_pool=None)[source]

Bases: django_auxilium.utils.functools.decorators.Decorator

Decorator for automatically attaching signals to the model class.

Usual Django convention is to include all signals in signals.py. That works really well in most cases however here are potential disadvantages:

  • it couples logic to a model class in different Python module
  • signals need to be imported somewhere in app initialization process

These issues can be resolved by moving the signal receivers to models.py however some issues with that are:

  • signals need to be defined after model definitions
  • if there are many models, it could be hard to find associated signals

This decorator attempts to solve that by moving the signal receiver definitions to the model itself which are retrieved via class method get_signals. It allows to tightly couple model signal receivers within the model which in some cases might be better compared to using signals.py module.

get_signals expected to return a list of signals which should be attached to the model class. That could either be achieved by returning either receiver handler callables or signal dict parameters. Here is a description of what each of them can do:

Callables:

An example:

>>> import random
>>> @auto_signals
... class MyModel(models.Model):
...     class Meta(object):
...         app_label = str(random.randrange(1000, 2000))
...     @staticmethod
...     def do_pre_save(sender, instance, *args, **kwargs):
...         print('triggered pre_save')
...     @staticmethod
...     def do_post_save(sender, instance, *args, **kwargs):
...         print('triggered post_save')
...     @classmethod
...     def get_signals(cls):
...         return [cls.do_pre_save, cls.do_post_save]

When get_signals returns a list of signal receivers, each receiver function name should contain the signal name to which it needs to connect to. By default this decorator supports all Django model signals. If different signals need to be supported, please use signal_pool parameter.

Dictionary:

An example:

>>> import random
>>> @auto_signals
... class MyModel(models.Model):
...     class Meta(object):
...         app_label = str(random.randrange(1000, 2000))
...     @classmethod
...     def get_signals(cls):
...         def so_something(sender, instance, *args, **kwargs):
...             print('doing something')
...         def do_something_else_post_save(sender, instance, *args, **kwargs):
...             print('doing something else')
...         return [
...             {
...                 'receiver': so_something,
...                 'weak': False,
...                 'dispatch_uid': 'my_signal_uuid',
...                 'signal': 'pre_save',
...             },
...             {
...                 'receiver': do_something_else_post_save,
...                 'weak': False,
...                 'dispatch_uid': 'my_other_signal_uuid',
...             },
...         ]

When get_signals returns a list of signal dictionaries, each dict should be parameters which will be passed to Django’s Signal.connect. The only exception is the optional signal key which when given should either be a name of the supported signal as per signal_pool or actual signal object. When not provided, this decorator will figure out the signal from the receiver’s name, same as when get_signals returns a list of callables as described above.

Parameters:
  • getter (str, optional) – Name of the class method on the decorated model which should return signals to be connected
  • signal_pool (dict, optional) – Dictionary of supported signals. Keys should be signal names and values should be actual signal objects. By default all model signals are supported.
connect_signal(signal)[source]

Connect individual signal to the decorated model

Parameters:signal (dict, function) – Either a dict or a function of the receiver signal handler. For more information about how each of those are handled, please AutoSignals description.
connect_signals()[source]

Connect all signals as returned by the signal getter on the decorated model

See also

connect_signal()

get_wrapped_object()[source]

Return the given model

Since signals are connected externally of a model class, the model class is not modified.

validate_model()[source]

Validate that the decorated object is a valid Django model class and that it implements signal getter.

class django_auxilium.models.signals.FieldSpec(name, field)

Bases: tuple

Field specification which is used by file descriptors to reference the field name and field object itself during decorator operations.

field

Alias for field number 1

name

Alias for field number 0

class django_auxilium.models.signals.FileFieldAutoChangeDelete(*args, **kwargs)[source]

Bases: django_auxilium.models.signals.FileFieldAutoDelete

Model decorator which automatically setups all the necessary signals to automatically remove files associated with given fields when they change on model save.

Unlike FileFieldAutoDelete which removed file fields on model deletion, this decorator removes files if they are changed. The premise is that if the file field is editable if it is changed either in Django Admin or in custom logic, unless custom logic is applied, by default Django would not remove the old file hence over time many orphan files can accumulate on the server. Locally it might not be a big deal however when external (especially paid) storage backend is used, extra files can make a real difference over time. This decorator automatically handles that and removes old files when they are changed.

In order to do that however model has to track when files change on the model and Django models do not do that out of the box. You can either implement that functionality yourself on the model by implementing the following methods:

  • is_dirty() - should return a bool if any of the model fields have changed. Usually this will require to track model fields during __init__ which will be used to initialize model instance when querying model from db and then comparing those values with the new values when this method is called.
  • get_dirty_fields() - should return a dict where the keys are the field names and the values are old field values.

Alternatively you can use a third-party package which implements that API. This decorator is tested with django-dirtyfields.

Warning

As mentioned above, this decorator requires is_dirty() and get_dirty_fields() to be implemented on the model. Even though we recommend to use django-dirtyfields for that functionality, this library does not ship with django-dirtyfields pre-installed and you will need to install it independently.

Note

This decorator does not remove files on delete as well. If you would like to both remove files on both delete and change you will need to apply both FileFieldAutoDelete and this decorator at the same time.

Examples

>>> import random
>>> from dirtyfields import DirtyFieldsMixin

>>> @file_field_auto_change_delete  # equivalent to @file_field_auto_change_delete('*')
... class FooModel(DirtyFieldsMixin, models.Model):
...     class Meta(object):
...         app_label = str(random.randrange(1000, 2000))
...     file = models.FileField(upload_to='foo')

>>> @file_field_auto_change_delete('file')
... class FooModel(DirtyFieldsMixin, models.Model):
...     class Meta(object):
...         app_label = str(random.randrange(1000, 2000))
...     file = models.FileField(upload_to='foo')

>>> # remote both on delete and change
>>> @file_field_auto_delete
... @file_field_auto_change_delete
... class FooModel(DirtyFieldsMixin, models.Model):
...     class Meta(object):
...         app_label = str(random.randrange(1000, 2000))
...     file = models.FileField(upload_to='foo')
Parameters:
  • fields (str, list) – Same as FileFieldAutoDelete fields parameter
  • signal (Signal, optional) – Same as FileFieldAutoDelete signal parameter. By default is post_save signal.
  • signal_name_pattern (str, optional) – The name given to the signal function which will automatically remove the file. This can be a string formatted string. Into it, two parameters will be passed which are model and field. model is a model class (not instance) to which the signal is being connected to and field is a name of the file field (or '_' delimited field names if multiple fields are given). The default pattern is post_save_{model.__name__}_delete_{field} which results in patterns like 'post_save_Model_delete_file_field'. The reason why this pattern might be useful is because it can be used to disconnect the signal receiver at a later time.
get_signal_function()[source]

Get the actual function which will be connected to the Django’s signal which conforms to the post_save signal signature:

def receiver(sender, instance, *args, **kwargs): pass
validate_model()[source]

Validate that the model is a valid Django model and that it implements necessary API in order to track that model fields have changed before saving.

class django_auxilium.models.signals.FileFieldAutoDelete(fields=u'*', signal=None, signal_name_pattern=None)[source]

Bases: django_auxilium.utils.functools.decorators.Decorator

Model decorator which automatically setups all the necessary signals to automatically remove files associated with given fields when model instance is removed.

Starting with Django 1.3 when model records are deleted via querysets (e.g. model.objects.filter(...).delete()), the model’s delete() method is no longer called. As a consequence, if the model has any file fields, those files will not be removed even if it is specified in the delete() method. The new way to remove files associated with a model is to use Django signals framework, or more specifically the post_delete signal. This decorator automatically connects the post_delete signal which will remove the file associated with the specified file field.

More about this can be found at Django 1.3 release notes.

Examples

>>> import random
>>> @file_field_auto_delete  # equivalent to @file_field_auto_delete('*')
... class FooModel(models.Model):
...     class Meta(object):
...         app_label = str(random.randrange(1000, 2000))
...     file = models.FileField(upload_to='foo')

>>> import random
>>> @file_field_auto_delete('file')
... class FooModel(models.Model):
...     class Meta(object):
...         app_label = str(random.randrange(1000, 2000))
...     file = models.FileField(upload_to='foo')
Parameters:
  • fields (str, list) – Either a single file field which should be removed on delete or a list of fields. Also '*' can be used in order to delete all FileField on the model.
  • signal (Signal, optional) – Djagno signal class which should be used to attach the receiver signal handler to. By default is post_delete signal.
  • signal_name_pattern (str, optional) – The name given to the signal function which will automatically remove the file. This can be a string formatted string. Into it, two parameters will be passed which are model and field. model is a model class (not instance) to which the signal is being connected to and field is a name of the file field (or '_' delimited field names if multiple fields are given). The default pattern is post_delete_{model.__name__}_delete_{field} which results in patterns like 'post_delete_Model_delete_file_field'. The reason why this pattern might be useful is because it can be used to disconnect the signal receiver at a later time.
field_names

list – List of normalized field names which should be removed on change

fields

list – List of validated FieldSpec which signal will use to remove changed files

signal_name_pattern

str – Same as the signal_name_pattern parameter

connect_signal_function()[source]

Connect the signal as returned by get_signal_function() into the Django’s signal’s framework.

get_field_by_name(name)[source]

Get a Django model field for the given field name from the decorated model

Parameters:name (str) – Name of the field to get
Returns:Django model field instance
Return type:Field
get_field_names()[source]

Get list of field names to be removed in the created signal

This method is primarily used to normalize the list of fields in case it is provided as a string, list or a wildcard '*'.

Returns:List of field names which should be removed in the created signal
Return type:list
get_fields()[source]

Get list of FieldSpec for all the fields to be removed

get_signal_function()[source]

Get the actual function which will be connected to the Django’s signal which conforms to the post_delete signal signature:

def receiver(sender, instance, *args, **kwargs): pass
get_signal_name()[source]

Using the signal_name_pattern pattern, return the formatted signal name.

Returns:name – The name of the signal
Return type:str
get_wrapped_object()[source]

Return the given model

Since signals are connected externally of a model class, the model class is not modified.

validate_field(name)[source]

Validate a single field on the model

This method is expected to be called after validate_model() and it assumes that self.to_wrap is a valid Django model. It also validates that the given field name is present in the model and that it subclasses Django’s FileField field class.

Raises:
  • AttributeError – When the field cannot be found on the model
  • TypeError – When the field is not a FileField
validate_fields()[source]

Validate all the fields on the model

This method is expected to be called after validate_model() and it assumes that self.to_wrap is a valid Django model. This method validates that the given fields are present in the model and that they are a subclass of Django’s FileField field class.

See also

validate_field()

validate_model()[source]

Validate the input to the decorator which is suppose to be a model

Make sure the given class is a subclass of Django’s Model class.

Raises:TypeError – If to_wrap is either not a class or not a Django’s model.Model class.
django_auxilium.models.signals.auto_signals(*args, **kwargs)

Decorator for automatically attaching signals to the model class.

Usual Django convention is to include all signals in signals.py. That works really well in most cases however here are potential disadvantages:

  • it couples logic to a model class in different Python module
  • signals need to be imported somewhere in app initialization process

These issues can be resolved by moving the signal receivers to models.py however some issues with that are:

  • signals need to be defined after model definitions
  • if there are many models, it could be hard to find associated signals

This decorator attempts to solve that by moving the signal receiver definitions to the model itself which are retrieved via class method get_signals. It allows to tightly couple model signal receivers within the model which in some cases might be better compared to using signals.py module.

get_signals expected to return a list of signals which should be attached to the model class. That could either be achieved by returning either receiver handler callables or signal dict parameters. Here is a description of what each of them can do:

Callables:

An example:

>>> import random
>>> @auto_signals
... class MyModel(models.Model):
...     class Meta(object):
...         app_label = str(random.randrange(1000, 2000))
...     @staticmethod
...     def do_pre_save(sender, instance, *args, **kwargs):
...         print('triggered pre_save')
...     @staticmethod
...     def do_post_save(sender, instance, *args, **kwargs):
...         print('triggered post_save')
...     @classmethod
...     def get_signals(cls):
...         return [cls.do_pre_save, cls.do_post_save]

When get_signals returns a list of signal receivers, each receiver function name should contain the signal name to which it needs to connect to. By default this decorator supports all Django model signals. If different signals need to be supported, please use signal_pool parameter.

Dictionary:

An example:

>>> import random
>>> @auto_signals
... class MyModel(models.Model):
...     class Meta(object):
...         app_label = str(random.randrange(1000, 2000))
...     @classmethod
...     def get_signals(cls):
...         def so_something(sender, instance, *args, **kwargs):
...             print('doing something')
...         def do_something_else_post_save(sender, instance, *args, **kwargs):
...             print('doing something else')
...         return [
...             {
...                 'receiver': so_something,
...                 'weak': False,
...                 'dispatch_uid': 'my_signal_uuid',
...                 'signal': 'pre_save',
...             },
...             {
...                 'receiver': do_something_else_post_save,
...                 'weak': False,
...                 'dispatch_uid': 'my_other_signal_uuid',
...             },
...         ]

When get_signals returns a list of signal dictionaries, each dict should be parameters which will be passed to Django’s Signal.connect. The only exception is the optional signal key which when given should either be a name of the supported signal as per signal_pool or actual signal object. When not provided, this decorator will figure out the signal from the receiver’s name, same as when get_signals returns a list of callables as described above.

Parameters:
  • getter (str, optional) – Name of the class method on the decorated model which should return signals to be connected
  • signal_pool (dict, optional) – Dictionary of supported signals. Keys should be signal names and values should be actual signal objects. By default all model signals are supported.
django_auxilium.models.signals.file_field_auto_change_delete(*args, **kwargs)

Model decorator which automatically setups all the necessary signals to automatically remove files associated with given fields when they change on model save.

Unlike FileFieldAutoDelete which removed file fields on model deletion, this decorator removes files if they are changed. The premise is that if the file field is editable if it is changed either in Django Admin or in custom logic, unless custom logic is applied, by default Django would not remove the old file hence over time many orphan files can accumulate on the server. Locally it might not be a big deal however when external (especially paid) storage backend is used, extra files can make a real difference over time. This decorator automatically handles that and removes old files when they are changed.

In order to do that however model has to track when files change on the model and Django models do not do that out of the box. You can either implement that functionality yourself on the model by implementing the following methods:

  • is_dirty() - should return a bool if any of the model fields have changed. Usually this will require to track model fields during __init__ which will be used to initialize model instance when querying model from db and then comparing those values with the new values when this method is called.
  • get_dirty_fields() - should return a dict where the keys are the field names and the values are old field values.

Alternatively you can use a third-party package which implements that API. This decorator is tested with django-dirtyfields.

Warning

As mentioned above, this decorator requires is_dirty() and get_dirty_fields() to be implemented on the model. Even though we recommend to use django-dirtyfields for that functionality, this library does not ship with django-dirtyfields pre-installed and you will need to install it independently.

Note

This decorator does not remove files on delete as well. If you would like to both remove files on both delete and change you will need to apply both FileFieldAutoDelete and this decorator at the same time.

Examples

>>> import random
>>> from dirtyfields import DirtyFieldsMixin

>>> @file_field_auto_change_delete  # equivalent to @file_field_auto_change_delete('*')
... class FooModel(DirtyFieldsMixin, models.Model):
...     class Meta(object):
...         app_label = str(random.randrange(1000, 2000))
...     file = models.FileField(upload_to='foo')

>>> @file_field_auto_change_delete('file')
... class FooModel(DirtyFieldsMixin, models.Model):
...     class Meta(object):
...         app_label = str(random.randrange(1000, 2000))
...     file = models.FileField(upload_to='foo')

>>> # remote both on delete and change
>>> @file_field_auto_delete
... @file_field_auto_change_delete
... class FooModel(DirtyFieldsMixin, models.Model):
...     class Meta(object):
...         app_label = str(random.randrange(1000, 2000))
...     file = models.FileField(upload_to='foo')
Parameters:
  • fields (str, list) – Same as FileFieldAutoDelete fields parameter
  • signal (Signal, optional) – Same as FileFieldAutoDelete signal parameter. By default is post_save signal.
  • signal_name_pattern (str, optional) – The name given to the signal function which will automatically remove the file. This can be a string formatted string. Into it, two parameters will be passed which are model and field. model is a model class (not instance) to which the signal is being connected to and field is a name of the file field (or '_' delimited field names if multiple fields are given). The default pattern is post_save_{model.__name__}_delete_{field} which results in patterns like 'post_save_Model_delete_file_field'. The reason why this pattern might be useful is because it can be used to disconnect the signal receiver at a later time.
django_auxilium.models.signals.file_field_auto_delete(*args, **kwargs)

Model decorator which automatically setups all the necessary signals to automatically remove files associated with given fields when model instance is removed.

Starting with Django 1.3 when model records are deleted via querysets (e.g. model.objects.filter(...).delete()), the model’s delete() method is no longer called. As a consequence, if the model has any file fields, those files will not be removed even if it is specified in the delete() method. The new way to remove files associated with a model is to use Django signals framework, or more specifically the post_delete signal. This decorator automatically connects the post_delete signal which will remove the file associated with the specified file field.

More about this can be found at Django 1.3 release notes.

Examples

>>> import random
>>> @file_field_auto_delete  # equivalent to @file_field_auto_delete('*')
... class FooModel(models.Model):
...     class Meta(object):
...         app_label = str(random.randrange(1000, 2000))
...     file = models.FileField(upload_to='foo')

>>> import random
>>> @file_field_auto_delete('file')
... class FooModel(models.Model):
...     class Meta(object):
...         app_label = str(random.randrange(1000, 2000))
...     file = models.FileField(upload_to='foo')
Parameters:
  • fields (str, list) – Either a single file field which should be removed on delete or a list of fields. Also '*' can be used in order to delete all FileField on the model.
  • signal (Signal, optional) – Djagno signal class which should be used to attach the receiver signal handler to. By default is post_delete signal.
  • signal_name_pattern (str, optional) – The name given to the signal function which will automatically remove the file. This can be a string formatted string. Into it, two parameters will be passed which are model and field. model is a model class (not instance) to which the signal is being connected to and field is a name of the file field (or '_' delimited field names if multiple fields are given). The default pattern is post_delete_{model.__name__}_delete_{field} which results in patterns like 'post_delete_Model_delete_file_field'. The reason why this pattern might be useful is because it can be used to disconnect the signal receiver at a later time.
django_auxilium.models.signals.field_names

list – List of normalized field names which should be removed on change

django_auxilium.models.signals.fields

list – List of validated FieldSpec which signal will use to remove changed files

django_auxilium.models.signals.signal_name_pattern

str – Same as the signal_name_pattern parameter