jedie / django-reversion-compare

Add compare view to django-reversion for comparing two versions of a reversion model.
https://pypi.org/project/django-reversion-compare/
315 stars 105 forks source link

invalid literal for int() with base 10: 'F' #44

Closed linevych closed 8 years ago

linevych commented 9 years ago

I was trying to compare two versions and got an error:

Environment:

Request Method: GET
Request URL: http://127.0.0.1:10801/uk/admin/blog/post/4/history/compare/?version_id2=2&version_id1=1

Django Version: 1.8.3
Python Version: 3.4.0
Installed Applications:
('djangocms_admin_style',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.sites',
 'cmsplugin_cascade',
 'cmsplugin_cascade.extra_fields',
 'cmsplugin_cascade.sharable',
 'cms',
 'treebeard',
 'menus',
 'sekizai',
 'bootstrap3',
 'solo',
 'easy_thumbnails',
 'filer',
 'modeltranslation',
 'disqus',
 'django_select2',
 'easy_select2',
 'taggit',
 'mptt',
 'djangocms_inherit',
 'djangocms_picture',
 'djangocms_file',
 'djangocms_link',
 'djangocms_text_ckeditor',
 'blog',
 'settings',
 'reversion',
 'reversion_compare')
Installed Middleware:
('django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.locale.LocaleMiddleware',
 'django.middleware.common.CommonMiddleware',
 'cms.middleware.user.CurrentUserMiddleware',
 'cms.middleware.page.CurrentPageMiddleware',
 'cms.middleware.toolbar.ToolbarMiddleware',
 'cms.middleware.language.LanguageCookieMiddleware')

Traceback:
File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/django/core/handlers/base.py" in get_response
  132.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/django/utils/decorators.py" in _wrapped_view
  110.                     response = view_func(request, *args, **kwargs)
File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
  57.         response = view_func(request, *args, **kwargs)
File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/django/contrib/admin/sites.py" in inner
  233.             return view(request, *args, **kwargs)
File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/reversion_compare/admin.py" in compare_view
  721.         compare_data, has_unfollowed_fields = self.compare(obj, version1, version2)
File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/reversion_compare/admin.py" in compare
  676.             if not obj_compare.changed():
File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/reversion_compare/admin.py" in changed
  268.             info = self.get_m2m_change_info()
File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/reversion_compare/admin.py" in get_m2m_change_info
  324.         m2m_data1, m2m_data2 = self.get_many_to_many()
File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/reversion_compare/admin.py" in get_many_to_many
  301.         m2m_data1, m2m_data2 = self._get_both_results("get_many_to_many")
File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/reversion_compare/admin.py" in _get_both_results
  289.         result1 = self._get_result(self.compare_obj1, func_name)
File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/reversion_compare/admin.py" in _get_result
  285.         result = func()
File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/reversion_compare/admin.py" in get_many_to_many
  136.             ids = [int(v) for v in self.value]  # is: version.field_dict[field.name]
File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/reversion_compare/admin.py" in <listcomp>
  136.             ids = [int(v) for v in self.value]  # is: version.field_dict[field.name]

Exception Type: ValueError at /uk/admin/blog/post/4/history/compare/
Exception Value: invalid literal for int() with base 10: 'F'
jedie commented 9 years ago

Interesting. This should not happen, because of:

        if self.has_int_pk:
            ids = [int(v) for v in self.value]  # is: version.field_dict[field.name]

https://github.com/jedie/django-reversion-compare/blob/fedc44dffbdb00a426f860f71f5eb0e9297efd8b/reversion_compare/admin.py#L135-L136

So you have a model that doesn't use a number ans primary key... But why is "has_int_pk" true?!? It comes from: reversion.models.has_int_pk -> https://github.com/etianen/django-reversion/blob/55e3709d730d06ae933ca557d0d7d704f2b05820/src/reversion/models.py#L107-L117

Please post your model.

linevych commented 9 years ago

Model:

class Post(models.Model):
    """
    Default post model
    """
    title = models.CharField(
        verbose_name=_(u"Title"),
        max_length=100,
        blank=True,
        null=True,
        default=_(u"Untitled"),
    )
    content = PlaceholderField(
        slotname='post_content',
        verbose_name=_(u"Text"),
        blank=True,
        null=True,
    )
    preview_text = models.TextField(
        verbose_name=_(u"Preview Text"),
        blank=True,
        null=True,
    )
    date = models.DateTimeField(
        verbose_name=_(u"Date"),
        auto_now_add=True,
    )
    modified = models.DateTimeField(
        verbose_name=_(u"Updated"),
        auto_now=True,
    )
    is_draft = models.BooleanField(
        verbose_name=_(u"Draft"),
        default=True,
    )
    enable_comments = models.BooleanField(
        verbose_name=_(u"Allow comments"),
        default=True,
    )
    category = models.ManyToManyField(
        "Category",
        verbose_name=_(u"Category"),
        blank=True,
    )
    tag = TaggableManager(
        verbose_name=_(u"Tags"),
        blank=True,
    )
    export_to_twitter = models.BooleanField(
        verbose_name=_(u"Export to Twitter"),
        default=True,
    )
    export_google_plus = models.BooleanField(
        verbose_name=_(u"Export to Google+"),
        default=True,
    )
    slug = models.SlugField(
        verbose_name=_(u"Slug"),
        max_length=100,
        blank=True,
        null=True,
    )

    image = FilerImageField(
        verbose_name=_(u"Preview image"),
        blank=True,
        null=True,
    )
    objects = models.Manager()
    published = PublishedManager()

    def __str__(self):
        return str(self.title)

    class Meta:
        verbose_name = _(u"Post")
        verbose_name_plural = _(u"Posts")

