Code Monkey home page Code Monkey logo

django-concurrency's People

Contributors

blueyed avatar boscomw avatar charettes avatar claytondaley avatar dmytrolitvinov avatar domdinicola avatar gitter-badger avatar haos616 avatar movermeyer avatar mrc75 avatar naddiseo avatar requires avatar richardasymmetric avatar saxix avatar sobolevn avatar tdruez avatar ticosax avatar zniper avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-concurrency's Issues

Tag releases

For some reason, github shows the broken release 1.3, hides 1.3.1 and 1.3.2, and it looks like 1.4 is not tagged.

Could you tag current and future releases? Thanks!

South migration not working

I am trying to integrate django-concurrency into my django project (v 1.6), but whenever I run schemamigration for south it gives the following error:

 ! Cannot freeze field 'shop.variation.version'
 ! (this field has class concurrency.fields.IntegerVersionField)

 ! South cannot introspect some fields; this is probably because they are custom
 ! fields. If they worked in 0.6 or below, this is because we have removed the
 ! models parser (it often broke things).
 ! To fix this, read http://south.aeracode.org/wiki/MyFieldsDontWork

Handle custom action with ConcurrencyActionMixin in a more generic way

Is it possible to make the ConcurrencyActionMixin handle custom actions out of the box, without having to override templates with the "identity" tag?

I'm using actions that are working in both modes, concurrency and no concurrency, I'd like to avoid to change and duplicate all my action related template.

Do you think this logic could be done in the Mixin?

Thanks.

Problem with restoring reversion

Thanks for nice concurency - It is pretty useful. I have one small problem caused probably by my insufficient knowledge. I am using reversion v2.0.12 with concurrency 1.4 on Django 1.8.18.3
I am getting Record Modified error when I try to restore some older version from history.
Please could you help?
Thanks David

Here is my admin code:

from reversion.admin import VersionAdmin
from .models import *
from concurrency.admin import ConcurrentModelAdmin
from concurrency.api import disable_concurrency

class OffersAdmin(VersionAdmin, ConcurrentModelAdmin):
inlines = [
Accomodation,
Food,
]
@disable_concurrency()
def recover_view(self, request, version_id, extra_context=None):
return super(ReversionConcurrentModelAdmin, self).recover_view(request, version_id, extra_context)

Incorrect default for IntegerVersionField

All VersionField's has default = 0
For IntegerVersionField it's incorrect and cause the following issue:
Concurrency silently doesn't work for first time object editing after django-concurrency implementation.
When I load one object (with IntegerVersionField value = 0) in two tabs and save 1st, then 2nd, 1st editing will be silently oveddided.

your example is off

You get the same model instance twice and save each and you get a concurrency error? Why exactly? You haven't changed any fields! Optimistic concurrency means detecting changes to an object at save/commit that overwrite concurrent changes. It generally requires keeping track of mutations of the object somehow or using a shadow object. If this really works like the example it is broken by design.

AttributeError: 'Configuration' object has no attribute '_concurrencymeta'

Hi there,

I'm using Django 1.6.8 and django_concurrency-0.8.1. I've tried to follow the documentation as best as I can, but without examples it's tough to determine if I've set it up correctly in my Admin interface.

I've changed my ConfigurationAdmin class so that it inherits from ConcurrentModelAdmin as the documentation suggests.

class ConfigurationAdmin(ConcurrentModelAdmin):

    form = forms.ConfigurationForm
    change_list_template = 'admin/configuration/change_list.html'

    ...

I also changed {{ obj.pk }} to {{ obj|identity }} in delete_selected_confirmation.html as directed in the docs. I haven't changed anything else, and am getting this exception when I go into the Configurations page. (Listing of the Configuration objects which I'm trying to add concurrency to.)

