diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index c7116cc5a7..f3710c1452 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -120,10 +120,9 @@ class Cache(object): mi.ondevice_col = self._field_for('ondevice', book_id, default_value='') mi.last_modified = self._field_for('last_modified', book_id, default_value=n) - formats = self._field_for('formats', book_id, default_value=()) + formats = self._field_for('formats', book_id) mi.format_metadata = {} - mi.languages = list(self._field_for('languages', book_id, - default_value=())) + mi.languages = list(self._field_for('languages', book_id)) if not formats: good_formats = None else: @@ -208,16 +207,13 @@ class Cache(object): ``book_id``. If no such book exists or it has no defined value for the field ``name`` or no such field exists, then ``default_value`` is returned. - default_values is not used for title, title_sort, authors, author_sort + default_value is not used for title, title_sort, authors, author_sort and series_index. This is because these always have values in the db. default_value is used for all custom columns. - The returned value for is_multiple fields are always tuples, unless - default_value is returned. - - WARNING: When returning the value for a is_multiple custom field this - method returns None (the default_value) if no value is set. The - get_custom() method from the old interface returned [] + The returned value for is_multiple fields are always tuples, even when + no values are found (in other words, default_value is ignored). The + exception is identifiers for which the returned value is always a dict. WARNING: For is_multiple fields this method returns tuples, the old interface generally returned lists. @@ -230,7 +226,13 @@ class Cache(object): return self.composite_for(name, book_id, default_value=default_value) try: - return self.fields[name].for_book(book_id, default_value=default_value) + field = self.fields[name] + except KeyError: + return default_value + if field.is_multiple: + default_value = {} if name == 'identifiers' else () + try: + return field.for_book(book_id, default_value=default_value) except (KeyError, IndexError): return default_value diff --git a/src/calibre/db/fields.py b/src/calibre/db/fields.py index d425f0d635..e154900031 100644 --- a/src/calibre/db/fields.py +++ b/src/calibre/db/fields.py @@ -33,6 +33,8 @@ class Field(object): self._default_sort_key = UNDEFINED_DATE if self.name == 'languages': self._sort_key = lambda x:sort_key(calibre_langcode_to_name(x)) + self.is_multiple = (bool(self.metadata['is_multiple']) or self.name == + 'formats') @property def metadata(self): @@ -141,6 +143,7 @@ class OnDeviceField(OneToOneField): def __init__(self, name, table): self.name = name self.book_on_device_func = None + self.is_multiple = False def book_on_device(self, book_id): if callable(self.book_on_device_func): diff --git a/src/calibre/db/tests/reading.py b/src/calibre/db/tests/reading.py index 269bc458c2..d1ff81440c 100644 --- a/src/calibre/db/tests/reading.py +++ b/src/calibre/db/tests/reading.py @@ -34,9 +34,9 @@ class ReadingTest(BaseTest): 'series' : None, 'series_index': 1.0, 'rating': None, - 'tags': None, - 'formats':None, - 'identifiers': None, + 'tags': (), + 'formats':(), + 'identifiers': {}, 'timestamp': datetime.datetime(2011, 9, 7, 13, 54, 41, tzinfo=local_tz), 'pubdate': datetime.datetime(2011, 9, 7, 13, 54, 41, @@ -44,15 +44,15 @@ class ReadingTest(BaseTest): 'last_modified': datetime.datetime(2011, 9, 7, 13, 54, 41, tzinfo=local_tz), 'publisher': None, - 'languages': None, + 'languages': (), 'comments': None, '#enum': None, - '#authors':None, + '#authors':(), '#date':None, '#rating':None, '#series':None, '#series_index': None, - '#tags':None, + '#tags':(), '#yesno':None, '#comments': None, @@ -66,7 +66,7 @@ class ReadingTest(BaseTest): 'series' : 'Series One', 'series_index': 1.0, 'tags':('Tag Two', 'Tag One'), - 'formats': None, + 'formats': (), 'rating': 4.0, 'identifiers': {'test':'one'}, 'timestamp': datetime.datetime(2011, 9, 5, 15, 6, @@ -96,7 +96,7 @@ class ReadingTest(BaseTest): 'series_index': 2.0, 'rating': 6.0, 'tags': ('Tag One',), - 'formats':None, + 'formats':(), 'identifiers': {'test':'two'}, 'timestamp': datetime.datetime(2011, 9, 6, 0, 0, tzinfo=local_tz), @@ -120,8 +120,10 @@ class ReadingTest(BaseTest): } for book_id, test in tests.iteritems(): for field, expected_val in test.iteritems(): - self.assertEqual(expected_val, - cache.field_for(field, book_id)) + val = cache.field_for(field, book_id) + self.assertEqual(expected_val, val, + 'Book id: %d Field: %s failed: %r != %r'%( + book_id, field, expected_val, val)) # }}} def test_sorting(self): # {{{