dumpdata.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import warnings
  2. from django.apps import apps
  3. from django.core import serializers
  4. from django.core.management.base import BaseCommand, CommandError
  5. from django.core.management.utils import parse_apps_and_model_labels
  6. from django.db import DEFAULT_DB_ALIAS, router
  7. class ProxyModelWarning(Warning):
  8. pass
  9. class Command(BaseCommand):
  10. help = (
  11. "Output the contents of the database as a fixture of the given format "
  12. "(using each model's default manager unless --all is specified)."
  13. )
  14. def add_arguments(self, parser):
  15. parser.add_argument(
  16. 'args', metavar='app_label[.ModelName]', nargs='*',
  17. help='Restricts dumped data to the specified app_label or app_label.ModelName.',
  18. )
  19. parser.add_argument(
  20. '--format', default='json',
  21. help='Specifies the output serialization format for fixtures.',
  22. )
  23. parser.add_argument(
  24. '--indent', type=int,
  25. help='Specifies the indent level to use when pretty-printing output.',
  26. )
  27. parser.add_argument(
  28. '--database',
  29. default=DEFAULT_DB_ALIAS,
  30. help='Nominates a specific database to dump fixtures from. '
  31. 'Defaults to the "default" database.',
  32. )
  33. parser.add_argument(
  34. '-e', '--exclude', action='append', default=[],
  35. help='An app_label or app_label.ModelName to exclude '
  36. '(use multiple --exclude to exclude multiple apps/models).',
  37. )
  38. parser.add_argument(
  39. '--natural-foreign', action='store_true', dest='use_natural_foreign_keys',
  40. help='Use natural foreign keys if they are available.',
  41. )
  42. parser.add_argument(
  43. '--natural-primary', action='store_true', dest='use_natural_primary_keys',
  44. help='Use natural primary keys if they are available.',
  45. )
  46. parser.add_argument(
  47. '-a', '--all', action='store_true', dest='use_base_manager',
  48. help="Use Django's base manager to dump all models stored in the database, "
  49. "including those that would otherwise be filtered or modified by a custom manager.",
  50. )
  51. parser.add_argument(
  52. '--pks', dest='primary_keys',
  53. help="Only dump objects with given primary keys. Accepts a comma-separated "
  54. "list of keys. This option only works when you specify one model.",
  55. )
  56. parser.add_argument(
  57. '-o', '--output',
  58. help='Specifies file to which the output is written.'
  59. )
  60. def handle(self, *app_labels, **options):
  61. format = options['format']
  62. indent = options['indent']
  63. using = options['database']
  64. excludes = options['exclude']
  65. output = options['output']
  66. show_traceback = options['traceback']
  67. use_natural_foreign_keys = options['use_natural_foreign_keys']
  68. use_natural_primary_keys = options['use_natural_primary_keys']
  69. use_base_manager = options['use_base_manager']
  70. pks = options['primary_keys']
  71. if pks:
  72. primary_keys = [pk.strip() for pk in pks.split(',')]
  73. else:
  74. primary_keys = []
  75. excluded_models, excluded_apps = parse_apps_and_model_labels(excludes)
  76. if not app_labels:
  77. if primary_keys:
  78. raise CommandError("You can only use --pks option with one model")
  79. app_list = dict.fromkeys(
  80. app_config for app_config in apps.get_app_configs()
  81. if app_config.models_module is not None and app_config not in excluded_apps
  82. )
  83. else:
  84. if len(app_labels) > 1 and primary_keys:
  85. raise CommandError("You can only use --pks option with one model")
  86. app_list = {}
  87. for label in app_labels:
  88. try:
  89. app_label, model_label = label.split('.')
  90. try:
  91. app_config = apps.get_app_config(app_label)
  92. except LookupError as e:
  93. raise CommandError(str(e))
  94. if app_config.models_module is None or app_config in excluded_apps:
  95. continue
  96. try:
  97. model = app_config.get_model(model_label)
  98. except LookupError:
  99. raise CommandError("Unknown model: %s.%s" % (app_label, model_label))
  100. app_list_value = app_list.setdefault(app_config, [])
  101. # We may have previously seen an "all-models" request for
  102. # this app (no model qualifier was given). In this case
  103. # there is no need adding specific models to the list.
  104. if app_list_value is not None:
  105. if model not in app_list_value:
  106. app_list_value.append(model)
  107. except ValueError:
  108. if primary_keys:
  109. raise CommandError("You can only use --pks option with one model")
  110. # This is just an app - no model qualifier
  111. app_label = label
  112. try:
  113. app_config = apps.get_app_config(app_label)
  114. except LookupError as e:
  115. raise CommandError(str(e))
  116. if app_config.models_module is None or app_config in excluded_apps:
  117. continue
  118. app_list[app_config] = None
  119. # Check that the serialization format exists; this is a shortcut to
  120. # avoid collating all the objects and _then_ failing.
  121. if format not in serializers.get_public_serializer_formats():
  122. try:
  123. serializers.get_serializer(format)
  124. except serializers.SerializerDoesNotExist:
  125. pass
  126. raise CommandError("Unknown serialization format: %s" % format)
  127. def get_objects(count_only=False):
  128. """
  129. Collate the objects to be serialized. If count_only is True, just
  130. count the number of objects to be serialized.
  131. """
  132. models = serializers.sort_dependencies(app_list.items())
  133. for model in models:
  134. if model in excluded_models:
  135. continue
  136. if model._meta.proxy and model._meta.proxy_for_model not in models:
  137. warnings.warn(
  138. "%s is a proxy model and won't be serialized." % model._meta.label,
  139. category=ProxyModelWarning,
  140. )
  141. if not model._meta.proxy and router.allow_migrate_model(using, model):
  142. if use_base_manager:
  143. objects = model._base_manager
  144. else:
  145. objects = model._default_manager
  146. queryset = objects.using(using).order_by(model._meta.pk.name)
  147. if primary_keys:
  148. queryset = queryset.filter(pk__in=primary_keys)
  149. if count_only:
  150. yield queryset.order_by().count()
  151. else:
  152. yield from queryset.iterator()
  153. try:
  154. self.stdout.ending = None
  155. progress_output = None
  156. object_count = 0
  157. # If dumpdata is outputting to stdout, there is no way to display progress
  158. if output and self.stdout.isatty() and options['verbosity'] > 0:
  159. progress_output = self.stdout
  160. object_count = sum(get_objects(count_only=True))
  161. stream = open(output, 'w') if output else None
  162. try:
  163. serializers.serialize(
  164. format, get_objects(), indent=indent,
  165. use_natural_foreign_keys=use_natural_foreign_keys,
  166. use_natural_primary_keys=use_natural_primary_keys,
  167. stream=stream or self.stdout, progress_output=progress_output,
  168. object_count=object_count,
  169. )
  170. finally:
  171. if stream:
  172. stream.close()
  173. except Exception as e:
  174. if show_traceback:
  175. raise
  176. raise CommandError("Unable to serialize database: %s" % e)