PageRenderTime 49ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/compat/haystack/fields.py

https://bitbucket.org/resplin/byteflow
Python | 203 lines | 123 code | 51 blank | 29 comment | 31 complexity | 60741e042a9ff660b63661bf49456185 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. import re
  2. from django.utils import datetime_safe
  3. from django.template import loader, Context
  4. from haystack.exceptions import SearchFieldError
  5. class NOT_PROVIDED:
  6. pass
  7. DATETIME_REGEX = re.compile('^(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})(T|\s+)(?P<hour>\d{2}):(?P<minute>\d{2}):(?P<second>\d{2}).*?$')
  8. # All the SearchFields variants.
  9. class SearchField(object):
  10. """The base implementation of a search field."""
  11. def __init__(self, model_attr=None, use_template=False, template_name=None,
  12. document=False, indexed=True, stored=True, default=NOT_PROVIDED,
  13. null=False):
  14. # Track what the index thinks this field is called.
  15. self.instance_name = None
  16. self.model_attr = model_attr
  17. self.use_template = use_template
  18. self.template_name = template_name
  19. self.document = document
  20. self.indexed = indexed
  21. self.stored = stored
  22. self._default = default
  23. self.null = null
  24. def has_default(self):
  25. """Returns a boolean of whether this field has a default value."""
  26. return self._default is not NOT_PROVIDED
  27. @property
  28. def default(self):
  29. """Returns the default value for the field."""
  30. if callable(self._default):
  31. return self._default()
  32. return self._default
  33. def prepare(self, obj):
  34. """
  35. Takes data from the provided object and prepares it for storage in the
  36. index.
  37. """
  38. # Give priority to a template.
  39. if self.use_template:
  40. return self.prepare_template(obj)
  41. elif self.model_attr is not None:
  42. # Check for `__` in the field for looking through the relation.
  43. attrs = self.model_attr.split('__')
  44. current_object = obj
  45. for attr in attrs:
  46. if not hasattr(current_object, attr):
  47. raise SearchFieldError("The model '%s' does not have a model_attr '%s'." % (repr(current_object), attr))
  48. current_object = getattr(current_object, attr, None)
  49. if current_object is None:
  50. if self.has_default():
  51. current_object = self._default
  52. # Fall out of the loop, given any further attempts at
  53. # accesses will fail misreably.
  54. break
  55. elif self.null:
  56. current_object = None
  57. # Fall out of the loop, given any further attempts at
  58. # accesses will fail misreably.
  59. break
  60. else:
  61. raise SearchFieldError("The model '%s' has an empty model_attr '%s' and doesn't allow a default or null value." % (repr(current_object), attr))
  62. if callable(current_object):
  63. return current_object()
  64. return current_object
  65. if self.has_default():
  66. return self.default
  67. else:
  68. return None
  69. def prepare_template(self, obj):
  70. """
  71. Flattens an object for indexing.
  72. This loads a template
  73. (``search/indexes/{app_label}/{model_name}_{field_name}.txt``) and
  74. returns the result of rendering that template. ``object`` will be in
  75. its context.
  76. """
  77. if self.instance_name is None and self.template_name is None:
  78. raise SearchFieldError("This field requires either its instance_name variable to be populated or an explicit template_name in order to load the correct template.")
  79. if self.template_name is not None:
  80. template_name = self.template_name
  81. else:
  82. template_name = 'search/indexes/%s/%s_%s.txt' % (obj._meta.app_label, obj._meta.module_name, self.instance_name)
  83. t = loader.get_template(template_name)
  84. return t.render(Context({'object': obj}))
  85. def convert(self, value):
  86. """
  87. Handles conversion between the data found and the type of the field.
  88. Extending classes should override this method and provide correct
  89. data coercion.
  90. """
  91. return value
  92. class CharField(SearchField):
  93. def prepare(self, obj):
  94. return self.convert(super(CharField, self).prepare(obj))
  95. def convert(self, value):
  96. if value is None:
  97. return None
  98. return unicode(value)
  99. class IntegerField(SearchField):
  100. def prepare(self, obj):
  101. return self.convert(super(IntegerField, self).prepare(obj))
  102. def convert(self, value):
  103. if value is None:
  104. return None
  105. return int(value)
  106. class FloatField(SearchField):
  107. def prepare(self, obj):
  108. return self.convert(super(FloatField, self).prepare(obj))
  109. def convert(self, value):
  110. if value is None:
  111. return None
  112. return float(value)
  113. class BooleanField(SearchField):
  114. def prepare(self, obj):
  115. return self.convert(super(BooleanField, self).prepare(obj))
  116. def convert(self, value):
  117. if value is None:
  118. return None
  119. return bool(value)
  120. class DateField(SearchField):
  121. def convert(self, value):
  122. if value is None:
  123. return None
  124. if isinstance(value, basestring):
  125. match = DATETIME_REGEX.search(value)
  126. if match:
  127. data = match.groupdict()
  128. return datetime_safe.date(int(data['year']), int(data['month']), int(data['day']))
  129. else:
  130. raise SearchFieldError("Date provided to '%s' field doesn't appear to be a valid date string: '%s'" % (self.instance_name, value))
  131. return value
  132. class DateTimeField(SearchField):
  133. def convert(self, value):
  134. if value is None:
  135. return None
  136. if isinstance(value, basestring):
  137. match = DATETIME_REGEX.search(value)
  138. if match:
  139. data = match.groupdict()
  140. return datetime_safe.datetime(int(data['year']), int(data['month']), int(data['day']), int(data['hour']), int(data['minute']), int(data['second']))
  141. else:
  142. raise SearchFieldError("Datetime provided to '%s' field doesn't appear to be a valid datetime string: '%s'" % (self.instance_name, value))
  143. return value
  144. class MultiValueField(SearchField):
  145. def prepare(self, obj):
  146. return self.convert(super(MultiValueField, self).prepare(obj))
  147. def convert(self, value):
  148. if value is None:
  149. return None
  150. return list(value)