Internal Server Error: /test/common/configuration/
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 137, in get_response
    response = response.render()
  File "/usr/local/lib/python2.7/dist-packages/django/template/response.py", line 105, in render
    self.content = self.rendered_content
  File "/usr/local/lib/python2.7/dist-packages/django/template/response.py", line 82, in rendered_content
    content = template.render(context)
  File "/usr/local/lib/python2.7/dist-packages/django/template/base.py", line 140, in render
    return self._render(context)
  File "/usr/local/lib/python2.7/dist-packages/django/test/utils.py", line 85, in instrumented_test_render
    return self.nodelist.render(context)
  File "/usr/local/lib/python2.7/dist-packages/django/template/base.py", line 840, in render
    bit = self.render_node(node, context)
  File "/usr/local/lib/python2.7/dist-packages/django/template/debug.py", line 78, in render_node
    return node.render(context)
  File "/usr/local/lib/python2.7/dist-packages/django/template/loader_tags.py", line 123, in render
    return compiled_parent._render(context)
  File "/usr/local/lib/python2.7/dist-packages/django/test/utils.py", line 85, in instrumented_test_render
    return self.nodelist.render(context)
  File "/usr/local/lib/python2.7/dist-packages/django/template/base.py", line 840, in render
    bit = self.render_node(node, context)
  File "/usr/local/lib/python2.7/dist-packages/django/template/debug.py", line 78, in render_node
    return node.render(context)
  File "/usr/local/lib/python2.7/dist-packages/django/template/loader_tags.py", line 123, in render
    return compiled_parent._render(context)
  File "/usr/local/lib/python2.7/dist-packages/django/test/utils.py", line 85, in instrumented_test_render
    return self.nodelist.render(context)
  File "/usr/local/lib/python2.7/dist-packages/django/template/base.py", line 840, in render
    bit = self.render_node(node, context)
  File "/usr/local/lib/python2.7/dist-packages/django/template/debug.py", line 78, in render_node
    return node.render(context)
  File "/usr/local/lib/python2.7/dist-packages/django/template/loader_tags.py", line 123, in render
    return compiled_parent._render(context)
  File "/usr/local/lib/python2.7/dist-packages/django/test/utils.py", line 85, in instrumented_test_render
    return self.nodelist.render(context)
  File "/usr/local/lib/python2.7/dist-packages/django/template/base.py", line 840, in render
    bit = self.render_node(node, context)
  File "/usr/local/lib/python2.7/dist-packages/django/template/debug.py", line 78, in render_node
    return node.render(context)
  File "/usr/local/lib/python2.7/dist-packages/django/template/loader_tags.py", line 62, in render
    result = block.nodelist.render(context)
  File "/usr/local/lib/python2.7/dist-packages/django/template/base.py", line 840, in render
    bit = self.render_node(node, context)
  File "/usr/local/lib/python2.7/dist-packages/django/template/debug.py", line 78, in render_node
    return node.render(context)
  File "/usr/local/lib/python2.7/dist-packages/django/template/loader_tags.py", line 62, in render
    result = block.nodelist.render(context)
  File "/usr/local/lib/python2.7/dist-packages/django/template/base.py", line 840, in render
    bit = self.render_node(node, context)
  File "/usr/local/lib/python2.7/dist-packages/django/template/debug.py", line 78, in render_node
    return node.render(context)
  File "/usr/local/lib/python2.7/dist-packages/django/template/base.py", line 1196, in render
    _dict = func(*resolved_args, **resolved_kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/templatetags/admin_list.py", line 288, in result_list
    'results': list(results(cl))}
  File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/templatetags/admin_list.py", line 266, in results
    yield ResultList(None, items_for_result(cl, res, None))
  File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/templatetags/admin_list.py", line 258, in __init__
    super(ResultList, self).__init__(*items)
  File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/templatetags/admin_list.py", line 185, in items_for_result
    f, attr, value = lookup_field(field_name, result, cl.model_admin)
  File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/util.py", line 254, in lookup_field
    value = attr(obj)
  File "/usr/local/lib/python2.7/dist-packages/concurrency/admin.py", line 37, in action_checkbox
    get_revision_of_object(obj))))
  File "/usr/local/lib/python2.7/dist-packages/concurrency/api.py", line 23, in get_revision_of_object
    return getattr(obj, get_version_fieldname(obj))
  File "/usr/local/lib/python2.7/dist-packages/concurrency/core.py", line 22, in get_version_fieldname
    return obj._concurrencymeta._field.attname
AttributeError: 'Configuration' object has no attribute '_concurrencymeta'
[21/Nov/2014 20:29:51] "GET /test/common/configuration/ HTTP/1.1" 500 368613

Does anyone know how to resolve this? Thanks!

Enhancement: Adding support for Admin list with list_editable

The current code does not support list_editable Admin config.
Saving a list with list_editable will not trigger any concurrency exception.

This is BTW a long standing bug in Django https://code.djangoproject.com/ticket/11313 (and a few related bugs closed as duplicates too)

So what would be the best approach for this?
Throwing out some thoughts:
Overrride the model admin form to make some checks?
Possibly, on each form in the editable list:

  • No change were done: Make sure we do not .save()
  • Changes with proper concurrency validation: Model.save()
  • Changes but do not validate the concurrency: Raise the proper form errors and display those in the list (make it clear for the User that something was not saved and why)

What would be your take on this?

Using ConcurrencyTestMixin leads to DeprecationWarning

