renaturalize.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. from django.apps import apps
  2. from django.core.management.base import BaseCommand, CommandError
  3. from utilities.fields import NaturalOrderingField
  4. class Command(BaseCommand):
  5. help = "Recalculate natural ordering values for the specified models"
  6. def add_arguments(self, parser):
  7. parser.add_argument(
  8. 'args', metavar='app_label.ModelName', nargs='*',
  9. help='One or more specific models (each prefixed with its app_label) to renaturalize',
  10. )
  11. def _get_models(self, names):
  12. """
  13. Compile a list of models to be renaturalized. If no names are specified, all models which have one or more
  14. NaturalOrderingFields will be included.
  15. """
  16. models = []
  17. if names:
  18. # Collect all NaturalOrderingFields present on the specified models
  19. for name in names:
  20. try:
  21. app_label, model_name = name.split('.')
  22. except ValueError:
  23. raise CommandError(
  24. "Invalid format: {}. Models must be specified in the form app_label.ModelName.".format(name)
  25. )
  26. try:
  27. app_config = apps.get_app_config(app_label)
  28. except LookupError as e:
  29. raise CommandError(str(e))
  30. try:
  31. model = app_config.get_model(model_name)
  32. except LookupError:
  33. raise CommandError("Unknown model: {}.{}".format(app_label, model_name))
  34. fields = [
  35. field for field in model._meta.concrete_fields if type(field) is NaturalOrderingField
  36. ]
  37. if not fields:
  38. raise CommandError(
  39. "Invalid model: {}.{} does not employ natural ordering".format(app_label, model_name)
  40. )
  41. models.append(
  42. (model, fields)
  43. )
  44. else:
  45. # Find *all* models with NaturalOrderingFields
  46. for app_config in apps.get_app_configs():
  47. for model in app_config.models.values():
  48. fields = [
  49. field for field in model._meta.concrete_fields if type(field) is NaturalOrderingField
  50. ]
  51. if fields:
  52. models.append(
  53. (model, fields)
  54. )
  55. return models
  56. def handle(self, *args, **options):
  57. models = self._get_models(args)
  58. if options['verbosity']:
  59. self.stdout.write("Renaturalizing {} models.".format(len(models)))
  60. for model, fields in models:
  61. for field in fields:
  62. target_field = field.target_field
  63. naturalize = field.naturalize_function
  64. count = 0
  65. # Print the model and field name
  66. if options['verbosity']:
  67. self.stdout.write(
  68. "{}.{} ({})... ".format(model._meta.label, field.target_field, field.name),
  69. ending='\n' if options['verbosity'] >= 2 else ''
  70. )
  71. self.stdout.flush()
  72. # Find all unique values for the field
  73. queryset = model.objects.values_list(target_field, flat=True).order_by(target_field).distinct()
  74. for value in queryset:
  75. naturalized_value = naturalize(value, max_length=field.max_length)
  76. if options['verbosity'] >= 2:
  77. self.stdout.write(" {} -> {}".format(value, naturalized_value), ending='')
  78. self.stdout.flush()
  79. # Update each unique field value in bulk
  80. changed = model.objects.filter(name=value).update(**{field.name: naturalized_value})
  81. if options['verbosity'] >= 2:
  82. self.stdout.write(" ({})".format(changed))
  83. count += changed
  84. # Print the total count of alterations for the field
  85. if options['verbosity'] >= 2:
  86. self.stdout.write(self.style.SUCCESS("{} {} updated ({} unique values)".format(
  87. count, model._meta.verbose_name_plural, queryset.count()
  88. )))
  89. elif options['verbosity']:
  90. self.stdout.write(self.style.SUCCESS(str(count)))
  91. if options['verbosity']:
  92. self.stdout.write(self.style.SUCCESS("Done."))