Whole project lives here: https://bitbucket.org/linevich/blog.linevich.net

jedie commented 9 years ago

Hm. That's look like a normal model. Nothing about a special primary key...

Can you run with django-extensions "runserver_plus": http://django-extensions.readthedocs.org/en/latest/runserver_plus.html

And figure out more information from traceback. e.g.: These values:

I looked into your source: https://bitbucket.org/linevich/blog.linevich.net/src/f08d681b0840016494065a05222e25b120c12aa9/project/blog/models.py?at=default

Category is a MPTTModel... Maybe this is the problematic part?!?

linevych commented 9 years ago

I removed category field - no result. Traceback from the runserver_plus extension, but I can't find there any necessary values.

Traceback (most recent call last):
  File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/django/contrib/staticfiles/handlers.py", line 63, in __call__
    return self.application(environ, start_response)
  File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/django/core/handlers/wsgi.py", line 189, in __call__
    response = self.get_response(request)
  File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/django/core/handlers/base.py", line 218, in get_response
    response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
  File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/django/core/handlers/base.py", line 261, in handle_uncaught_exception
    return debug.technical_500_response(request, *exc_info)
  File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/django_extensions/management/technical_response.py", line 5, in null_technical_500_response
    six.reraise(exc_type, exc_value, tb)
  File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/six.py", line 659, in reraise
    raise value
  File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/django/core/handlers/base.py", line 132, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/django/utils/decorators.py", line 110, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/django/views/decorators/cache.py", line 57, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/django/contrib/admin/sites.py", line 233, in inner
    return view(request, *args, **kwargs)
  File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/reversion_compare/admin.py", line 721, in compare_view
    compare_data, has_unfollowed_fields = self.compare(obj, version1, version2)
  File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/reversion_compare/admin.py", line 676, in compare
    if not obj_compare.changed():
  File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/reversion_compare/admin.py", line 268, in changed
    info = self.get_m2m_change_info()
  File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/reversion_compare/admin.py", line 324, in get_m2m_change_info
    m2m_data1, m2m_data2 = self.get_many_to_many()
  File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/reversion_compare/admin.py", line 301, in get_many_to_many
    m2m_data1, m2m_data2 = self._get_both_results("get_many_to_many")
  File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/reversion_compare/admin.py", line 289, in _get_both_results
    result1 = self._get_result(self.compare_obj1, func_name)
  File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/reversion_compare/admin.py", line 285, in _get_result
    result = func()
  File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/reversion_compare/admin.py", line 136, in get_many_to_many
    ids = [int(v) for v in self.value]  # is: version.field_dict[field.name]
  File "/home/blog_linevich_net/.env/lib/python3.4/site-packages/reversion_compare/admin.py", line 136, in <listcomp>
    ids = [int(v) for v in self.value]  # is: version.field_dict[field.name]
ValueError: invalid literal for int() with base 10: 'F'
jedie commented 9 years ago

You must use the interactive part,to get the information

see: http://django-extensions.readthedocs.org/en/latest/runserver_plus.html#interactive-debugging-console

jennahowe commented 9 years ago

I had this error too, and it seems to be something to do with django-taggit-- for me anyway.

>>> self.field.get_internal_type() 'ManyToManyField' >>> self.field.rel.to <class 'taggit.models.Tag'>

There were some other issues with taggit: https://github.com/jedie/django-reversion-compare/issues/10, not sure if this is related or not, doesn't really look like it is.

prokaktus commented 8 years ago

Hello @jedie! I've stucked with this issue too. I've tried to debug code and this is problem that I see (reproduced with model with ManyToMany through field):

class CompareObject(object):
    def __init__(self, field, field_name, obj, version, has_int_pk, adapter):
        ...
        # try and get a value, if none punt
        self.value = version.field_dict.get(field_name, "Field Didn't exist!")

If field is ManyToMany-through, then field_dict for this key will be empty. But this field contains in array fields

    def compare(self, obj, version1, version2):
        ...
        # Create a list of all normal fields and append many-to-many fields
        fields = [field for field in obj._meta.fields]
        concrete_model = obj._meta.concrete_model
        fields += concrete_model._meta.many_to_many    #<---- place, where we put m2m-through-field

And my question is: should m2m be contained in field_dict? And if the answer is yes, where should I look to fix this problem?

Thanks for your appreciation!

jedie commented 8 years ago

Thanks for this information! Unfortunately I have just a little time. Am glad about every contribution.

prokaktus commented 8 years ago

@jedie it's ok, I'm very glad to contribute. But I need a little more info about expected behavior. m2m-through field should be in version.field_dict? Because IFAIU, this dictionary filled in django-reversion.

jedie commented 8 years ago

So spontaneously I do not know the answer ;)

prokaktus commented 8 years ago

Ok. Soo-o-o, anyway, thanks for help)

erwinfeser commented 8 years ago

I am having the same problem

opbod commented 8 years ago

Same problem here. invalid literal for int() with base 10: 'F'.

vdboor commented 8 years ago

One part of the solution might be using a placeholder object instead of

self.value = version.field_dict.get(field_name, _("Field Didn't exist!"))

which gets parsed in a loop at:

ids = [int(v) for v in self.value]