diff --git a/beets/dbcore/types.py b/beets/dbcore/types.py index f96863f9c..2cf51bc81 100644 --- a/beets/dbcore/types.py +++ b/beets/dbcore/types.py @@ -34,30 +34,42 @@ class Type(object): """The `Query` subclass to be used when querying the field. """ - null = None - """The value to be exposed when the underlying value is None. + model_type = unicode + """The python type that is used to represent the value in the model. + + The model is guaranteed to return a value of this type if the field + is accessed. To this end, the constructor is used by the `normalize` + and `from_sql` methods and the `default` property. """ + @property + def null(self): + """The value to be exposed when the underlying value is None. + """ + return self.model_type() + def format(self, value): """Given a value of this type, produce a Unicode string representing the value. This is used in template evaluation. """ # Fallback formatter. Convert to Unicode at all cost. if value is None: - return u'' - elif isinstance(value, basestring): - if isinstance(value, bytes): - return value.decode('utf8', 'ignore') - else: - return value - else: - return unicode(value) + value = self.null + if value is None: + value = u'' + if isinstance(value, bytes): + value = value.decode('utf8', 'ignore') + + return unicode(value) def parse(self, string): """Parse a (possibly human-written) string and return the indicated value of this type. """ - return string + try: + return self.model_type(string) + except ValueError: + return self.null def normalize(self, value): """Given a value that will be assigned into a field of this @@ -67,17 +79,29 @@ class Type(object): if value is None: return self.null else: + # TODO This should eventually be replaced by + # `self.model_type(value)` return value def from_sql(self, sql_value): - """Convert a value received from the database adapter to a value - stored in the model object. + """Receives the value stored in the SQL backend and return the + value to be stored in the model. + + For fixed fields the type of `value` is determined by the column + type given in the `sql` property and the SQL to Python mapping + given here: + https://docs.python.org/2/library/sqlite3.html#sqlite-and-python-types + + For flexible field the value is a unicode object. The method + must therefore be able to parse them. """ return self.normalize(sql_value) def to_sql(self, model_value): """Convert a value as stored in the model object to a value used by the database adapter. + For flexible field the value is a unicode object. The method + must therefore be able to parse them. """ return model_value @@ -85,7 +109,7 @@ class Type(object): # Reusable types. class Default(Type): - pass + null = None class Integer(Type): @@ -93,16 +117,7 @@ class Integer(Type): """ sql = u'INTEGER' query = query.NumericQuery - null = 0 - - def format(self, value): - return unicode(value or 0) - - def parse(self, string): - try: - return int(string) - except ValueError: - return 0 + model_type = int class PaddedInt(Integer): @@ -144,17 +159,11 @@ class Float(Type): """ sql = u'REAL' query = query.NumericQuery - null = 0.0 + model_type = float def format(self, value): return u'{0:.1f}'.format(value or 0.0) - def parse(self, string): - try: - return float(string) - except ValueError: - return 0.0 - class NullFloat(Float): """Same as `Float`, but does not normalize `None` to `0.0`. @@ -167,13 +176,6 @@ class String(Type): """ sql = u'TEXT' query = query.SubstringQuery - null = u'' - - def format(self, value): - return unicode(value) if value else u'' - - def parse(self, string): - return string class Boolean(Type): @@ -181,7 +183,7 @@ class Boolean(Type): """ sql = u'INTEGER' query = query.BooleanQuery - null = False + model_type = bool def format(self, value): return unicode(bool(value)) diff --git a/beets/library.py b/beets/library.py index c6c48d7cc..105da3631 100644 --- a/beets/library.py +++ b/beets/library.py @@ -87,6 +87,7 @@ class DateType(types.Float): class PathType(types.Type): sql = u'BLOB' query = PathQuery + model_type = str def format(self, value): return util.displayable_path(value)