util.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. from __future__ import unicode_literals
  2. import datetime
  3. import decimal
  4. import hashlib
  5. import logging
  6. from time import time
  7. from django.conf import settings
  8. from django.utils.encoding import force_bytes
  9. from django.utils.timezone import utc
  10. logger = logging.getLogger('django.db.backends')
  11. class CursorWrapper(object):
  12. def __init__(self, cursor, db):
  13. self.cursor = cursor
  14. self.db = db
  15. def __getattr__(self, attr):
  16. if attr in ('execute', 'executemany', 'callproc'):
  17. self.db.set_dirty()
  18. cursor_attr = getattr(self.cursor, attr)
  19. if attr in ('callproc', 'close', 'execute', 'executemany',
  20. 'fetchone', 'fetchmany', 'fetchall', 'nextset'):
  21. return self.db.wrap_database_errors()(cursor_attr)
  22. else:
  23. return cursor_attr
  24. def __iter__(self):
  25. return iter(self.cursor)
  26. class CursorDebugWrapper(CursorWrapper):
  27. def execute(self, sql, params=None):
  28. self.db.set_dirty()
  29. start = time()
  30. try:
  31. with self.db.wrap_database_errors():
  32. if params is None:
  33. # params default might be backend specific
  34. return self.cursor.execute(sql)
  35. return self.cursor.execute(sql, params)
  36. finally:
  37. stop = time()
  38. duration = stop - start
  39. sql = self.db.ops.last_executed_query(self.cursor, sql, params)
  40. self.db.queries.append({
  41. 'sql': sql,
  42. 'time': "%.3f" % duration,
  43. })
  44. logger.debug('(%.3f) %s; args=%s' % (duration, sql, params),
  45. extra={'duration': duration, 'sql': sql, 'params': params}
  46. )
  47. def executemany(self, sql, param_list):
  48. self.db.set_dirty()
  49. start = time()
  50. try:
  51. with self.db.wrap_database_errors():
  52. return self.cursor.executemany(sql, param_list)
  53. finally:
  54. stop = time()
  55. duration = stop - start
  56. try:
  57. times = len(param_list)
  58. except TypeError: # param_list could be an iterator
  59. times = '?'
  60. self.db.queries.append({
  61. 'sql': '%s times: %s' % (times, sql),
  62. 'time': "%.3f" % duration,
  63. })
  64. logger.debug('(%.3f) %s; args=%s' % (duration, sql, param_list),
  65. extra={'duration': duration, 'sql': sql, 'params': param_list}
  66. )
  67. ###############################################
  68. # Converters from database (string) to Python #
  69. ###############################################
  70. def typecast_date(s):
  71. return s and datetime.date(*map(int, s.split('-'))) or None # returns None if s is null
  72. def typecast_time(s): # does NOT store time zone information
  73. if not s: return None
  74. hour, minutes, seconds = s.split(':')
  75. if '.' in seconds: # check whether seconds have a fractional part
  76. seconds, microseconds = seconds.split('.')
  77. else:
  78. microseconds = '0'
  79. return datetime.time(int(hour), int(minutes), int(seconds), int(float('.'+microseconds) * 1000000))
  80. def typecast_timestamp(s): # does NOT store time zone information
  81. # "2005-07-29 15:48:00.590358-05"
  82. # "2005-07-29 09:56:00-05"
  83. if not s: return None
  84. if not ' ' in s: return typecast_date(s)
  85. d, t = s.split()
  86. # Extract timezone information, if it exists. Currently we just throw
  87. # it away, but in the future we may make use of it.
  88. if '-' in t:
  89. t, tz = t.split('-', 1)
  90. tz = '-' + tz
  91. elif '+' in t:
  92. t, tz = t.split('+', 1)
  93. tz = '+' + tz
  94. else:
  95. tz = ''
  96. dates = d.split('-')
  97. times = t.split(':')
  98. seconds = times[2]
  99. if '.' in seconds: # check whether seconds have a fractional part
  100. seconds, microseconds = seconds.split('.')
  101. else:
  102. microseconds = '0'
  103. tzinfo = utc if settings.USE_TZ else None
  104. return datetime.datetime(int(dates[0]), int(dates[1]), int(dates[2]),
  105. int(times[0]), int(times[1]), int(seconds),
  106. int((microseconds + '000000')[:6]), tzinfo)
  107. def typecast_decimal(s):
  108. if s is None or s == '':
  109. return None
  110. return decimal.Decimal(s)
  111. ###############################################
  112. # Converters from Python to database (string) #
  113. ###############################################
  114. def rev_typecast_decimal(d):
  115. if d is None:
  116. return None
  117. return str(d)
  118. def truncate_name(name, length=None, hash_len=4):
  119. """Shortens a string to a repeatable mangled version with the given length.
  120. """
  121. if length is None or len(name) <= length:
  122. return name
  123. hsh = hashlib.md5(force_bytes(name)).hexdigest()[:hash_len]
  124. return '%s%s' % (name[:length-hash_len], hsh)
  125. def format_number(value, max_digits, decimal_places):
  126. """
  127. Formats a number into a string with the requisite number of digits and
  128. decimal places.
  129. """
  130. if isinstance(value, decimal.Decimal):
  131. context = decimal.getcontext().copy()
  132. context.prec = max_digits
  133. return '%s' % str(value.quantize(decimal.Decimal(".1") ** decimal_places, context=context))
  134. else:
  135. return "%.*f" % (decimal_places, value)