For a test case like:

class OurTests(TransactionTestCase, ConcurrencyTestMixin):
    # ...

Sample output.

Creating test database for alias 'default'...
/Users/evdb/foo/bar/.venv-2.6/lib/python2.6/site-packages/concurrency/utils.py:89: DeprecationWarning: get_revision_of_object is deprecated; use concurrency.api.get_revision_of_object instead
  v1 = get_revision_of_object(target)
/Users/evdb/foo/bar/.venv-2.6/lib/python2.6/site-packages/concurrency/utils.py:90: DeprecationWarning: get_revision_of_object is deprecated; use concurrency.api.get_revision_of_object instead
  v2 = get_revision_of_object(target_copy)
../Users/evdb/foo/bar/.venv-2.6/lib/python2.6/site-packages/concurrency/utils.py:98: DeprecationWarning: get_revision_of_object is deprecated; use concurrency.api.get_revision_of_object instead
  version = get_revision_of_object(target)
.
----------------------------------------------------------------------
Ran 3 tests in 2.482s

I believe that the fix is simply to change the method called in the testing mixin to the not deprecated one.

No "1 demo model was changed successfully." should be sure when concurrency revert works

When list editting, it shows:

Record with pk `1` has been modified and was not updated
1 demo model was changed successfully.

Actually the last message "1 demo model was changed successfully." is not accurate, because the second list editting doesn't not happen(changed successfully) due to the concurrency limit. So in this case, it should be "0 demo model was changed successfully.".

Field used for concurrency can only be named version

And if a model already has a filed named version, there is a clash possible when using an abstract model with a concurrency version filed named cv and a subclass that has a version field.

Here is the example problem:
Model is:
class Package(models.Model): name = models.CharField(max_length=255) version = models.CharField(max_length=255) concurrency_version = IntegerVersionField()
and the simple code that shows my version field being overwritten:
from test.models import Package p=Package(name='a', version='1.2.3') p.save() Package.objects.all()[0].version u'1354609388041623'
The version filed now is u'1354609388041623' and not u'1.2.3' as expected....

Can't upgrade django-concurrency to 1.0

Collecting django-concurrency==1.0 (from -r requirements.txt (line 41))
  Using cached django-concurrency-1.0.tar.gz
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 20, in <module>
      File "/tmp/pip-build-pMvY07/django-concurrency/setup.py", line 83, in <module>
        name=app.NAME,
    AttributeError: 'module' object has no attribute 'NAME'

    ----------------------------------------

Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-pMvY07/django-concurrency

Trying to upgrade django-concurrency but it's failing. Only django concurrency has this issue, all the other packages are fine.

get next version return null when IGNORE_DEFAULT set to False

Hi,
get next version return null when IGNORE_DEFAULT set to False, old_value is null, by right it should be 0

File "...\Anaconda2\lib\site-packages\concurrency\fields.py", line 182, in _do_update
new_version = field._get_next_version(model_instance)
File "...\Anaconda2\lib\site-packages\concurrency\fields.py", line 235, in _get_next_version
return max(int(old_value) + 1, (int(time.time() * 1000000) - OFFSET))
ValueError: invalid literal for int() with base 10: ''

loaddata management command fails with RecordModifiedError

We tried to use dumpdata and loaddata to set up a realistic baseline developer database, and got errors because dumpdata carries the versions with it.

It would be nice to have a workaround for this. If you're interested, we could look into a pull request, but we'd want to make sure it fits with your overall vision.

I see some related issues like #13 so if you feel this is a closed question from django-concurrency's perspective, just say so.

Rejecting updates without a version

I haven't looked into the internals to see if this is difficult or even possible - but I'd like to start a discussion on the subject.

I'd like to be able to raise an exception if a versioned instance is modified without supplying a version value. This would close a loophole where there is some method or API call that I've forgotten to update to handle concurrency checks.

Our use case is a large complex codebase where we want to quickly add baseline protection against concurrent updates - a strict 'fail fast' in any situation where there is a potential for conflicts.

Reversion support

The doc mentions that Django Reversion is supported. Is there any doc to explains how this works?

Question: is Django 2.0 fully supported?

Hi Stefano,

I see a commit ( a1bcf17 ) that added support for Django 2. But on the readme, it still says

Supported Django versions: 1.8.x, 1.9.x, 1.10.x., 1.11.x

I'm wondering if it's safe to start my new project with Django 2

Thank you!

does django-concurrency support front end update?

Hi,

I configured django-concurrency for my app, and it works for the model in admin site, but from my own custom detail template html page which calls view function to update/save model, it does not work, how to configure to support this? thanks.

