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- containscreatedandmodifiedattributesCreatedModel- containscreatedattributeModifiedModel- containsmodifiedattributeUserModel- containsuserattribute which is a foreign key to a Django user modelTitleDescriptionModel- containstitleanddescriptionattributes
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 = barsyntax 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:
objectBase class for implementing callback email backends.
“Callback” is used in a sense that appropriate signals are sent while sending message.
Sending: signals.sending_mailsignal is sent when message is in progress of being sentSent: signals.sent_mailsignal is sent when message was successfully sentFailed: signals.failed_mailsignal 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.EmailBackendCustom 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.EmailBackendCustom 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.CharFieldCustom 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 unsupportedtypecolor 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.FileFieldTyped 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_magicis used, please make sure thatpython-magicis 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_whitelistis 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 asTruewhich 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.CharFieldForm 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
ValidationErrorwill be raised. Useful to blacklist specific values for being validated. If more complex logic is required, please consider usingmappingas 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
separatorif 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.CharFieldRange 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
Rangerangeconfig value.Returns: Return type: Range
-
validate(value)[source]¶ Validate the rangeconfig value.
Following is validated:
- rows or columns are supplied depending on
required_rowandrequired_columnsparameters - 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:
objectMiddleware 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.DEBUGis 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:
objectMixin for random filename file mixins which implements Django’s
deconstructto be Django-migrations compatible
-
class
django_auxilium.models.fields.files.RandomFileNameFileFieldMixin(*args, **kwargs)[source]¶ Bases:
django_auxilium.models.fields.files.RandomFileFieldDeconstructMixinMixing for a custom
FileFieldwhich 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.RandomFileFieldDeconstructMixinMixing for a custom
FileFieldwhich 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_tocompliant 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
mediawhere 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_tocompliant functionReturn type: function
- path (str) – The path relative to
-
django_auxilium.models.fields.files.random_filename_upload_to(path)[source]¶ Get
upload_tocompliant method which will always return a random filename.This method can be used as Django’s
FileFieldupload_toparameter. It returns anupload_tocallable which will make sure the filename will always be random. This method also accepts apathparameter which will define where relative to the media folder, the file will be stored.Parameters: path (str) – The path relative to mediawhere the filename should be uploaded toReturns: upload_to – Django FileField’supload_tocompliant 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.ModifiedModelThis 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.ModelThis 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.ModelThis 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.BaseModelThis 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.BaseModelThis 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.ModelThis 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.parentis aForwardManyToOneDescriptorinstance.
-
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.DecoratorDecorator 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.pyhowever 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.pymodule.get_signalsexpected 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_signalsreturns 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_poolparameter.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_signalsreturns a list of signal dictionaries, eachdictshould be parameters which will be passed to Django’sSignal.connect. The only exception is the optionalsignalkey which when given should either be a name of the supported signal as persignal_poolor actual signal object. When not provided, this decorator will figure out the signal from the receiver’s name, same as whenget_signalsreturns 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 AutoSignalsdescription.
-
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:
tupleField 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.FileFieldAutoDeleteModel decorator which automatically setups all the necessary signals to automatically remove files associated with given fields when they change on model save.
Unlike
FileFieldAutoDeletewhich 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 aboolif 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 adictwhere 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-dirtyfieldsfor that functionality, this library does not ship withdjango-dirtyfieldspre-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
FileFieldAutoDeleteand 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
FileFieldAutoDeletefieldsparameter - signal (Signal, optional) – Same as
FileFieldAutoDeletesignalparameter. By default ispost_savesignal. - 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
modelandfield.modelis a model class (not instance) to which the signal is being connected to andfieldis 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.DecoratorModel 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_deletesignal. This decorator automatically connects thepost_deletesignal 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 allFileFieldon the model. - signal (Signal, optional) – Djagno signal class which should be used to attach the receiver signal handler to.
By default is
post_deletesignal. - 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
modelandfield.modelis a model class (not instance) to which the signal is being connected to andfieldis 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_patternparameter
-
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_deletesignal signature:def receiver(sender, instance, *args, **kwargs): pass
-
get_signal_name()[source]¶ Using the
signal_name_patternpattern, 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_wrapis a valid Django model. It also validates that the given field name is present in the model and that it subclasses Django’sFileFieldfield 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_wrapis a valid Django model. This method validates that the given fields are present in the model and that they are a subclass of Django’sFileFieldfield 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.pyhowever 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.pymodule.get_signalsexpected 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_signalsreturns 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_poolparameter.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_signalsreturns a list of signal dictionaries, eachdictshould be parameters which will be passed to Django’sSignal.connect. The only exception is the optionalsignalkey which when given should either be a name of the supported signal as persignal_poolor actual signal object. When not provided, this decorator will figure out the signal from the receiver’s name, same as whenget_signalsreturns 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
FileFieldAutoDeletewhich 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 aboolif 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 adictwhere 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-dirtyfieldsfor that functionality, this library does not ship withdjango-dirtyfieldspre-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
FileFieldAutoDeleteand 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
FileFieldAutoDeletefieldsparameter - signal (Signal, optional) – Same as
FileFieldAutoDeletesignalparameter. By default ispost_savesignal. - 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
modelandfield.modelis a model class (not instance) to which the signal is being connected to andfieldis 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_deletesignal. This decorator automatically connects thepost_deletesignal 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 allFileFieldon the model. - signal (Signal, optional) – Djagno signal class which should be used to attach the receiver signal handler to.
By default is
post_deletesignal. - 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
modelandfield.modelis a model class (not instance) to which the signal is being connected to andfieldis 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
FieldSpecwhich signal will use to remove changed files
-
django_auxilium.models.signals.signal_name_pattern¶ str – Same as the
signal_name_patternparameter
- 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:
objectBase 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
parentobject
-
delete(*args, **kwargs)[source]¶ This method must be overwritten by subclasses which should delete cache value for the given parameters
argsandkwargsare the parameters passed to the cached function and can be used by the method to determine which cache value from theparentobject 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
argsandkwargsare the parameters passed to the cached function and can be used by the method to extract the cache value from theparentobjectWhen 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.HybridDecoratorBase 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.BaseCacheDecoratorDecorator 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
MemoizeDecoratorExamples
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:
objectCache 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
CacheDecoratorwhich 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_classis used. - as_property (bool, optional) –
Whether to implement the descriptor as a property. By default it is
False.Warning
This option as
Truecan 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 (
methodparameter) 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.BaseCacheCaching 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
parentobject, that indicates that the cache was set and that value is used for cache. When not present, that means cache is missing and henceNotInCacheis raised in most use-cases.-
delete(*args, **kwargs)[source]¶ Delete the cache value from the
parentobjectRaises: NotInCache– When the cache is not set and so cannot be deleted
-
get(*args, **kwargs)[source]¶ Get the cache value from the
parentobjectRaises: NotInCache– When the cache is not set
-
-
class
django_auxilium.utils.functools.cache.MemoizeDecorator(is_method=None)[source]¶ Bases:
django_auxilium.utils.functools.cache.BaseCacheDecoratorDecorator 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.CacheDescriptorCache 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
MemoizeDecoratorwhich 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.BaseCacheCaching 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
NotInCacheis raised.-
delete(*args, **kwargs)[source]¶ Delete the cache value from the
parentobject 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
parentobject 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.ExceptionException 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
MemoizeDecoratorExamples
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
cachewhich 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
cachewhich 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:
objectBase 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.wrapssaving 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_wrapwhich is the object the method should wrap. The method itself should return the wrapped version of theto_wrapobject. 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
foowill 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.wrapsto 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.DecoratorClass 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_classattribute.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 –
Trueif the object to be decorated is a class method, orFalseif it is a standalone function
-
pre_wrap()[source]¶ Method which determines whether the
to_wrapis 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_methoddecorator parameter.
-
-
class
django_auxilium.utils.functools.lazy.LazyDecorator(types)[source]¶ Bases:
django_auxilium.utils.functools.decorators.DecoratorLazy 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_typeswas 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.PromiseWrapper class for lazy objects as returned by the
LazyDecoratorThe 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:
typeMetaclass for
LazyWrapperThe reason we need metaclass is because we need to customize some magic methods for
LazyWrapperfor particular types while creating class which is what metaclasses are for!This metaclass expects all subclasses of
LazyWrapperto supplypossible_typesattribute 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
LazyDecoratorIt has the same API as Django’s
lazyfunction 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
magiclibraryParameters: 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
magiclibraryParameters: 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
magiclibraryNote
This function reads first 1Kb of the file in order to determine the content_type however at the end seeks back to
0Parameters: 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.HTMLParserCustom 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 aspreortextarea.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:
objectUtility 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.rangenamedtupleHelper 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.DecoratorSimple 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.WizardViewModified Django’s
WizardViewwhich allows to specifyform_listas class attribute.When using Django’s
WizardView, theform_listmust be specified when callingas_view():WizardView.as_view(form_list)
This class allows to provide the
form_listas 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