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 usingsignals.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:
>>> @auto_signals ... class MyModel(models.Model): ... @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 usesignal_pool
parameter.Dictionary: An example:
>>> @auto_signals ... class MyModel(models.Model): ... @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, eachdict
should be parameters which will be passed to Django’sSignal.connect
. The only exception is the optionalsignal
key which when given should either be a name of the supported signal as persignal_pool
or actual signal object. When not provided, this decorator will figure out the signal from the receiver’s name, same as whenget_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
-
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.
-
__getnewargs__
()¶ Return self as a plain tuple. Used by copy and pickle.
-
__getstate__
()¶ Exclude the OrderedDict from pickling
-
__repr__
()¶ Return a nicely formatted representation string
-
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 abool
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 adict
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()
andget_dirty_fields()
to be implemented on the model. Even though we recommend to usedjango-dirtyfields
for that functionality, this library does not ship withdjango-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
>>> from dirtyfields import DirtyFieldsMixin >>> @file_field_auto_change_delete # equivalent to @file_field_auto_change_delete('*') ... class FooModel(DirtyFieldsMixin, models.Model): ... file = models.FileField(upload_to='foo') >>> @file_field_auto_change_delete('file') ... class FooModel(DirtyFieldsMixin, models.Model): ... 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): ... 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 ispost_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
andfield
.model
is a model class (not instance) to which the signal is being connected to andfield
is a name of the file field (or'_'
delimited field names if multiple fields are given). The default pattern ispost_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.
-
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’sdelete()
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 thedelete()
method. The new way to remove files associated with a model is to use Django signals framework, or more specifically thepost_delete
signal. This decorator automatically connects thepost_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
>>> @file_field_auto_delete # equivalent to @file_field_auto_delete('*') ... class FooModel(models.Model): ... file = models.FileField(upload_to='foo') >>> @file_field_auto_delete('file') ... class FooModel(models.Model): ... 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 allFileField
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
andfield
.model
is a model class (not instance) to which the signal is being connected to andfield
is a name of the file field (or'_'
delimited field names if multiple fields are given). The default pattern ispost_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
-
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_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 thatself.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’sFileField
field class.Raises: AttributeError
– When the field cannot be found on the modelTypeError
– When the field is not aFileField
-
validate_fields
()[source]¶ Validate all the fields on the model
This method is expected to be called after
validate_model()
and it assumes thatself.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’sFileField
field class.See also
- fields (str, list) – Either a single file field which should be removed on delete
or a list of fields. Also
-
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 usingsignals.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:
>>> @auto_signals ... class MyModel(models.Model): ... @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 usesignal_pool
parameter.Dictionary: An example:
>>> @auto_signals ... class MyModel(models.Model): ... @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, eachdict
should be parameters which will be passed to Django’sSignal.connect
. The only exception is the optionalsignal
key which when given should either be a name of the supported signal as persignal_pool
or actual signal object. When not provided, this decorator will figure out the signal from the receiver’s name, same as whenget_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 abool
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 adict
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()
andget_dirty_fields()
to be implemented on the model. Even though we recommend to usedjango-dirtyfields
for that functionality, this library does not ship withdjango-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
>>> from dirtyfields import DirtyFieldsMixin >>> @file_field_auto_change_delete # equivalent to @file_field_auto_change_delete('*') ... class FooModel(DirtyFieldsMixin, models.Model): ... file = models.FileField(upload_to='foo') >>> @file_field_auto_change_delete('file') ... class FooModel(DirtyFieldsMixin, models.Model): ... 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): ... 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 ispost_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
andfield
.model
is a model class (not instance) to which the signal is being connected to andfield
is a name of the file field (or'_'
delimited field names if multiple fields are given). The default pattern ispost_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’sdelete()
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 thedelete()
method. The new way to remove files associated with a model is to use Django signals framework, or more specifically thepost_delete
signal. This decorator automatically connects thepost_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
>>> @file_field_auto_delete # equivalent to @file_field_auto_delete('*') ... class FooModel(models.Model): ... file = models.FileField(upload_to='foo') >>> @file_field_auto_delete('file') ... class FooModel(models.Model): ... 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 allFileField
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
andfield
.model
is a model class (not instance) to which the signal is being connected to andfield
is a name of the file field (or'_'
delimited field names if multiple fields are given). The default pattern ispost_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
- fields (str, list) – Either a single file field which should be removed on delete
or a list of fields. Also