fields.py 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. # coding: utf-8
  2. from __future__ import print_function
  3. from __future__ import absolute_import
  4. from __future__ import division
  5. from __future__ import unicode_literals
  6. from django.db import models
  7. import re
  8. class FingerprintField(models.CharField):
  9. description = "CharField that holds hex digits, without spaces, in uppercase"
  10. re_spaces = re.compile(r"\s+")
  11. re_invalid = re.compile(r"[^0-9A-Fa-f]+")
  12. @classmethod
  13. def clean_fingerprint(cls, value):
  14. # Refuse all non-strings
  15. if not isinstance(value, basestring): return None
  16. # Remove spaces
  17. value = cls.re_spaces.sub("", value)
  18. # Convert empty strings to None
  19. if not value: return None
  20. # Refuse strings with non-hex characters
  21. if cls.re_invalid.search(value): return None
  22. # Refuse hex strings whose length does not match a fingerprint
  23. if len(value) != 32 and len(value) != 40: return None
  24. # Uppercase the result
  25. return value.upper()
  26. ## not really useful, because in Person this is blank=True which not cleaned / validated
  27. def to_python(self, value):
  28. """
  29. Converts a value from the database to a python object
  30. """
  31. value = super(FingerprintField, self).to_python(value)
  32. return self.clean_fingerprint(value)
  33. def get_prep_value(self, value):
  34. """
  35. Converts a value from python to the DB
  36. """
  37. value = super(FingerprintField, self).get_prep_value(value)
  38. return self.clean_fingerprint(value)
  39. def formfield(self, **kwargs):
  40. # bypass our parent to fix "maxlength" attribute in widget: fingerprint
  41. # with spaces are 50 chars to allow easy copy-and-paste
  42. if 'max_length' not in kwargs:
  43. kwargs.update({'max_length': 50})
  44. return super(FingerprintField, self).formfield(**kwargs)
  45. # Implementation notes
  46. #
  47. # * Multiple NULL values in UNIQUE fields
  48. # They are supported in sqlite, postgresql and mysql, and that is good
  49. # enough.
  50. # See http://www.sqlite.org/nulls.html
  51. # See http://stackoverflow.com/questions/454436/unique-fields-that-allow-nulls-in-django
  52. # for possible Django gotchas
  53. # * Denormalised fields
  54. # Some limited redundancy is tolerated for convenience, but it is
  55. # checked/enforced/recomputed during daily maintenance procedures
  56. #
  57. #
  58. # See http://stackoverflow.com/questions/454436/unique-fields-that-allow-nulls-in-django
  59. #
  60. # This is used for uid fields, that need to be enforced to be unique EXCEPT
  61. # when they are empty
  62. #
  63. class CharNullField(models.CharField):
  64. description = "CharField that stores NULL but returns ''"
  65. # this is the value right out of the db, or an instance
  66. def to_python(self, value):
  67. if isinstance(value, models.CharField): # if an instance, just return the instance
  68. return value
  69. if value is None:
  70. # if the db has a NULL, convert it into the Django-friendly '' string
  71. return ""
  72. else:
  73. # otherwise, return just the value
  74. return value
  75. # catches value right before sending to db
  76. def get_db_prep_value(self, value, connection, prepared=False):
  77. if value=="":
  78. # if Django tries to save '' string, send the db None (NULL)
  79. return None
  80. else:
  81. # otherwise, just pass the value
  82. return value