def test_concurrency_conflict(self): import concurrency.api as api target = self._get_concurrency_target() target_copy = self._get_concurrency_target() v1 = api.get_revision_of_object(target) v2 = api.get_revision_of_object(target_copy) assert v1 == v2, "got same row with different version (%s/%s)" % (v1, v2) target.save() assert target.pk is not None # sanity check self.assertRaises(RecordModifiedError, target_copy.save)
def _do_update(model_instance, base_qs, using, pk_val, values, update_fields, forced_update): version_field = model_instance._concurrencymeta.field old_version = get_revision_of_object(model_instance) if not version_field.model._meta.abstract: if version_field.model is not base_qs.model: return func(model_instance, base_qs, using, pk_val, values, update_fields, forced_update) for i, (field, _1, value) in enumerate(values): if field == version_field: new_version = field._get_next_version(model_instance) values[i] = (field, _1, new_version) field._set_version_value(model_instance, new_version) break if values: if (model_instance._concurrencymeta.enabled and conf.ENABLED and not getattr(model_instance, '_concurrency_disabled', False) and old_version): filter_kwargs = {'pk': pk_val, version_field.attname: old_version} updated = base_qs.filter(**filter_kwargs)._update(values) >= 1 if not updated: version_field._set_version_value(model_instance, old_version) updated = conf._callback(model_instance) else: filter_kwargs = {'pk': pk_val} updated = base_qs.filter(**filter_kwargs)._update(values) >= 1 else: updated = base_qs.filter(pk=pk_val).exists() return updated
def identity(obj): """ returns a string representing "<pk>,<version>" of the passed object """ if hasattr(obj, 'RevisionMetaInfo'): return mark_safe("{0},{1}".format(unlocalize(obj.pk), get_revision_of_object(obj))) else: return mark_safe(unlocalize(obj.pk))
def action_checkbox(self, obj): """ A list_display column containing a checkbox widget. """ if self.check_concurrent_action: return helpers.checkbox.render(helpers.ACTION_CHECKBOX_NAME, force_text("%s,%s" % (obj.pk, get_revision_of_object(obj)))) else: return super(ConcurrencyActionMixin, self).action_checkbox(obj)
def inner(self, force_insert=False, force_update=False, using=None, **kwargs): reload = kwargs.pop('refetch', False) ret = func(self, force_insert, force_update, using, **kwargs) TriggerVersionField._increment_version_number(self) if reload: ret = refetch(self) setattr(self, self._concurrencymeta.field.attname, get_revision_of_object(ret)) return ret
def _do_update(model_instance, base_qs, using, pk_val, values, update_fields, forced_update): version_field = model_instance._concurrencymeta.field old_version = get_revision_of_object(model_instance) if not version_field.model._meta.abstract: if version_field.model is not base_qs.model: return func(model_instance, base_qs, using, pk_val, values, update_fields, forced_update) for i, (field, _1, value) in enumerate(values): if field == version_field: if (model_instance._concurrencymeta.increment and not getattr(model_instance, '_concurrency_disable_increment', False)): new_version = field._get_next_version(model_instance) values[i] = (field, _1, new_version) field._set_version_value(model_instance, new_version) # else: # new_version = old_version break # This provides a default if either (1) no values were provided or (2) we reached this code as part of a # create. We don't need to worry about a race condition because a competing create should produce an # error anyway. updated = base_qs.filter(pk=pk_val).exists() # This second situation can occur because `Model.save_base` calls `Model._save_parent` without relaying # the `force_insert` flag that marks the process as a create. Eventually, `Model._save_table` will call # this function as-if it were in the middle of an update. The update is expected to fail because there # is no object to update and the caller will fall back on the create logic instead. We need to ensure # the update fails (but does not raise an exception) under this circumstance by skipping the concurrency # logic. if values and updated: if (model_instance._concurrencymeta.enabled and conf.ENABLED and not getattr( model_instance, '_concurrency_disabled', False) and (old_version or not conf.IGNORE_DEFAULT)): filter_kwargs = { 'pk': pk_val, version_field.attname: old_version } updated = base_qs.filter( **filter_kwargs)._update(values) >= 1 if not updated: version_field._set_version_value( model_instance, old_version) updated = conf._callback(model_instance) else: filter_kwargs = {'pk': pk_val} updated = base_qs.filter( **filter_kwargs)._update(values) >= 1 return updated
def test_meta_inheritance(self): # TestModelWithCustomOptions extends ConcurrentModel # but we disabled concurrency only in TestModelWithCustomOptions import concurrency.api as api concurrency_enabled1 = SimpleConcurrentModel.objects.get_or_create( **{'username': '******'})[0] concurrency_enabled2 = SimpleConcurrentModel.objects.get_or_create( **{'username': '******'})[0] v1 = api.get_revision_of_object(concurrency_enabled1) v2 = api.get_revision_of_object(concurrency_enabled2) assert v1 == v2, "got same row with different version (%s/%s)" % (v1, v2) concurrency_enabled1.save() assert concurrency_enabled1.pk is not None # sanity check self.assertRaises(RecordModifiedError, concurrency_enabled2.save) concurrency_disabled1 = ConcurrencyDisabledModel.objects.get_or_create( **{'username': '******'})[0] concurrency_disabled2 = ConcurrencyDisabledModel.objects.get_or_create( **{'username': '******'})[0] v1 = api.get_revision_of_object(concurrency_disabled1) v2 = api.get_revision_of_object(concurrency_disabled2) assert v1 == v2, "got same row with different version (%s/%s)" % (v1, v2) concurrency_disabled1.save() assert concurrency_disabled1.pk is not None # sanity check v1 = api.get_revision_of_object(concurrency_disabled1) v2 = api.get_revision_of_object(concurrency_disabled2) assert v1 != v2
def _management_form(self): """Returns the ManagementForm instance for this FormSet.""" if self.is_bound: form = ConcurrentManagementForm(self.data, auto_id=self.auto_id, prefix=self.prefix) if not form.is_valid(): raise ValidationError('ManagementForm data is missing or has been tampered with') else: form = ConcurrentManagementForm(auto_id=self.auto_id, prefix=self.prefix, initial={TOTAL_FORM_COUNT: self.total_form_count(), INITIAL_FORM_COUNT: self.initial_form_count(), MAX_NUM_FORM_COUNT: self.max_num}, versions=[(form.instance.pk, get_revision_of_object(form.instance)) for form in self.initial_forms]) return form
def _do_update(model_instance, base_qs, using, pk_val, values, update_fields, forced_update): version_field = model_instance._concurrencymeta.field old_version = get_revision_of_object(model_instance) if not version_field.model._meta.abstract: if version_field.model is not base_qs.model: return func(model_instance, base_qs, using, pk_val, values, update_fields, forced_update) for i, (field, _1, value) in enumerate(values): if field == version_field: if (model_instance._concurrencymeta.increment and not getattr(model_instance, '_concurrency_disable_increment', False)): new_version = field._get_next_version(model_instance) values[i] = (field, _1, new_version) field._set_version_value(model_instance, new_version) # else: # new_version = old_version break # This provides a default if either (1) no values were provided or (2) we reached this code as part of a # create. We don't need to worry about a race condition because a competing create should produce an # error anyway. updated = base_qs.filter(pk=pk_val).exists() # This second situation can occur because `Model.save_base` calls `Model._save_parent` without relaying # the `force_insert` flag that marks the process as a create. Eventually, `Model._save_table` will call # this function as-if it were in the middle of an update. The update is expected to fail because there # is no object to update and the caller will fall back on the create logic instead. We need to ensure # the update fails (but does not raise an exception) under this circumstance by skipping the concurrency # logic. if values and updated: if (model_instance._concurrencymeta.enabled and conf.ENABLED and not getattr(model_instance, '_concurrency_disabled', False) and (old_version or not conf.IGNORE_DEFAULT)): filter_kwargs = {'pk': pk_val, version_field.attname: old_version} updated = base_qs.filter(**filter_kwargs)._update(values) >= 1 if not updated: version_field._set_version_value(model_instance, old_version) updated = conf._callback(model_instance) else: filter_kwargs = {'pk': pk_val} updated = base_qs.filter(**filter_kwargs)._update(values) >= 1 return updated
def test_meta_inheritance(self): # TestModelWithCustomOptions extends ConcurrentModel # but we disabled concurrency only in TestModelWithCustomOptions import concurrency.api as api concurrency_enabled1 = SimpleConcurrentModel.objects.get_or_create(**{'username': '******'})[0] concurrency_enabled2 = SimpleConcurrentModel.objects.get_or_create(**{'username': '******'})[0] v1 = api.get_revision_of_object(concurrency_enabled1) v2 = api.get_revision_of_object(concurrency_enabled2) assert v1 == v2, "got same row with different version (%s/%s)" % (v1, v2) concurrency_enabled1.save() assert concurrency_enabled1.pk is not None # sanity check self.assertRaises(RecordModifiedError, concurrency_enabled2.save) concurrency_disabled1 = ConcurrencyDisabledModel.objects.get_or_create(**{'username': '******'})[0] concurrency_disabled2 = ConcurrencyDisabledModel.objects.get_or_create(**{'username': '******'})[0] v1 = api.get_revision_of_object(concurrency_disabled1) v2 = api.get_revision_of_object(concurrency_disabled2) assert v1 == v2, "got same row with different version (%s/%s)" % (v1, v2) concurrency_disabled1.save() assert concurrency_disabled1.pk is not None # sanity check v1 = api.get_revision_of_object(concurrency_disabled1) v2 = api.get_revision_of_object(concurrency_disabled2) assert v1 != v2
def test_concurrency_safety(self): import concurrency.api as api target = self.concurrency_model() version = api.get_revision_of_object(target) self.assertFalse(bool(version), "version is not null %s" % version)
def _increment_version_number(obj): old_value = get_revision_of_object(obj) setattr(obj, obj._concurrencymeta.field.attname, int(old_value) + 1)
def test_get_revision_of_object(model_class=SimpleConcurrentModel): instance = model_class(username=next(nextname)) instance.save() assert get_revision_of_object(instance) == instance.version
def version(obj): """ returns the value of the VersionField of the passed object """ return get_revision_of_object(obj)
def test_get_revision_of_object(self): o1 = TestModel0.objects.create() self.assertEqual(get_revision_of_object(o1), o1.version)