update_fields

How does the use of update_fields on model save work? Does it just work? Is there any caveats?

Nothing obvious in the docs.

TransactionManagementError during save.

Hi there,

Versions: Django 1.6.8 and django-concurrency 0.8.1, with MySQL 5.5.40.

I'm developing on a Ubuntu VM. On a clone of this VM, I have added django-concurrency to an object called Configuration on my admin page. After doing so, whenever I use this VM and add more than about 3 new 1-to-many relationships to this Configuration object, I get a TransactionManagementError. This does not happen on the old VM with identical code, and without django-concurrency.

The MySQL logs just show that after a couple of the INSERTs, a rollback command was issued. The database has no exceptions or other information. Here is the exception from Django:

#some INSERTS
Internal Server Error: /test/common/configuration/6/
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 112, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/options.py", line 466, in wrapper
    return self.admin_site.admin_view(view)(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/utils/decorators.py", line 99, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/views/decorators/cache.py", line 52, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/sites.py", line 198, in inner
    return view(request, *args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/utils/decorators.py", line 29, in _wrapper
    return bound_func(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/utils/decorators.py", line 99, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/utils/decorators.py", line 25, in bound_func
    return func(self, *args2, **kwargs2)
  File "/usr/local/lib/python2.7/dist-packages/django/db/transaction.py", line 371, in inner
    return func(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/options.py", line 1267, in change_view
    self.log_change(request, new_object, change_message)
  File "/usr/local/lib/python2.7/dist-packages/concurrency/admin.py", line 205, in log_change
    super(ConcurrencyListEditableMixin, self).log_change(request, object, message)
  File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/options.py", line 650, in log_change
    change_message=message
  File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/models.py", line 20, in log_action
    e.save()
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 545, in save
    force_update=force_update, update_fields=update_fields)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 573, in save_base
    updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 654, in _save_table
    result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 687, in _do_insert
    using=using, raw=raw)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/manager.py", line 232, in _insert
    return insert_query(self.model, objs, fields, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 1514, in insert_query
    return query.get_compiler(using=using).execute_sql(return_id)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 903, in execute_sql
    cursor.execute(sql, params)
  File "/usr/local/lib/python2.7/dist-packages/django/db/backends/util.py", line 69, in execute
    return super(CursorDebugWrapper, self).execute(sql, params)
  File "/usr/local/lib/python2.7/dist-packages/django/db/backends/util.py", line 47, in execute
    self.db.validate_no_broken_transaction()
  File "/usr/local/lib/python2.7/dist-packages/django/db/backends/__init__.py", line 372, in validate_no_broken_transaction
    "An error occurred in the current transaction. You can't "
TransactionManagementError: An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.
Internal Server Error: /test/common/configuration/6/
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 112, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/options.py", line 466, in wrapper
    return self.admin_site.admin_view(view)(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/utils/decorators.py", line 99, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/views/decorators/cache.py", line 52, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/sites.py", line 198, in inner
    return view(request, *args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/utils/decorators.py", line 29, in _wrapper
    return bound_func(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/utils/decorators.py", line 99, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/utils/decorators.py", line 25, in bound_func
    return func(self, *args2, **kwargs2)
  File "/usr/local/lib/python2.7/dist-packages/django/db/transaction.py", line 371, in inner
    return func(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/options.py", line 1267, in change_view
    self.log_change(request, new_object, change_message)
  File "/usr/local/lib/python2.7/dist-packages/concurrency/admin.py", line 205, in log_change
    super(ConcurrencyListEditableMixin, self).log_change(request, object, message)
  File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/options.py", line 650, in log_change
    change_message=message
  File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/models.py", line 20, in log_action
    e.save()
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 545, in save
    force_update=force_update, update_fields=update_fields)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 573, in save_base
    updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 654, in _save_table
    result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 687, in _do_insert
    using=using, raw=raw)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/manager.py", line 232, in _insert
    return insert_query(self.model, objs, fields, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 1514, in insert_query
    return query.get_compiler(using=using).execute_sql(return_id)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 903, in execute_sql
    cursor.execute(sql, params)
  File "/usr/local/lib/python2.7/dist-packages/django/db/backends/util.py", line 69, in execute
    return super(CursorDebugWrapper, self).execute(sql, params)
  File "/usr/local/lib/python2.7/dist-packages/django/db/backends/util.py", line 47, in execute
    self.db.validate_no_broken_transaction()
  File "/usr/local/lib/python2.7/dist-packages/django/db/backends/__init__.py", line 372, in validate_no_broken_transaction
    "An error occurred in the current transaction. You can't "
TransactionManagementError: An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.
[25/Nov/2014 16:33:57] "POST /test/common/configuration/6/ HTTP/1.1" 500 307743

I've added this code to the project so far:

from concurrency.admin import ConcurrentModelAdmin
...
class ConfigurationAdmin(ConcurrentModelAdmin):

    form = forms.ConfigurationForm
    change_list_template = 'admin/configuration/change_list.html'
...
from concurrency.fields import AutoIncVersionField
class Configuration(models.Model):
    """
    Database Model for attaching :class:`Resource` objects to
    :class:`Product` objects.
    """
    objects = ConfigurationManager()
    version = AutoIncVersionField()
    #version = models.CharField(max_length=128, verbose_name='config version')
...
    {% for obj in queryset %}
    <input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj|identity }}" />
    {% endfor %}

Not sure what the problem could be. Any help would be greatly appreciated. Thanks!

Triggers not created from abstract models

This may also be related to issue #78

From what I can tell, it may be related to having a TriggerVersionField in an abstract base class, which causes django to create all the version fields with the same creation_counter. The creation_counter is used in __hash__, which is called via set(_TRIGGERS) in the create_triggers function.

I've been having trouble writing a specific test for this, but I did manage to reproduce in another repo

Installation fails on Python 3.7.0b1

Installation fails on Python 3.7.0b1.

% pip install django-concurrency
Collecting django-concurrency==1.4 (from -r requirements/base.txt (line 29))
  Using cached django-concurrency-1.4.tar.gz                                                                                                                 
    Complete output from command python setup.py egg_info:
                                                                                                                                                       
    Installed /tmp/easy_install-s5x7gw65/pytest-runner-3.0/.eggs/setuptools_scm-1.15.7-py3.7.egg
    zip_safe flag not set; analyzing archive contents...                                                                                                  
    Traceback (most recent call last):
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/sandbox.py", line 157, in save_modules
        yield saved                
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/sandbox.py", line 198, in setup_context
        yield                                    
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/sandbox.py", line 248, in run_setup
        DirectorySandbox(setup_dir).run(runner)
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/sandbox.py", line 278, in run
        return func()        
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/sandbox.py", line 246, in runner
        _execfile(setup_script, ns)
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/sandbox.py", line 47, in _execfile     
        exec(code, globals, locals)                            
      File "/tmp/easy_install-s5x7gw65/pytest-runner-3.0/setup.py", line 68, in <module>                                                                              
        'License :: OSI Approved :: BSD License',     
      File "…/pyenv/3.7.0b1/lib/python3.7/distutils/core.py", line 148, in setup
        dist.run_commands()
      File "…/pyenv/3.7.0b1/lib/python3.7/distutils/dist.py", line 966, in run_commands
        self.run_command(cmd)
      File "…/pyenv/3.7.0b1/lib/python3.7/distutils/dist.py", line 985, in run_command
        cmd_obj.run()
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/command/bdist_egg.py", line 209, in run
        os.path.join(archive_root, 'EGG-INFO'), self.zip_safe()
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/command/bdist_egg.py", line 245, in zip_safe
        return analyze_egg(self.bdist_dir, self.stubs)
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/command/bdist_egg.py", line 355, in analyze_egg
        safe = scan_module(egg_dir, base, name, stubs) and safe
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/command/bdist_egg.py", line 392, in scan_module
        code = marshal.load(f)
    ValueError: bad marshal data (unknown type code)

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-build-h66zo67p/django-concurrency/setup.py", line 83, in <module>
        platforms=['any'],
      File "…/pyenv/3.7.0b1/lib/python3.7/distutils/core.py", line 108, in setup
        _setup_distribution = dist = klass(attrs)
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/dist.py", line 315, in __init__
        self.fetch_build_eggs(attrs['setup_requires'])
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/dist.py", line 361, in fetch_build_eggs
        replace_conflicting=True,
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/pkg_resources/__init__.py", line 850, in resolve
        dist = best[req.key] = env.best_match(req, ws, installer)
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/pkg_resources/__init__.py", line 1122, in best_match
        return self.obtain(req, installer)                                         
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/pkg_resources/__init__.py", line 1134, in obtain
        return installer(requirement)                                             
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/dist.py", line 429, in fetch_build_egg
        return cmd.easy_install(req)                                                           
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/command/easy_install.py", line 665, in easy_install
        return self.install_item(spec, dist.location, tmpdir, deps)
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/command/easy_install.py", line 695, in install_item
        dists = self.install_eggs(spec, download, tmpdir)
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/command/easy_install.py", line 876, in install_eggs
        return self.build_and_install(setup_script, setup_base)
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/command/easy_install.py", line 1115, in build_and_install
        self.run_setup(setup_script, setup_base, args)
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/command/easy_install.py", line 1101, in run_setup
        run_setup(setup_script, args)
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/sandbox.py", line 251, in run_setup
        raise
      File "…/pyenv/3.7.0b1/lib/python3.7/contextlib.py", line 130, in __exit__
        self.gen.throw(type, value, traceback)
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/sandbox.py", line 198, in setup_context
        yield
      File "…/pyenv/3.7.0b1/lib/python3.7/contextlib.py", line 130, in __exit__
        self.gen.throw(type, value, traceback)
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/sandbox.py", line 169, in save_modules
        saved_exc.resume()
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/sandbox.py", line 144, in resume
        six.reraise(type, exc, self._tb)
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/pkg_resources/_vendor/six.py", line 685, in reraise
        raise value.with_traceback(tb)
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/sandbox.py", line 157, in save_modules
        yield saved
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/sandbox.py", line 198, in setup_context
        yield
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/sandbox.py", line 248, in run_setup
        DirectorySandbox(setup_dir).run(runner)
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/sandbox.py", line 278, in run
        return func()
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/sandbox.py", line 246, in runner
        _execfile(setup_script, ns)
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/sandbox.py", line 47, in _execfile
        exec(code, globals, locals)
      File "/tmp/easy_install-s5x7gw65/pytest-runner-3.0/setup.py", line 68, in <module>
        'License :: OSI Approved :: BSD License',
      File "…/pyenv/3.7.0b1/lib/python3.7/distutils/core.py", line 148, in setup
        dist.run_commands()
      File "…/pyenv/3.7.0b1/lib/python3.7/distutils/dist.py", line 966, in run_commands
        self.run_command(cmd)
      File "…/pyenv/3.7.0b1/lib/python3.7/distutils/dist.py", line 985, in run_command
        cmd_obj.run()
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/command/bdist_egg.py", line 209, in run
        os.path.join(archive_root, 'EGG-INFO'), self.zip_safe()
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/command/bdist_egg.py", line 245, in zip_safe
        return analyze_egg(self.bdist_dir, self.stubs)
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/command/bdist_egg.py", line 355, in analyze_egg
        safe = scan_module(egg_dir, base, name, stubs) and safe
      File "…/pyenv/3.7.0b1/envs/tmp-3.7.0b1-project-1FLUQD/lib/python3.7/site-packages/setuptools/command/bdist_egg.py", line 392, in scan_module
        code = marshal.load(f)
    ValueError: bad marshal data (unknown type code)

django-concurrency not working if model has a save method

This is a subtle heisenbug.

In short, if you have a model

class MyModel(models.Model):
    version = IntegerVersionField()

    def save(*args, **kwargs):
        bla-bla-bla

then sometimes save is wrapped by django-concurrency and all works fine, but sometimes save is not wrapped, and there is effectively no concurrency check.

More precisely, sometimes depends on hash seed of python interpreter. I.e. in my setup all is working if PYTHONHASHSEED=42 and if PYTHONHASHSEED=2 then concurrency is broken.

If a model doesn't define a custom save method, then there is no such weird behaviour.

I think I've spotted the cause of the bug. The problem is that save gets wrapped inside contribute_to_class https://github.com/saxix/django-concurrency/blob/develop/concurrency/fields.py#L67.

And contribute_to_class is called by Model metaclass here https://github.com/django/django/blob/master/django/db/models/base.py#L155.

Note that add_to_class (https://github.com/django/django/blob/master/django/db/models/base.py#L285) calls contribute_to_class if the argument has this method, or just setattr otherwise.

And add_to_class is called for all attributes of the model, including version field and save method, if model defines one. So if add_to_class is called on save after add_to_class is called on version, wrapped model save gets overwritten with plain save. The order of version and save depends on iteration order of attrs dict, which depends on PYTHONHASHSEED.

The proposed fix is to rewrite contribute_to_class in such a way that wrapping of save method is bound to class_prepared signal. Something similar is done in django simple history here: https://github.com/treyhunner/django-simple-history/blob/master/simple_history/models.py#L44

Enhancement: Add admin UI for resolving concurrency exceptions

It would be great to have a basic admin ui to resolve concurrency exceptions....
Something that could show:

  • The fields before editing by the current user
  • The fields as edited by the current user
  • The fields as they were edited by others
    And provide the ability to resolve and save a new object with the conflicts manually resolved ...

triggers not created during tests

Hi,

Is there anything special to do to get the triggers to be created when running the tests?

When I run the tests on my project, I get this

WARNINGS:
?: (concurrency.W001) Missed trigger for field beyond.User.version

When I check the test database, the triggers are never created.

I am using django 1.11 and I have tried with the latest commit: 2c92198

Thanks

Don't use `objects` manager to retrieve an object

in https://github.com/saxix/django-concurrency/blob/develop/concurrency/core.py#L48

model_instance.__class__.objects is used to get a manager. However this has two drawbacks:

I suggest using model_instance.class._base_manager or model_instance.class._default_manager but I'm not sure because these are underscored private attributes...

ConcurrencyListEditableMixin is not thread safe

The variable _concurrency_list_editable_errors should not be stored on the ModelAdmin instance as it's shared by all the Users. I would suggest to store it on the "request" instead as it's unique for each User and each Request.

Also, initializing it in the changelist_view() method is not great as it requires to go through that view before the other views can work. Note that moving the logic for _concurrency_list_editable_errors to the "request" and initializing it in a better "entry point" method will solve this issue too.

DB OperationalError when using the trigger-based field

Hello

When using the trigger-based field (TriggerVersionField), I get a django.db.utils.OperationalError: server closed the connection unexpectedly error after the first RecordModifiedError was raised.
I do not have this issue using the AutoIncVersionField field but I cannot reproduce this issue in the django shell either so I am not sure what is causing it.
Could there be an issue in the trigger? Have you ever had this issue? Any idea?

Thanks


django==1.9.1
django-concurrency==1.3.1
PostgreSQL 9.5.3

Expected RecordModifiedError on user.save()

22:56:15 worker3.1       |   File "/var/app/venv/lib/python3.5/site-packages/concurrency/fields.py", line 208, in _do_update
22:56:15 worker3.1       |     updated = conf._callback(model_instance)
22:56:15 worker3.1       |   File "/var/app/venv/lib/python3.5/site-packages/concurrency/views.py", line 19, in callback
22:56:15 worker3.1       |     raise RecordModifiedError(_('Record has been modified'), target=target)
22:56:15 worker3.1       | concurrency.exceptions.RecordModifiedError: Record has been modified
22:56:15 worker3.1       |

Then a User.objects.get(id=id) raises:

22:57:12 worker2.1       |   File "/var/app/venv/lib/python3.5/site-packages/django/db/models/manager.py", line 122, in manager_method
22:57:12 worker2.1       |     return getattr(self.get_queryset(), name)(*args, **kwargs)
22:57:12 worker2.1       |   File "/var/app/venv/lib/python3.5/site-packages/django/db/models/query.py", line 381, in get
22:57:12 worker2.1       |     num = len(clone)
22:57:12 worker2.1       |   File "/var/app/venv/lib/python3.5/site-packages/django/db/models/query.py", line 240, in __len__
22:57:12 worker2.1       |     self._fetch_all()
22:57:12 worker2.1       |   File "/var/app/venv/lib/python3.5/site-packages/django/db/models/query.py", line 1074, in _fetch_all
22:57:12 worker2.1       |     self._result_cache = list(self.iterator())
22:57:12 worker2.1       |   File "/var/app/venv/lib/python3.5/site-packages/django/db/models/query.py", line 52, in __iter__
22:57:12 worker2.1       |     results = compiler.execute_sql()
22:57:12 worker2.1       |   File "/var/app/venv/lib/python3.5/site-packages/django/db/models/sql/compiler.py", line 848, in execute_sql
22:57:12 worker2.1       |     cursor.execute(sql, params)
22:57:12 worker2.1       |   File "/var/app/venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 79, in execute
22:57:12 worker2.1       |     return super(CursorDebugWrapper, self).execute(sql, params)
22:57:12 worker2.1       |   File "/var/app/venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 64, in execute
22:57:12 worker2.1       |     return self.cursor.execute(sql, params)
22:57:12 worker2.1       |   File "/var/app/venv/lib/python3.5/site-packages/django/db/utils.py", line 95, in __exit__
22:57:12 worker2.1       |     six.reraise(dj_exc_type, dj_exc_value, traceback)
22:57:12 worker2.1       |   File "/var/app/venv/lib/python3.5/site-packages/django/utils/six.py", line 685, in reraise
22:57:12 worker2.1       |     raise value.with_traceback(tb)
22:57:12 worker2.1       |   File "/var/app/venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 64, in execute
22:57:12 worker2.1       |     return self.cursor.execute(sql, params)
22:57:12 worker2.1       | django.db.utils.OperationalError: server closed the connection unexpectedly
22:57:12 worker2.1       |      This probably means the server terminated abnormally
22:57:12 worker2.1       |      before or while processing the request.
22:57:12 worker2.1       |

Add support Django 1.8

Need update to support Django1.8:
concurrency\forms.py:7: RemovedInDjango19Warning: django.utils.importlib will be removed in Django 1.9.
from django.utils.importlib import import_module

concurrency\utils.py:7: RemovedInDjango19Warning: The utilities in django.db.models.loading are deprecated in favor of the new application loading system.
from django.db.models.loading import get_models, get_apps

South 0.76 schemamigration <module> --auto

Hi Saxix,

just wanted to add a version lock field to an existing model with several existing migrations and got.
... models.py
behind the class defintion.

from concurrency import fields
lock_version = fields.AutoIncVersionField()
lock_version.contribute_to_class(Account, 'lock_version')

manage.py schemamigration company --auto
produces this stacktrace

! Cannot freeze field 'company.account.lock_version'
! (this field has class concurrency.fields.AutoIncVersionField)

! South cannot introspect some fields; this is probably because they are custom

! fields. If they worked in 0.6 or below, this is because we have removed the
! models parser (it often broke things).
! To fix this, read http://south.aeracode.org/wiki/MyFieldsDontWork

Initial Update

The bot created this issue to inform you that pyup.io has been set up on this repo.
Once you have closed it, the bot will open pull requests for updates as soon as they are available.

Feature request: ConditionalVersionField

Such field may work like this:

            fields = self.get_ool_fields()
            exclude = self.get_ool_exclusions()
            data = model_to_dict(self, fields=fields, exclude=exclude, filter_not_editable=False)

            # Resolve nested resources
            if isinstance(fields, (list, tuple)):
                for source in fields:
                    try:
                        value = self
                        for component in source.split('.'):
                            if value is None:
                                break
                            value = get_component(value, component)
                    except ObjectDoesNotExist:
                        continue

                    if is_simple_callable(getattr(value, 'all', None)):
                        value = [item.pk for item in value.all()]

                    data[source] = value
            data = OrderedDict(sorted(data.items()))
            version = hashlib.sha1(force_text(data)).hexdigest()

so based on model settings it's generate hash for a set of fields values.
this may be very handy in case one want to bump a version only if business critical fields have changed (helps when model updates often)

Let IntegerVersionField starts with '1'

Suppose we have a model,

class User(models.Model):
    version = AutoIncVersionField()

    id = models.UUIDField(primary_key=True, default=uuid.uuid4)
    name = models.CharField(max_length=255)

If we create the object using create, we are getting the expected version value(which is 1).

User.objects.create(name='name')

But instead of that if we create the model using the initializer and create it using the save() method. The version is 2.

user = User(name='name')
user.save()

Add demo model action in the demo project of django concurrency fails

When i add a new "Demo models" in "Demoapp", the following error happens:

MultiValueDictKeyError at /admin/demoapp/demomodel/add/

"Key u'_concurrency_version_None' not found in <QueryDict: {u'char': [u'abc'], u'integer': [u'2'], u'csrfmiddlewaretoken': [u'fMPewSuw05AdKheUcbSjrah0myrrgdEh'], u'version': [u''], u'_save': [u'Save']}>"

Request Method: POST
Request URL: http://127.0.0.1:8000/admin/demoapp/demomodel/add/
Django Version: 1.5.1
Exception Type: MultiValueDictKeyError
Exception Value:

"Key u'_concurrency_version_None' not found in <QueryDict: {u'char': [u'abc'], u'integer': [u'2'], u'csrfmiddlewaretoken': [u'fMPewSuw05AdKheUcbSjrah0myrrgdEh'], u'version': [u''], u'_save': [u'Save']}>"

Exception Location: C:\Python27\lib\site-packages\django\utils\datastructures.py in getitem, line 295
Python Executable: C:\Python27\python.exe
Python Version: 2.7.0
Python Path:

['E:\gitpro\django-concurrency\demo',
'C:\Windows\system32\python27.zip',
'C:\Python27\DLLs',
'C:\Python27\lib',
'C:\Python27\lib\plat-win',
'C:\Python27\lib\lib-tk',
'C:\Python27',
'C:\Python27\lib\site-packages',

documentation: admin fields/fieldsets override

If you use ConcurrentModelAdmin , and you manually set fieldsets or fields in admin, you must ensure the version field gets added to the admin. Otherwise concurrency checks will not be enforced.

This small detail took me almost 2 hours to discover. 1 line of documentation would protect future users from this.

Btw: thank you for a very cool library 👍

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.