djangae icon indicating copy to clipboard operation
djangae copied to clipboard

Fix computed field pagination

Open igniteflow opened this issue 7 years ago • 7 comments

Fixes #958

Summary of changes proposed in this Pull Request:

  • Fixes bug in ordering of integer fields in pagination
  • Adds support for paginating on negative integer fields
  • Adds tests for int and char computed fields

PR checklist:

  • [x] Updated relevant documentation
  • [x] Updated CHANGELOG.md
  • [x] Added tests for my change

igniteflow avatar Oct 25 '17 12:10 igniteflow

Quick couple of thoughts (haven't had time to review in detail):

  • elif isinstance(value, float): should just be else, because if the value is a timedelta / boolean / whatever, then we should also blow up.
    • Possibly the previous if statement should also check for long.

adamalton avatar Oct 25 '17 14:10 adamalton

If we want to be Python 3.x friendly, use isinstance(x, numbers.Real) instead of isinstance(x, (int, long, float)) or isinstance(x, numbers.Integral) instead of isinstance(x, (int, long)).

davidwtbuxton avatar Oct 25 '17 17:10 davidwtbuxton

Is Python 3 compatibility a blocker to merging this?

igniteflow avatar Oct 31 '17 13:10 igniteflow

No, there are lots of other non-python3-compatible things, so it shouldn't be a blocker.

However... when I run the tests on this branch locally, lots of the pagination tests fail with this same error:

ERROR: test_page_jump_updates_count_correctly (djangae.contrib.pagination.tests.DatastorePaginatorTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/aalton/Sites/github/djangae/testapp/djangae/contrib/pagination/tests.py", line 83, in setUp
    self.u1 = TestUser.objects.create(id=1, first_name="A", last_name="A")
  File "/Users/aalton/Sites/github/djangae/testapp/libs/django/db/models/manager.py", line 127, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/Users/aalton/Sites/github/djangae/testapp/libs/django/db/models/query.py", line 348, in create
    obj.save(force_insert=True, using=self.db)
  File "/Users/aalton/Sites/github/djangae/testapp/libs/django/db/models/base.py", line 734, in save
    force_update=force_update, update_fields=update_fields)
  File "/Users/aalton/Sites/github/djangae/testapp/libs/django/db/models/base.py", line 762, in save_base
    updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
  File "/Users/aalton/Sites/github/djangae/testapp/libs/django/db/models/base.py", line 846, in _save_table
    result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
  File "/Users/aalton/Sites/github/djangae/testapp/libs/django/db/models/base.py", line 885, in _do_insert
    using=using, raw=raw)
  File "/Users/aalton/Sites/github/djangae/testapp/libs/django/db/models/manager.py", line 127, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/Users/aalton/Sites/github/djangae/testapp/libs/django/db/models/query.py", line 920, in _insert
    return query.get_compiler(using=using).execute_sql(return_id)
  File "/Users/aalton/Sites/github/djangae/testapp/libs/django/db/models/sql/compiler.py", line 973, in execute_sql
    for sql, params in self.as_sql():
  File "/Users/aalton/Sites/github/djangae/testapp/djangae/db/backends/appengine/compiler.py", line 68, in as_sql
    self.query.raw), tuple())
  File "/Users/aalton/Sites/github/djangae/testapp/djangae/db/backends/appengine/commands.py", line 643, in __init__
    getattr(obj, field.attname) if raw else field.pre_save(obj, True),
  File "/Users/aalton/Sites/github/djangae/testapp/djangae/fields/computed.py", line 13, in pre_save
    value = self.get_computed_value(model_instance)
  File "/Users/aalton/Sites/github/djangae/testapp/djangae/fields/computed.py", line 31, in get_computed_value
    return self.computer(model_instance)
  File "/Users/aalton/Sites/github/djangae/testapp/djangae/contrib/pagination/decorators.py", line 23, in generator
    value = convert_to_paginatable_value(value, neg)
  File "/Users/aalton/Sites/github/djangae/testapp/djangae/contrib/pagination/decorators.py", line 49, in convert_to_paginatable_value
    value = u''.join([unichr(int(i)) for i in _chunks(str(value), n=5)])
