Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

hstore.py 3.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import json
  2. from django.contrib.postgres import forms, lookups
  3. from django.contrib.postgres.fields.array import ArrayField
  4. from django.core import exceptions
  5. from django.db.models import Field, TextField, Transform
  6. from django.utils.translation import gettext_lazy as _
  7. from .mixins import CheckFieldDefaultMixin
  8. __all__ = ['HStoreField']
  9. class HStoreField(CheckFieldDefaultMixin, Field):
  10. empty_strings_allowed = False
  11. description = _('Map of strings to strings/nulls')
  12. default_error_messages = {
  13. 'not_a_string': _('The value of "%(key)s" is not a string or null.'),
  14. }
  15. _default_hint = ('dict', '{}')
  16. def db_type(self, connection):
  17. return 'hstore'
  18. def get_transform(self, name):
  19. transform = super().get_transform(name)
  20. if transform:
  21. return transform
  22. return KeyTransformFactory(name)
  23. def validate(self, value, model_instance):
  24. super().validate(value, model_instance)
  25. for key, val in value.items():
  26. if not isinstance(val, str) and val is not None:
  27. raise exceptions.ValidationError(
  28. self.error_messages['not_a_string'],
  29. code='not_a_string',
  30. params={'key': key},
  31. )
  32. def to_python(self, value):
  33. if isinstance(value, str):
  34. value = json.loads(value)
  35. return value
  36. def value_to_string(self, obj):
  37. return json.dumps(self.value_from_object(obj))
  38. def formfield(self, **kwargs):
  39. return super().formfield(**{
  40. 'form_class': forms.HStoreField,
  41. **kwargs,
  42. })
  43. def get_prep_value(self, value):
  44. value = super().get_prep_value(value)
  45. if isinstance(value, dict):
  46. prep_value = {}
  47. for key, val in value.items():
  48. key = str(key)
  49. if val is not None:
  50. val = str(val)
  51. prep_value[key] = val
  52. value = prep_value
  53. if isinstance(value, list):
  54. value = [str(item) for item in value]
  55. return value
  56. HStoreField.register_lookup(lookups.DataContains)
  57. HStoreField.register_lookup(lookups.ContainedBy)
  58. HStoreField.register_lookup(lookups.HasKey)
  59. HStoreField.register_lookup(lookups.HasKeys)
  60. HStoreField.register_lookup(lookups.HasAnyKeys)
  61. class KeyTransform(Transform):
  62. output_field = TextField()
  63. def __init__(self, key_name, *args, **kwargs):
  64. super().__init__(*args, **kwargs)
  65. self.key_name = key_name
  66. def as_sql(self, compiler, connection):
  67. lhs, params = compiler.compile(self.lhs)
  68. return "(%s -> '%s')" % (lhs, self.key_name), params
  69. class KeyTransformFactory:
  70. def __init__(self, key_name):
  71. self.key_name = key_name
  72. def __call__(self, *args, **kwargs):
  73. return KeyTransform(self.key_name, *args, **kwargs)
  74. @HStoreField.register_lookup
  75. class KeysTransform(Transform):
  76. lookup_name = 'keys'
  77. function = 'akeys'
  78. output_field = ArrayField(TextField())
  79. @HStoreField.register_lookup
  80. class ValuesTransform(Transform):
  81. lookup_name = 'values'
  82. function = 'avals'
  83. output_field = ArrayField(TextField())