Django Auxilium¶
Contents¶
Highlights¶
As mentioned in README, this library is a collection of various utilities for working in Django. This document highlights some of the most useful features. To see all of them you can either browse the source code or browse the API docs.
Models¶
Base models are available with common attributes:
BaseModel
- containscreated
andmodified
attributesCreatedModel
- containscreated
attributeModifiedModel
- containsmodified
attributeUserModel
- containsuser
attribute which is a foreign key to a Django user modelTitleDescriptionModel
- containstitle
anddescription
attributes
Model fields¶
Useful fields:
RandomFileField
- file field which always generates unique filename when storing new filesOriginalFilenameRandomFileField
- file field which always generates unique filename when storing new files however stores original filename on specified fieldRandomImageField
- image field which always generates unique filename when storing new filesOriginalFilenameRandomImageField
- image field which always generates unique filename when storing new files however stores original filename on specified field
Form fields¶
TypedFileField
- file field which enforces allowed file extensions and/or mimetypesMultipleValuesCharField
- character field which normalizes to multiple values via any delimiter including regex. Useful when in UI multiple inputs cannot be used but multiple values need to be collected.
Signals¶
Model signal utility decorators:
file_field_auto_delete
- this decorator automatically attaches appropriate signal to remove file fields when model is removed:>>> @file_field_auto_delete ... class FooModel(models.Model): ... file = models.FileField(upload_to='foo')
file_field_auto_change_delete
- this decorator automatically attaches appropriate signal to remove file fields when model is removed:>>> from dirtyfields import DirtyFieldsMixin >>> @file_field_auto_change_delete ... class FooModel(DirtyFieldsMixin, models.Model): ... file = models.FileField(upload_to='foo')
auto_signals
- this decorator allows to define model signals within the model which are returned as part ofget_signals()
method:>>> @auto_signals ... class MyModel(models.Model): ... @classmethod ... def get_signals(cls): ... def so_something_pre_save(sender, instance, *args, **kwargs): ... print('doing something on pre_save') ... return [ ... { ... 'receiver': so_something, ... 'weak': False, ... 'dispatch_uid': 'my_signal_uuid', ... }, ... ]
Middleware¶
MinifyHTMLMiddleware
- simple and speedy HTML minifying middleware
Decorators¶
Utilities¶
cache
- decorator for caching either stand-alone functions or class methods so that they are only executed once. Useful for expensive functions which do not accept any parameters:>>> @cache def my_expensive_function(): ... return list(zip(range(1000000), range(1000000)))
memoize
- decorator for caching either stand-alone functions or class methods which cache results per given parameters so that subsequent calls with same parameters are not executed. Useful for expensive functions which accept parameters:>>> @memoize ... def fib(n): ... if n == 0: ... return 0 ... elif n == 1: ... return 1 ... else: ... return fib(n - 1) + fib(n - 2)
lazy
- decorator for making functions execute lazily. They only execute once any operation is executed on their result, including type checking:>>> @lazy(str) ... def foo(self): ... print('executing foo') ... return foo >>> f = foo() >>> isinstance(f, str) executing foo True >>> f == 'foo' True
Bases¶
These base decorator classes are available which can be used to create class-based-decorators. They aim to provide to decorators what Django did with class-based-views to regular views. Both have a place however for more complex tasks separating logic within decorator might be more useful. As a matter of fact, the above decorators are all implemented on top of these base classes:
Decorator
- this is a base class for creating class-based-decorators:>>> class MyPartialDecorator(Decorator): ... def __init__(self, *args, **kwargs): ... self.args = args ... self.kwargs = kwargs ... def get_wrapped_object(self): ... def wrapper(*args, **kwargs): ... _args = self.args + args ... _kwargs = self.kwargs.copy() ... _kwargs.update(kwargs) ... return self.to_wrap(*_args, **_kwargs) ... return wrapper >>> my_partial = MyPartialDecorator.as_decorator()
HybridDecorator
- this is a base class for creating class-based-decorators which can wrap both standalone functions and class methods:>>> class MyDecorator(Decorator): ... def get_wrapped_object(self): ... if self.in_class: ... # class method ... else: ... # standalone function >>> my_decorator = MyDecorator.as_decorator()
History¶
0.1.4 (2018-05-26)¶
- Added: New APi for setting cache values. Now can do
Class.method.push(value, *args, **kwargs)
.
0.1.3 (2018-05-24)¶
- Fixed: Django 2.0 support for minify middleware.
0.1.2 (2017-11-22)¶
- Fixed: Not removing all spaces between html tags.
Sometimes spaces matter for formatting.
For example
<strong>Hello</strong> <i>World</i>
cannot be minified any further.
0.1.1 (2016-09-26)¶
- Fixed: Cache properties now allow to set cache value via
foo = bar
syntax when cache descriptor hasas_property == True
0.1.0 (2015-11-26)¶
- First release on PyPI.
django_auxilium¶
django_auxilium package¶
Subpackages¶
django_auxilium.callback_mail package¶
This package provides provides custom email backends which use Django signals framework to send signals when various mail sending progress events occur.
You can refer to signals
for a full
list of supported signals.
In order to enable this functionality, you need to change your Django email backend:
EMAIL_BACKEND = 'django_auxilium.callback_mail.filebased.EmailBackend'
Examples
from django.dispatch import receiver
from django_auxilium.callback_mail.signals import sent_mail
@receiver(sent_mail)
def my_callback(sender, message):
print("Message sent!")
-
class
django_auxilium.callback_mail.base.
BaseCallbackEmailBackendMixin
[source]¶ Bases:
object
Base class for implementing callback email backends.
“Callback” is used in a sense that appropriate signals are sent while sending message.
Sending: signals.sending_mail
signal is sent when message is in progress of being sentSent: signals.sent_mail
signal is sent when message was successfully sentFailed: signals.failed_mail
signal is sent when message sending failed
-
class
django_auxilium.callback_mail.filebased.
CallbackEmailBackend
(*args, **kwargs)[source]¶ Bases:
django_auxilium.callback_mail.base.BaseCallbackEmailBackendMixin
,django.core.mail.backends.filebased.EmailBackend
Custom file-based email backend which sends signals on progress events of sending messages.
-
django_auxilium.callback_mail.signals.
failed_mail
= <django.dispatch.dispatcher.Signal object>¶ Signal sent when message sending has failed
Parameters: - message – Email message which was not sent
- reason (str) – Reason for why message was not sent
-
django_auxilium.callback_mail.signals.
sending_mail
= <django.dispatch.dispatcher.Signal object>¶ Signal sent when message is being sent.
Parameters: message – Email message which is being sent
-
django_auxilium.callback_mail.signals.
sent_mail
= <django.dispatch.dispatcher.Signal object>¶ Signal sent when message has successfully been sent
Parameters: message – Email message which was sent
-
class
django_auxilium.callback_mail.smtp.
CallbackEmailBackend
(host=None, port=None, username=None, password=None, use_tls=None, fail_silently=False, use_ssl=None, timeout=None, ssl_keyfile=None, ssl_certfile=None, **kwargs)[source]¶ Bases:
django_auxilium.callback_mail.base.BaseCallbackEmailBackendMixin
,django.core.mail.backends.smtp.EmailBackend
Custom SMPT email backend which sends signals on progress events of sending messages.
django_auxilium.forms package¶
-
class
django_auxilium.forms.color.
ColorCharField
(*args, **kwargs)[source]¶ Bases:
django.forms.fields.CharField
Custom django field which is able to validate colors as input.
Parameters: type (str) – The type of the color format. The supported formats are
hex: The hexadecimal notation of the color. Examples is:
ff0000 #00ff00
This type also allows to specify these additional parameters:
- hash_required : bool
If the
#
is required in the value (e.g.'#ff0000'
)- hash_output : bool
If the
#
is required in the output validated value into_python
Raises: ValueError
– If unsupportedtype
color type is provided-
default_error_messages
= {u'invalid': u'Invalid color value.'}¶
-
class
django_auxilium.forms.files.
TypedFileField
(*args, **kwargs)[source]¶ Bases:
django.forms.fields.FileField
Typed FileField which allows to make sure the uploaded file has the appropriate type.
File type can be verified either by extension and/or mimetype.
This field accepts all the parameters as FileField, however in addition it accepts some additional parameters as documented below.
Examples
TypedFileField(ext_whitelist=['jpg', 'jpeg'], type_whitelist=['image/jpeg'])
Warning
If
use_magic
is used, please make sure thatpython-magic
is installed. This library does not require it by default.Parameters: - ext_whitelist (list, optional) – List of allowed file extensions. Note that each extension
should emit the first period. For example for filename
'example.jpg'
, the allowed extension should be'jpg'
. - type_whitelist (list, optional) – List of allowed file mimetypes.
- use_magic (bool, optional) – If
type_whitelist
is specified, this boolean determines whether to usepython-magic
(based of top oflibmagic
) to determine the mimetypes of files instead of relying on Django’sUploadedFile.content_type
. Django does not take any real care to determine any accurately the mimetype so it is recommended to leave this parameter asTrue
which is the default value.
-
default_error_messages
= {u'extension': u'File extension is not supported.', u'mimetype': u'File mimetype is not supported.'}¶
- ext_whitelist (list, optional) – List of allowed file extensions. Note that each extension
should emit the first period. For example for filename
-
class
django_auxilium.forms.multiple_values.
MultipleValuesCharField
(delimiter=u', ', separator=u', ', mapping=None, max_values=None, min_values=None, strip=True, disregard_empty=True, invalid_values=None, *args, **kwargs)[source]¶ Bases:
django.forms.fields.CharField
Form field to allow users to enter multiple values.
The best approach to enter multiple values is to provide a user with multiple input fields however sometimes that is not feasible. In that case this field provides a way for a user to enter multiple values in a single input field. The values can be delimited by either a constant character(s) or even by a regular expression.
Examples
MultipleValuesCharField(delimiter=',') MultipleValuesCharField(delimiter=',', min_values=1, max_values=10) MultipleValuesCharField(delimiter=re.compile(r'\W+'), separator='\n') MultipleValuesCharField(mapping=int) MultipleValuesCharField(mapping=lambda i: i == 5) MultipleValuesCharField(mapping={'foo': 'bar'})
Parameters: - delimiter (str, Regex, optional) – The delimiter according to which the values are split. It can also be given as a
compiled regular expression (e.g.
re.compile('\W+')
). Default is','
(comma). - separator (str) – Iff the delimiter is a regular expression, then this value will be used to separate values within the widget when rendering values back in the UI.
- mapping (dict, callable, optional) – By default all split values are casted to a Python string. The mapping allows to
change that so that individual values will be mapped to different data-types.
Mapping can be defined as a callable which will should accept one parameter, the
value, and return the input value properly casted. Mapping can also be defined
as a dictionary. In that case, if the individual value exists as a key of the
dictionary, the value associated with the key will be used as a final casted
value. Please note that if the key is not found in the dictionary, then the input
value will be used.
Default is
None
. - max_values (int, optional) – The maximum allowed number of values. Default is
None
. - min_values (int, optional) – The minimum required number of provided values. Default is
None
. - strip (bool, optional) – If
True
, then once the user string is split, all values are stripped of whitespace on either side of the string before being converted to Python value by using Python’s stringstrip()
. Default isTrue
. - disregard_empty (bool, optional) – If
True
, then once input string is split, all false evaluated values are disregarded. Default isTrue
. - invalid_values (list, optional) – If provided, this list determines which values are invalid and if any are
encountered, a
ValidationError
will be raised. Useful to blacklist specific values for being validated. If more complex logic is required, please consider usingmapping
as a callable function.
-
default_error_messages
= {u'invalid_value': u'{0} is an invalid value.', u'invalid_values': u'{0} are invalid values.', u'max_values': u'Ensure this value has at least {0} values (it has {1}).', u'min_values': u'Ensure this value has at most {0} values (it has {1}).'}¶
-
prepare_value
(values)[source]¶ Prepare values to be displayed in the UI.
By default this method joins all the values by the
separator
if the values is a list or tuple. Otherwise, this method returns the value itself. Having this method does not require to define a custom widget for this form field.
- delimiter (str, Regex, optional) – The delimiter according to which the values are split. It can also be given as a
compiled regular expression (e.g.
-
class
django_auxilium.forms.range.
RangeSelectorField
(*args, **kwargs)[source]¶ Bases:
django.forms.fields.CharField
Range field which supports Excel-type rangeconfig selectors.
The rangeconfig selection is made using the following format:
<Column><Row>:<Column><Row>
Examples
A:A A1:A50 1:50
Parameters: - max_rows (int, optional) – The maximum number of rows the range can have
- max_columns (int, optional) – The maximum number of columns the range can have
- max_either (int, optional) – The maximum number of rows and columns the range can have. For example if the value is 1 then either a single column or a single row can be selected.
- required_rows (bool, optional) – Whether rows must be supplied in the rangeconfig
- required_columns (bool, optional) – Whether columns must be supplied in the rangeconfig
-
default_error_messages
= {u'invalid': u'Invalid range value.', u'max_columns': u'Too many columns selected. Maximum is {0}.', u'max_either': u'Too many rows or columns selected. Maximum is {0}.', u'max_rows': u'Too many rows selected. Maximum is {0}.', u'max_total': u'Too many total cells selected. Maximum is {0}.', u'required_columns': u'Column selection is required.', u'required_rows': u'Row selection is required.', u'values': u'Top-left coordinate must be first.'}¶
-
to_python
(value)[source]¶ Convert range string value to
Range
rangeconfig value.Returns: Return type: Range
-
validate
(value)[source]¶ Validate the rangeconfig value.
Following is validated:
- rows or columns are supplied depending on
required_row
andrequired_columns
parameters - rangeconfig is given as top-left to bottom-right
- columns range is within
max_columns
- rows range is within
max_rows
- both columns and rows range is within
max_either
- rows or columns are supplied depending on
django_auxilium.middleware package¶
-
class
django_auxilium.middleware.html.
MinifyHTMLMiddleware
(get_response=None)[source]¶ Bases:
object
Middleware for minifying HTML.
Unlike some other minifyers, this minifyer is pretty simple and tried to do the bare minimum in an attempt of being super-lightweight.
This middleware only works when
settings.DEBUG
is disabled.See also
django_auxilium.utils.html.simple_minify
- What is used to actually minify HTML
django_auxilium.models package¶
Collection of Django custom model field which have something to do with files
-
class
django_auxilium.models.fields.files.
OriginalFilenameRandomFileField
(*args, **kwargs)¶ Bases:
django_auxilium.models.fields.files.RandomFileNameWithFilenameFileFieldMixin
,django.db.models.fields.files.FileField
-
class
django_auxilium.models.fields.files.
OriginalFilenameRandomImageField
(*args, **kwargs)¶ Bases:
django_auxilium.models.fields.files.RandomFileNameWithFilenameFileFieldMixin
,django.db.models.fields.files.ImageField
-
class
django_auxilium.models.fields.files.
RandomFileField
(*args, **kwargs)¶ Bases:
django_auxilium.models.fields.files.RandomFileNameFileFieldMixin
,django.db.models.fields.files.FileField
-
class
django_auxilium.models.fields.files.
RandomFileFieldDeconstructMixin
[source]¶ Bases:
object
Mixin for random filename file mixins which implements Django’s
deconstruct
to be Django-migrations compatible
-
class
django_auxilium.models.fields.files.
RandomFileNameFileFieldMixin
(*args, **kwargs)[source]¶ Bases:
django_auxilium.models.fields.files.RandomFileFieldDeconstructMixin
Mixing for a custom
FileField
which generates random filenameParameters: upload_to (str) – String where uploaded files should be saved. Within that directory, random filename will always be selected.
-
class
django_auxilium.models.fields.files.
RandomFileNameWithFilenameFileFieldMixin
(*args, **kwargs)[source]¶ Bases:
django_auxilium.models.fields.files.RandomFileFieldDeconstructMixin
Mixing for a custom
FileField
which generates random filename and also stores original filenameParameters: - upload_to (str) – String where uploaded files should be saved. Within that directory, random filename will always be selected.
- filename_field (str) – Name of Django model field name where original filename should be stored
-
class
django_auxilium.models.fields.files.
RandomImageField
(*args, **kwargs)¶ Bases:
django_auxilium.models.fields.files.RandomFileNameFileFieldMixin
,django.db.models.fields.files.ImageField
-
django_auxilium.models.fields.files.
original_random_filename_upload_to
(path, filename_field)[source]¶ Get
upload_to
compliant method which will always return a random filename however will also store the original filename in the model.Parameters: - path (str) – The path relative to
media
where the filaname should be uploaded to - filename_field (str) – The name of the model attribute to which the original filename should be stored.
Returns: upload_to – Django
FileField
’supload_to
compliant functionReturn type: function
- path (str) – The path relative to
-
django_auxilium.models.fields.files.
random_filename_upload_to
(path)[source]¶ Get
upload_to
compliant method which will always return a random filename.This method can be used as Django’s
FileField
upload_to
parameter. It returns anupload_to
callable which will make sure the filename will always be random. This method also accepts apath
parameter which will define where relative to the media folder, the file will be stored.Parameters: path (str) – The path relative to media
where the filename should be uploaded toReturns: upload_to – Django FileField
’supload_to
compliant functionReturn type: function
Collection of simple abstract base models with a few additional attributes which don’t require much logic.
-
class
django_auxilium.models.base.
BaseModel
(*args, **kwargs)[source]¶ Bases:
django_auxilium.models.base.CreatedModel
,django_auxilium.models.base.ModifiedModel
This model adds date created and last date modified attributes
-
get_next_by_created
(**morekwargs)¶
-
get_next_by_modified
(**morekwargs)¶
-
get_previous_by_created
(**morekwargs)¶
-
get_previous_by_modified
(**morekwargs)¶
-
-
class
django_auxilium.models.base.
CreatedModel
(*args, **kwargs)[source]¶ Bases:
django.db.models.base.Model
This model adds date created attribute
-
created
¶ DateTimeField – The datetime when the model instance is created
-
created
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
-
get_next_by_created
(**morekwargs)¶
-
get_previous_by_created
(**morekwargs)¶
-
-
class
django_auxilium.models.base.
ModifiedModel
(*args, **kwargs)[source]¶ Bases:
django.db.models.base.Model
This model adds the last date modified attribute
-
modified
¶ DateTimeField – The datetime when the model was last modified and saved
-
get_next_by_modified
(**morekwargs)¶
-
get_previous_by_modified
(**morekwargs)¶
-
modified
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
-
-
class
django_auxilium.models.base.
NoteModel
(*args, **kwargs)[source]¶ Bases:
django_auxilium.models.base.BaseModel
This model adds a notes field
-
notes
¶ TextField – Text field for storing notes
-
get_next_by_created
(**morekwargs)¶
-
get_next_by_modified
(**morekwargs)¶
-
get_previous_by_created
(**morekwargs)¶
-
get_previous_by_modified
(**morekwargs)¶
-
notes
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
-
-
class
django_auxilium.models.base.
TitleDescriptionModel
(*args, **kwargs)[source]¶ Bases:
django_auxilium.models.base.BaseModel
This model adds a title and description fields
-
title
¶ CharField – Maximum length is 256 character
-
description
¶ TextField
-
description
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
-
get_next_by_created
(**morekwargs)¶
-
get_next_by_modified
(**morekwargs)¶
-
get_previous_by_created
(**morekwargs)¶
-
get_previous_by_modified
(**morekwargs)¶
-
title
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
-
-
class
django_auxilium.models.base.
UserModel
(*args, **kwargs)[source]¶ Bases:
django.db.models.base.Model
This model adds a foreign key to a user attribute
-
user
¶ User – The user associated with the model
-
user
Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.
In the example:
class Child(Model): parent = ForeignKey(Parent, related_name='children')
child.parent
is aForwardManyToOneDescriptor
instance.
-
user_id
¶ A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
-
-
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:
>>> 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 usesignal_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, 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.
-
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
>>> 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 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
>>> 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 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:
>>> 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 usesignal_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, 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
>>> 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 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
>>> 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 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
django_auxilium.utils package¶
-
class
django_auxilium.utils.functools.cache.
BaseCache
(parent, attr)[source]¶ Bases:
object
Base class for implementing cache implementations
Parameters: - parent (object) – A parent object where cache is stored
- attr (str) – Name of the attribute under which cache will be stored
in the
parent
object
-
delete
(*args, **kwargs)[source]¶ This method must be overwritten by subclasses which should delete cache value for the given parameters
args
andkwargs
are the parameters passed to the cached function and can be used by the method to determine which cache value from theparent
object to removeWhen cache value is not found, this method should raise
NotInCache
-
get
(*args, **kwargs)[source]¶ This method must be overwritten by subclasses which should return cache value
args
andkwargs
are the parameters passed to the cached function and can be used by the method to extract the cache value from theparent
objectWhen cache value is not found, this method should raise
NotInCache
-
class
django_auxilium.utils.functools.cache.
BaseCacheDecorator
(is_method=None)[source]¶ Bases:
django_auxilium.utils.functools.decorators.HybridDecorator
Base decorator for caching callables so that they only execute once and all subsequent calls return cached value
This is very useful for expensive functions
-
cache_class
¶ Caching implementation to use when wrapping standalone functions. This attribute is meant to be changed in subclasses.
alias of
Caching
-
cache_descriptor_class
= None¶ Descriptor class to be used when caching is applied to class methods. This attribute is meant to be changed in subclasses.
-
get_wrapped_object
()[source]¶ Main method for wrapping the given object.
This method has separate code paths for the following scenarios:
Class method: When wrapping class method descriptor is returned as created by get_cache_descriptor()
. That descriptor is then responsible for maintaining cache state for the class instance.Function: When wrapping standalone function, this method returns a wrapping function which caches the value on this decorator instance. That means that the cache becomes global since functions in Python are singletons.
-
-
class
django_auxilium.utils.functools.cache.
CacheDecorator
(as_property=False, *args, **kwargs)[source]¶ Bases:
django_auxilium.utils.functools.cache.BaseCacheDecorator
Decorator for caching callables so that they only execute once and all subsequent calls return cached value.
Note
This caching decorator does not account function parameters to cache values. If you need to cache different calls per given parameters, please look at
MemoizeDecorator
Examples
When caching standalone functions:
>>> @CacheDecorator.as_decorator() ... def compute(): ... print('computing here') ... return 'foo' >>> print(compute()) computing here foo >>> print(compute()) foo >>> print(compute.pop()) foo >>> print(compute()) computing here foo
When caching class methods:
>>> class Foo(object): ... @CacheDecorator.as_decorator() ... def foo(self): ... print('computing here') ... return 'foo' >>> f = Foo() >>> print(f.foo()) computing here foo >>> print(f.foo()) foo >>> print(f.foo.pop()) foo >>> print(f.foo()) computing here foo
Parameters: as_property (bool) – Boolean whether to create a property descriptor when using it on a class method
Warning
This is only meant to be used when the wrapping method does not accept any parameters since there is no way in Python to pass parameters to properties
-
cache_descriptor_class
¶ Descriptor class to be used when caching is applied to class methods
alias of
CacheDescriptor
-
-
class
django_auxilium.utils.functools.cache.
CacheDescriptor
(method, cache_class=None, as_property=False)[source]¶ Bases:
object
Cache descriptor to be used to add instance-level cache to class methods.
Note
This descriptor could be used independently (and there are even some examples below) however it is meant to be used along with
CacheDecorator
which provides much cleaner API.Examples
>>> def bar(self): ... print('computing') ... return 'bar' >>> class Foo(object): ... foo = CacheDescriptor(bar) >>> f = Foo() >>> print(f.foo()) computing bar >>> print(f.foo()) bar >>> print(f.foo.pop()) bar >>> print(f.foo()) computing bar >>> f.foo.push('another value') >>> print(f.foo()) another value
Parameters: - method (function) – Callable which this descriptor is meant to wrap and cache
- cache_class (type, optional) – Caching implementation cache which should be used to
apply caching. By default
default_cache_class
is used. - as_property (bool, optional) –
Whether to implement the descriptor as a property. By default it is
False
.Warning
This option as
True
can only be used with some caching implementations such asCaching
. Other implementations do not suppose this.
-
cache_attribute_pattern
= u'{name}_cache_{hash}'¶ String pattern for constructing the cache attribute name under which cache will be stored on the instance. This attribute is meant to be changed in subclasses to customize the functionality.
-
default_cache_class
¶ Cache implementation class which will be used for the caching. This attribute is meant to be changed in subclasses to customize the functionality.
alias of
Caching
-
get_cache
(instance)[source]¶ Helper method which given returns cache implementation instance for the given instance with given parameters
-
getter
(instance, *args, **kwargs)[source]¶ Wrapper method around the decorator-wrapped callable (
method
parameter) which returns cache value when available or otherwise computes and stores the value in cache by evaluating wrapped method
-
class
django_auxilium.utils.functools.cache.
Caching
(parent, attr)[source]¶ Bases:
django_auxilium.utils.functools.cache.BaseCache
Caching implementation which stores single cache value on the parent object
The cache is simply stored on the given attribute. When the attribute is present on the
parent
object, that indicates that the cache was set and that value is used for cache. When not present, that means cache is missing and henceNotInCache
is raised in most use-cases.-
delete
(*args, **kwargs)[source]¶ Delete the cache value from the
parent
objectRaises: NotInCache
– When the cache is not set and so cannot be deleted
-
get
(*args, **kwargs)[source]¶ Get the cache value from the
parent
objectRaises: NotInCache
– When the cache is not set
-
-
class
django_auxilium.utils.functools.cache.
MemoizeDecorator
(is_method=None)[source]¶ Bases:
django_auxilium.utils.functools.cache.BaseCacheDecorator
Decorator for memoizing functions so that they only execute once and all subsequent calls with same parameters
This is very useful for expensive functions which accept parameters
Examples
When caching standalone functions:
>>> @MemoizeDecorator.as_decorator() ... def compute(x): ... print('computing for', x) ... return x + 'foo' >>> print(compute('bar')) computing for bar barfoo >>> print(compute('awesome')) computing for awesome awesomefoo >>> print(compute('bar')) barfoo >>> print(compute.pop('bar')) barfoo >>> print(compute('bar')) computing for bar barfoo >>> print(compute('awesome')) awesomefoo
When caching class methods:
>>> class Foo(object): ... @MemoizeDecorator.as_decorator() ... def foo(self, x): ... print('computing for', x) ... return x + 'foo' >>> f = Foo() >>> print(f.foo('bar')) computing for bar barfoo >>> print(f.foo('awesome')) computing for awesome awesomefoo >>> print(f.foo('bar')) barfoo >>> print(f.foo('awesome')) awesomefoo >>> print(f.foo.pop('bar')) barfoo >>> print(f.foo('bar')) computing for bar barfoo >>> print(f.foo('awesome')) awesomefoo
-
cache_descriptor_class
¶ Descriptor class to be used when caching is applied to class methods
alias of
MemoizeDescriptor
-
-
class
django_auxilium.utils.functools.cache.
MemoizeDescriptor
(method, cache_class=None, as_property=False)[source]¶ Bases:
django_auxilium.utils.functools.cache.CacheDescriptor
Cache descriptor to be used to add instance-level cache to class methods which considers method parameters when caching values.
In other words, this descriptor caches method results per given parameters.
Note
This descriptor could be used independently (and there are even some examples below) however it is meant to be used along with
MemoizeDecorator
which provides much cleaner API.Examples
>>> def bar(self, x): ... print('computing for', x) ... return x + 'bar' >>> class Foo(object): ... foo = MemoizeDescriptor(bar) >>> f = Foo() >>> print(f.foo('foo')) computing for foo foobar >>> print(f.foo('awesome')) computing for awesome awesomebar >>> print(f.foo('foo')) foobar >>> print(f.foo.pop('foo')) foobar >>> print(f.foo('foo')) computing for foo foobar >>> print(f.foo('awesome')) awesomebar
-
cache_attribute_pattern
= u'{name}_memoize_{hash}'¶ String pattern for constructing the cache attribute name under which cache will be stored on the instance. This attribute is meant to be changed in subclasses to customize the functionality.
-
-
class
django_auxilium.utils.functools.cache.
Memoizing
(parent, attr)[source]¶ Bases:
django_auxilium.utils.functools.cache.BaseCache
Caching implementation which stores single cache value for a set of given parameters on the parent object hence is called memoization
The cache is stored on the given attribute as a dictionary. Keys are string representations of the given parameters to the cached function and the values are their corresponding cache values. When the key is in the dictionary, that is used as cache value. Otherwise, in most cases
NotInCache
is raised.-
delete
(*args, **kwargs)[source]¶ Delete the cache value from the
parent
object for the key as computed by the given parametersRaises: NotInCache
– When the cache is not set and so cannot be deleted
-
get
(*args, **kwargs)[source]¶ Get the cache value from the
parent
object by computing the key from the given parametersRaises: NotInCache
– When the cache is not set
-
-
exception
django_auxilium.utils.functools.cache.
NotInCache
[source]¶ Bases:
exceptions.Exception
Exception for when a value is not present in cache.
Primary purpose of the exception is to normalize various exception types from different cache implementations.
-
django_auxilium.utils.functools.cache.
cache
(*args, **kwargs)¶ Decorator for caching callables so that they only execute once and all subsequent calls return cached value.
Note
This caching decorator does not account function parameters to cache values. If you need to cache different calls per given parameters, please look at
MemoizeDecorator
Examples
When caching standalone functions:
>>> @CacheDecorator.as_decorator() ... def compute(): ... print('computing here') ... return 'foo' >>> print(compute()) computing here foo >>> print(compute()) foo >>> print(compute.pop()) foo >>> print(compute()) computing here foo
When caching class methods:
>>> class Foo(object): ... @CacheDecorator.as_decorator() ... def foo(self): ... print('computing here') ... return 'foo' >>> f = Foo() >>> print(f.foo()) computing here foo >>> print(f.foo()) foo >>> print(f.foo.pop()) foo >>> print(f.foo()) computing here foo
Parameters: as_property (bool) – Boolean whether to create a property descriptor when using it on a class method
Warning
This is only meant to be used when the wrapping method does not accept any parameters since there is no way in Python to pass parameters to properties
-
django_auxilium.utils.functools.cache.
cache_method
(*args, **kwargs)¶ Shortcut for
cache
which automatically indicates that the cached object is a method.These are equivalent:
class Foo(object): @cache(is_method=True) def foo(self): pass class Foo(object): @cache_method def bar(self): pass
-
django_auxilium.utils.functools.cache.
cache_property
(*args, **kwargs)¶ Shortcut for
cache
which automatically creates properties when used on class methods.These are equivalent:
@cache(as_property=True) def foo(self): pass @cache_property def bar(self): pass
-
django_auxilium.utils.functools.cache.
memoize
(*args, **kwargs)¶ Decorator for memoizing functions so that they only execute once and all subsequent calls with same parameters
This is very useful for expensive functions which accept parameters
Examples
When caching standalone functions:
>>> @MemoizeDecorator.as_decorator() ... def compute(x): ... print('computing for', x) ... return x + 'foo' >>> print(compute('bar')) computing for bar barfoo >>> print(compute('awesome')) computing for awesome awesomefoo >>> print(compute('bar')) barfoo >>> print(compute.pop('bar')) barfoo >>> print(compute('bar')) computing for bar barfoo >>> print(compute('awesome')) awesomefoo
When caching class methods:
>>> class Foo(object): ... @MemoizeDecorator.as_decorator() ... def foo(self, x): ... print('computing for', x) ... return x + 'foo' >>> f = Foo() >>> print(f.foo('bar')) computing for bar barfoo >>> print(f.foo('awesome')) computing for awesome awesomefoo >>> print(f.foo('bar')) barfoo >>> print(f.foo('awesome')) awesomefoo >>> print(f.foo.pop('bar')) barfoo >>> print(f.foo('bar')) computing for bar barfoo >>> print(f.foo('awesome')) awesomefoo
Collection of simple utilities which modify some behaviour of functions
-
class
django_auxilium.utils.functools.decorators.
Decorator
[source]¶ Bases:
object
Base class for various decorators.
Its aim is to do the same for decorators as Django class-based-views did to function-based-views.
The most common pattern for creating decorators in Python can be summarized in the following snippet:
from functools import wraps def decorator(f): @wraps(f) def wrapper(*args, **args): # some logic here return f(*args, **kwargs) return wrapper
The problem with the above approach is when decorators need to employ more difficult logic. That sometimes can cause decorator functions to become big hence making it difficult to maintain and test. This class is meant to simplify this task. It provides a way to make decorators by using classes. Its aim is to do the same for decorators as Django class-based-views did to function-based-views. This means that decorator’s various functionality can be split among class methods which can make the decorator’s logic more transparent and more test-friendly. It also automatically wraps the decorated object using
functools.wraps
saving some coding lines. Finally, this class allows to create decorators where initializing the decorator is optional. This perhaps can better be illustrated in a snippet:@decorator # not initialized def happy(): pass @decorator(foo='bar') # initialized def bunnies(): pass
Using this class is pretty simple. The most useful method you should know is
get_wrapped_object()
. Inside the method you can refer toto_wrap
which is the object the method should wrap. The method itself should return the wrapped version of theto_wrap
object. If your decorator needs to accept additional parameters, you can overwrite decorators__init__
. Finally, to use the decorator, don’t forget to use theas_decorator()
class method. You can refer to Examples to see a more full example of how to use this class.Examples
>>> class D(Decorator): ... def __init__(self, happy=False): ... self.happy = happy ... def get_wrapped_object(self): ... def wrapped(*args, **kwargs): ... print('called wrapped with', args, kwargs) ... if self.happy: ... print('Bunnies are very happy today!') ... return self.to_wrap(*args, **kwargs) ... return wrapped ... >>> decorator = D.as_decorator() >>> # not initialized (default values will be used if defined) >>> @decorator ... def sum(a, b): ... return a + b >>> sum(5, 6) called wrapped with (5, 6) {} 11 >>> # initialized with no parameters (default values will be used if defined) >>> @decorator() ... def sum(a, b): ... return a + b >>> sum(7, 8) called wrapped with (7, 8) {} 15 >>> # initialized with keyword parameters >>> @decorator(happy=True) ... def sum(a, b): ... "Sum function" ... return a + b >>> sum(9, 10) called wrapped with (9, 10) {} Bunnies are very happy today! 19 >>> sum.__doc__ == 'Sum function' True
-
to_wrap
¶ object – The object to be decorated/wrapped. This attributes becomes available when the decorator is called on the object.
-
classmethod
as_decorator
(*a, **kw)[source]¶ Return the actual decorator.
This method is necessary because the decorator is a class. Consider the following:
>>> class ClassDecorator(object): ... def __init__(self, obj): ... self.to_wrap = obj >>> @ClassDecorator ... def foo(): pass >>> isinstance(foo, ClassDecorator) True
In the above, since
foo
will be passed to the decorator’s class__init__
, the returned object will be an instance of the decorator’s class instead of a wrapper function. To avoid that, this method constructs a wrapper function which guarantees that the output of the decorator will be the wrapped object:>>> class D(Decorator): ... pass >>> d = D.as_decorator() >>> @d ... def foo(): pass >>> isinstance(foo, D) False
-
get_wrapped_object
()[source]¶ Returns the wrapped version of the object to be decorated/wrapped.
This is the meat of class because this method is the one which creates the decorator. Inside the method, you can refer to the object to be decorated via
self.to_wrap
.Note
This class automatically uses the
functools.wraps
to preserve theto_wrap
’s object useful attributes such as__doc__
hence there is no need to do that manually. You can just return a wrapped object and the class will take care of the rest.Returns: f – Decorated/wrapped function Return type: object
-
-
class
django_auxilium.utils.functools.decorators.
HybridDecorator
(is_method=None)[source]¶ Bases:
django_auxilium.utils.functools.decorators.Decorator
Class for implementing decorators which can decorate both regular functions as well as class methods.
This decorator automatically determines if the object to be decorated is a stand-alone function or a class method by adding an
in_class
attribute.Parameters: is_method (bool, optional) – Explicitly specify whether the decorator is being used on class method or a standalone function. When not specified, pre_wrap()
is used to automatically determine that. Please look at its documentation for the explanation of its limitations.-
in_class
¶ bool –
True
if the object to be decorated is a class method, orFalse
if it is a standalone function
-
pre_wrap
()[source]¶ Method which determines whether the
to_wrap
is a class method or a standalone functionWarning
This method uses pretty primitive technique to determine whether the wrapped callable is a class method or a function and so it might not work in all cases. It checks the first parameter name of the callable and if it is either
'self'
or'cls'
it is most likely a method.If you need more precise behavior you are encouraged to use
is_method
decorator parameter.
-
-
class
django_auxilium.utils.functools.lazy.
LazyDecorator
(types)[source]¶ Bases:
django_auxilium.utils.functools.decorators.Decorator
Lazy evaluation decorator.
Parameters: types (tuple, list) – Iterable of possible output data-types of the output of the input function Examples
>>> lazy = LazyDecorator >>> @lazy([six.string_types, int]) ... def a(bar): ... print('invoking a with {0}'.format(bar)) ... if isinstance(bar, int): ... return bar + 5 ... else: ... return bar + 'foo' >>> l = a('bar') >>> isinstance(l, six.string_types) invoking a with bar True >>> print(l) barfoo >>> l = a(5) >>> isinstance(l, int) invoking a with 5 True >>> print(l) 10
-
get_lazy_wrapper_class
()[source]¶ Construct lazy wrapper class for the given possible types
Returns: Lazy wrapper class specifically made for the possible types Return type: type
-
get_possible_types
(possible_types)[source]¶ Get possible types as a list
Parameters: possible_types (list, any) – Either a list of possible types or a single possible type Returns: List of possible types. If the input possible_types
was given as a single possible types, a list with a single item is returned.Return type: list
-
-
class
django_auxilium.utils.functools.lazy.
LazyWrapper
(f, args, kwargs)[source]¶ Bases:
django.utils.functional.Promise
Wrapper class for lazy objects as returned by the
LazyDecorator
The job of this wrapper is to not execute the inner function to get the result until absolutely necessary. For example simply storing a variable will not trigger inner function to be executed until something is done with the variable such as printing it, etc.
Parameters: - f (def) – Function which this lazy object wraps
- args (tuple) – Tuple of arguments which were passed to the function for execution
- kwargs (dict) – Dict of keyword arguments which were passed to the function for execution
-
class
django_auxilium.utils.functools.lazy.
LazyWrapperMeta
[source]¶ Bases:
type
Metaclass for
LazyWrapper
The reason we need metaclass is because we need to customize some magic methods for
LazyWrapper
for particular types while creating class which is what metaclasses are for!This metaclass expects all subclasses of
LazyWrapper
to supplypossible_types
attribute which this metaclass will use to customize the created class.
-
django_auxilium.utils.functools.lazy.
SPECIAL_METHODS
= ['__abs__', '__add__', '__and__', '__call__', '__cmp__', '__coerce__', '__contains__', '__delitem__', '__delslice__', '__div__', '__divmod__', '__eq__', '__float__', '__floordiv__', '__ge__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__hex__', '__iadd__', '__iand__', '__idiv__', '__idivmod__', '__ifloordiv__', '__ilshift__', '__imod__', '__imul__', '__int__', '__invert__', '__ior__', '__ipow__', '__irshift__', '__isub__', '__iter__', '__itruediv__', '__ixor__', '__le__', '__len__', '__long__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rfloorfiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setitem__', '__setslice__', '__str__', '__sub__', '__truediv__', '__unicode__', '__xor__', 'next']¶ The list of special methods is compiled from http://code.activestate.com/recipes/496741-object-proxying/
-
django_auxilium.utils.functools.lazy.
flazy
(f, possible_types)[source]¶ Wrapper function for the
LazyDecorator
It has the same API as Django’s
lazy
function so the same code can be reused with this decorator if necessary.Examples
>>> def f(): ... print('inside f') ... return 5 >>> g = flazy(f, int) >>> h = g() >>> print(str(h)) inside f 5
-
django_auxilium.utils.functools.lazy.
lazy
¶
-
django_auxilium.utils.content_type.
get_content_type
(f)[source]¶ Get content_type of a file by using
magic
libraryParameters: f (str or file) – Either a binary string of a file content or a file-like object from which file data will be read Returns: Content-type of a file as string Return type: str
-
django_auxilium.utils.content_type.
get_content_type_from_binary
(data)[source]¶ Get content_type of a binary string by using
magic
libraryParameters: data (binary) – Binary string data of a file from which content_type will be determined Returns: Content-type of a file as string Return type: str
-
django_auxilium.utils.content_type.
get_content_type_from_file
(f)[source]¶ Get content_type of a file-like object by using
magic
libraryNote
This function reads first 1Kb of the file in order to determine the content_type however at the end seeks back to
0
Parameters: f (file) – File-like object which should implement .seek()
and.read()
methods from which content_type will be determinedReturns: Content-type of a file as string Return type: str
-
class
django_auxilium.utils.html.
TextExtractorHTMLParser
[source]¶ Bases:
HTMLParser.HTMLParser
Custom HTML parser which extracts only text while parsing HTML
Once the parser parses the HTML,
get_text()
can be called which will return extracted text-
get_text
()[source]¶ Get extracted text after HTML is parsed.
Returns: Extracted text from the HTML document Return type: str
-
handle_charref
(number)[source]¶ Handler for processing character references.
This method handles both decimal (e.g.
'>' == '>'
) and hexadecimal (e.g.'>' == '>'
) references. It does that by simply converting the reference number to an integer with appropriate base and then converts that number to a character.
-
-
django_auxilium.utils.html.
html_to_text
(html)[source]¶ Function to convert HTML text to plain text by stripping all HTML.
Implementation is based from http://stackoverflow.com/questions/753052/strip-html-from-strings-in-python.
-
django_auxilium.utils.html.
simple_minify
(string)[source]¶ Minify HTML with very simple algorithm.
This function tries to minify HTML by stripping most spaces between all html tags (e.g.
</div> <div>
-></div> <div>
). Note that not all spaces are removed since sometimes that can adjust rendered HTML (e.g.<strong>Hello</strong> <i></i>
). In addition to that, this function replaces all whitespace (more then two consecutive whitespace characters or new line) with a space character except inside excluded tags such aspre
ortextarea
.Though process:
To minify everything except content of excluded tags in one step requires very complex regular expression. The disadvantage is the regular expression will involve look-behinds and look-aheads. Those operations make regex much more resource-hungry which eats precious server resources. In addition, complex regex are hard to understand and can be hard to maintain. That is why this function splits the task into multiple sections.
- Regex expression which matches all exclude tags within the html is used to split the HTML split into components. Since the regex expression is wrapped inside a group, the content of the exclude tags is also included inside the resulting split list. Due to that it is guaranteed that every odd element (if there are any) will be the excluded tags.
- All the split components are looped and processed in order to construct final minified HTML.
- All odd indexed elements are not processed and are simply appended to final HTML since as explained above, they are guaranteed to be content of excluded tags hence do not require minification.
- All even indexed elements are minified by stripping whitespace between tags and redundant whitespace is stripped in general via simple regex.
You can notice that the process does not involve parsing HTML since that usually adds some overhead (e.g. using beautiful soup). By using 2 regex passes this achieves very similar result which performs much better.
-
class
django_auxilium.utils.range.
AlphabeticNumbers
[source]¶ Bases:
object
Utility class for dealing with numbers in alphabetical form. Useful for converting Excel column notation into numbers and vise verse.
-
class
django_auxilium.utils.range.
Range
[source]¶ Bases:
django_auxilium.utils.range.rangenamedtuple
Helper data-structure for storing excel-type ranges.
Also has some useful range-related properties
-
columns
¶ Gets the number of columns in a given range.
Returns: columns – The number of columns Return type: int
-
rows
¶ Gets the number of columns in a given range.
Returns: columns – The number of columns Return type: int
-
-
class
django_auxilium.utils.string.
TextOnion
[source]¶ Bases:
django_auxilium.utils.functools.decorators.Decorator
Simple text onion decorator.
This decorator ensures that the decorated function always receives text string even if the decorator receives binary text. Once the wrapped function computes the result, decorator returns the same data-type as it has received.
-
django_auxilium.utils.string.
text_onion
(*args, **kwargs)¶ Simple text onion decorator.
This decorator ensures that the decorated function always receives text string even if the decorator receives binary text. Once the wrapped function computes the result, decorator returns the same data-type as it has received.
django_auxilium.views package¶
-
class
django_auxilium.views.wizard.
WizardView
(**kwargs)[source]¶ Bases:
formtools.wizard.views.WizardView
Modified Django’s
WizardView
which allows to specifyform_list
as class attribute.When using Django’s
WizardView
, theform_list
must be specified when callingas_view()
:WizardView.as_view(form_list)
This class allows to provide the
form_list
as class attribute:Wizard(WizardView): form_list = [] Wizard.as_view()
Warning
This class requires django-formtools to be independently installed since this library does not install it as a dependency.
-
form_list
= []¶
-
Django Auxilium¶
- Free software: MIT license
- GitHub: https://github.com/miki725/django-auxilium
- Documentation: http://django-auxilium.readthedocs.org/
About¶
Django Auxilium is a set of utilities packages as a Django app which
help working with Django. The word “auxilium” means help in Latin.
How this project came about is because I used to have an app for each
of my Django projects called core
or common
where I kept all of my
utility methods and classes, but after doing a couple of projects,
maintaining the same folder within all of the project became non-productive,
hence I refactored it into a separate reusable package.
Docs¶
For some of the highlights about the library and the most useful features you are encouraged to take a look at the documentation, particularly Highlights document.
Installation¶
Easiest way to install is by using pip
:
$ pip install django-auxilium
If you want to install from source code, you can also install using setup tools:
$ python setup.py install
Tests¶
Before running tests you need to install all test dependencies:
$ pip install -r requirements-dev.txt
# or
$ make install
Then to run tests you can use Makefile
:
$ make test
Note
This library uses both functional and doctests