ValueError: unichr() arg not in range(0x10000) (narrow Python build)

I'm guessing that Travis is running the "wide" python unicode build. But on the narrow one it dies.

adamalton avatar Oct 31 '17 18:10 adamalton

Interesting, like Travis I'm also running a wide build which is why I hadn't come across this. Will check now!

igniteflow avatar Nov 09 '17 15:11 igniteflow

When running the tests on my Mac, I'm now getting this (different test, same errorr):

ERROR: test_negatives (djangae.contrib.pagination.tests.ComputedIntegerFieldPaginationTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/adamalton/Sites/github/djangae/testapp/djangae/contrib/pagination/tests.py", line 275, in test_negatives
    obj = ComputedIntegerFieldModel.objects.create(count=i)
  File "/Users/adamalton/Sites/github/djangae/testapp/libs/django/db/models/manager.py", line 127, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/Users/adamalton/Sites/github/djangae/testapp/libs/django/db/models/query.py", line 348, in create
    obj.save(force_insert=True, using=self.db)
  File "/Users/adamalton/Sites/github/djangae/testapp/libs/django/db/models/base.py", line 734, in save
    force_update=force_update, update_fields=update_fields)
  File "/Users/adamalton/Sites/github/djangae/testapp/libs/django/db/models/base.py", line 762, in save_base
    updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
  File "/Users/adamalton/Sites/github/djangae/testapp/libs/django/db/models/base.py", line 846, in _save_table
    result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
  File "/Users/adamalton/Sites/github/djangae/testapp/libs/django/db/models/base.py", line 885, in _do_insert
    using=using, raw=raw)
  File "/Users/adamalton/Sites/github/djangae/testapp/libs/django/db/models/manager.py", line 127, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/Users/adamalton/Sites/github/djangae/testapp/libs/django/db/models/query.py", line 920, in _insert
    return query.get_compiler(using=using).execute_sql(return_id)
  File "/Users/adamalton/Sites/github/djangae/testapp/libs/django/db/models/sql/compiler.py", line 973, in execute_sql
    for sql, params in self.as_sql():
  File "/Users/adamalton/Sites/github/djangae/testapp/djangae/db/backends/appengine/compiler.py", line 68, in as_sql
    self.query.raw), tuple())
  File "/Users/adamalton/Sites/github/djangae/testapp/djangae/db/backends/appengine/commands.py", line 643, in __init__
    getattr(obj, field.attname) if raw else field.pre_save(obj, True),
  File "/Users/adamalton/Sites/github/djangae/testapp/djangae/fields/computed.py", line 13, in pre_save
    value = self.get_computed_value(model_instance)
  File "/Users/adamalton/Sites/github/djangae/testapp/djangae/fields/computed.py", line 31, in get_computed_value
    return self.computer(model_instance)
  File "/Users/adamalton/Sites/github/djangae/testapp/djangae/contrib/pagination/decorators.py", line 24, in generator
    value = convert_to_paginatable_value(value, neg)
  File "/Users/adamalton/Sites/github/djangae/testapp/djangae/contrib/pagination/decorators.py", line 55, in convert_to_paginatable_value
    value = u''.join([unichr(int(i)) for i in _chunks(str(value), n=5)])
ValueError: unichr() arg not in range(0x10000) (narrow Python build)

adamalton avatar Nov 13 '17 17:11 adamalton

I noticed that this has been added:

if value is None:
    return unicode(None)

but that will produce u"None", which is presumably not what we want?

I'm guessing that we should do:

if value is None:
    return u""

?

adamalton avatar Nov 13 '17 17:11 adamalton