API¶
Forms¶
ConcurrentForm¶
-
class
concurrency.forms.
ConcurrentForm
(data=None, files=None, auto_id='id_%s', prefix=None, initial=None, error_class=<class 'django.forms.utils.ErrorList'>, label_suffix=None, empty_permitted=False, instance=None, use_required_attribute=None, renderer=None)[source]¶ Simple wrapper to ModelForm that try to mitigate some concurrency error. Note that is always possible have a RecordModifiedError in model.save(). Statistically form.clean() should catch most of the concurrent editing, but is good to catch RecordModifiedError in the view too.
VersionWidget¶
-
class
concurrency.forms.
VersionWidget
(attrs=None)[source]¶ Widget that show the revision number using <div>
Usually VersionField use HiddenInput as Widget to minimize the impact on the forms, in the Admin this produce a side effect to have the label Version without any value, you should use this widget to display the current revision number
Exceptions¶
VersionChangedError¶
-
class
concurrency.exceptions.
RecordModifiedError
¶
RecordModifiedError¶
InconsistencyError¶
Changed in version 0.7.
Warning
removed in 0.7
-
class
concurrency.exceptions.
InconsistencyError
¶
VersionError¶
Admin¶
ConcurrentModelAdmin¶
Warning
If you customize fields
or fieldsets
remember to add version field to the list. (See issue issue #81)
ConcurrencyActionMixin¶
ConcurrencyListEditableMixin¶
Middleware¶
-
class
concurrency.middleware.
ConcurrencyMiddleware
¶
ConcurrencyMiddleware¶
See also
-
class
concurrency.middleware.
ConcurrencyMiddleware
(get_response=None)[source]¶ Intercept RecordModifiedError and invoke a callable defined in
CONCURRECY_HANDLER409
passing the request and the object.
concurrency.views.conflict¶
Helpers¶
apply_concurrency_check()¶
New in version 0.4.
Changed in version 0.8.
Add concurrency check to existing classes.
Note
With Django 1.7 and the new migrations management, this utility does not work anymore. To add concurrency management to a external Model, you need to use a migration to add a VersionField to the desired Model.
Note
See demo.auth_migrations
for a example how to add IntegerVersionField
to auth.Group
)
operations = [
# add version to django.contrib.auth.Group
migrations.AddField(
model_name='Group',
name='version',
field=IntegerVersionField(help_text=b'Version', default=1),
),
]
and put in your settings.py
MIGRATION_MODULES = {
...
...
'auth': '<new.migration.package>',
}
disable_concurrency()¶
New in version 0.6.
Context manager to temporary disable concurrency checking.
Changed in version 0.9.
Starting from version 0.9, disable_concurrency can disable both at Model level or instance level, depending on the passed object. Passing Model is useful in django commands, load data or fixtures, where instance should be used by default
Changed in version 1.0.
Is now possible use disable_concurrency without any argument to disable concurrency on any Model. This features has been developed to be used in django commands
Changed in version 1.1.
Does not raise an exception if a model not under concurrency management is passed as argument.
examples¶
@disable_concurrency()
def recover_view(self, request, version_id, extra_context=None):
return super().recover_view(request,
version_id,
extra_context)
def test_recover():
deleted_list = revisions.get_deleted(ReversionConcurrentModel)
delete_version = deleted_list.get(id=5)
with disable_concurrency(ReversionConcurrentModel):
deleted_version.revert()
concurrency_disable_increment()¶
New in version 1.1.
Context manager to temporary disable version increment. Concurrent save is still checked but no version increment is triggered, this creates ‘shadow saves’,
It accepts both a Model or an instance as target.
Triggers¶
TriggerFactory¶
New in version 2.3.
-
class
concurrency.triggers.
TriggerFactory
(connection)[source]¶ Abstract Factory class to create triggers. Implemementations need to set the following attributes
update_clause, drop_clause and list_clause
Those will be formatted using standard python format() as:
self.update_clause.format(trigger_name=field.trigger_name, opts=field.model._meta, field=field)
So as example:
update_clause = """CREATE TRIGGER {trigger_name} AFTER UPDATE ON {opts.db_table} BEGIN UPDATE {opts.db_table} SET {field.column} = {field.column}+1 WHERE {opts.pk.column} = NEW.{opts.pk.column}; END; """
See also
Test Utilties¶
ConcurrencyTestMixin¶
-
class
concurrency.utils.
ConcurrencyTestMixin
[source]¶ Mixin class to test Models that use VersionField
this class offer a simple test scenario. Its purpose is to discover some conflict in the save() inheritance:
from concurrency.utils import ConcurrencyTestMixin from myproject.models import MyModel class MyModelTest(ConcurrencyTestMixin, TestCase): concurrency_model = TestModel0 concurrency_kwargs = {'username': 'test'}
Signining¶
New in version 0.5.
VersionField is ‘displayed’ in the Form using an django.forms.HiddenInput
widget, anyway to be sure that the version is not
tampered with, its value is signed. The default VersionSigner is concurrency.forms.VersionFieldSigner
that simply
extends django.core.signing.Signer
. If you want change your Signer you can set CONCURRENCY_FIELD_SIGNER
in your settings
mysigner.py
class DummySigner(): """ Dummy signer that simply returns the raw version value. (Simply do not sign it) """ def sign(self, value): return smart_str(value) def unsign(self, signed_value): return smart_str(signed_value)
settings.py
CONCURRENCY_FIELD_SIGNER = "myapp.mysigner.DummySigner"