def test_egg5(self): """Loading an app from an egg that has an import error in its models module raises that error""" egg_name = '%s/brokenapp.egg' % self.egg_dir sys.path.append(egg_name) with six.assertRaisesRegex(self, ImportError, 'modelz'): with app_cache._with_app('broken_app'): app_cache.get_app_config('omelet.app_no_models').models_module
def test_egg2(self): """Loading an app from an egg that has no models returns no models (and no error)""" egg_name = '%s/nomodelapp.egg' % self.egg_dir sys.path.append(egg_name) with self.settings(INSTALLED_APPS=['app_no_models']): models_module = app_cache.get_app_config('app_no_models').models_module self.assertIsNone(models_module)
def test_egg3(self): """Models module can be loaded from an app located under an egg's top-level package""" egg_name = '%s/omelet.egg' % self.egg_dir sys.path.append(egg_name) with self.settings(INSTALLED_APPS=['omelet.app_with_models']): models_module = app_cache.get_app_config('app_with_models').models_module self.assertIsNotNone(models_module)
def test_egg4(self): """Loading an app with no models from under the top-level egg package generates no error""" egg_name = '%s/omelet.egg' % self.egg_dir sys.path.append(egg_name) with self.settings(INSTALLED_APPS=['omelet.app_no_models']): models_module = app_cache.get_app_config('app_no_models').models_module self.assertIsNone(models_module)
def test_sql_all(self): app = app_cache.get_app_config("commands_sql").models_module output = sql_all(app, no_style(), connections[DEFAULT_DB_ALIAS]) self.assertEqual(self.count_ddl(output, "CREATE TABLE"), 3) # PostgreSQL creates one additional index for CharField self.assertIn(self.count_ddl(output, "CREATE INDEX"), [3, 4])
def test_egg1(self): """Models module can be loaded from an app in an egg""" egg_name = '%s/modelapp.egg' % self.egg_dir sys.path.append(egg_name) with self.settings(INSTALLED_APPS=['app_with_models']): models_module = app_cache.get_app_config('app_with_models').models_module self.assertIsNotNone(models_module)
def test_sql_delete(self): app = app_cache.get_app_config("commands_sql").models_module output = sql_delete(app, no_style(), connections[DEFAULT_DB_ALIAS]) drop_tables = [o for o in output if o.startswith("DROP TABLE")] self.assertEqual(len(drop_tables), 3) # Lower so that Oracle's upper case tbl names wont break sql = drop_tables[-1].lower() six.assertRegex(self, sql, r"^drop table .commands_sql_comment.*")
def app_index(self, request, app_label, extra_context=None): user = request.user app_name = app_cache.get_app_config(app_label).verbose_name has_module_perms = user.has_module_perms(app_label) if not has_module_perms: raise PermissionDenied app_dict = {} for model, model_admin in self._registry.items(): if app_label == model._meta.app_label: perms = model_admin.get_model_perms(request) # Check whether user has any perm for this module. # If so, add the module to the model_list. if True in perms.values(): info = (app_label, model._meta.model_name) model_dict = { 'name': capfirst(model._meta.verbose_name_plural), 'object_name': model._meta.object_name, 'perms': perms, } if perms.get('change'): try: model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=self.name) except NoReverseMatch: pass if perms.get('add'): try: model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=self.name) except NoReverseMatch: pass if app_dict: app_dict['models'].append(model_dict), else: # First time around, now that we know there's # something to display, add in the necessary meta # information. app_dict = { 'name': app_name, 'app_label': app_label, 'app_url': '', 'has_module_perms': has_module_perms, 'models': [model_dict], } if not app_dict: raise Http404('The requested admin page does not exist.') # Sort the models alphabetically within each app. app_dict['models'].sort(key=lambda x: x['name']) context = dict(self.each_context(), title=_('%(app)s administration') % {'app': app_name}, app_list=[app_dict], app_label=app_label, ) context.update(extra_context or {}) return TemplateResponse(request, self.app_index_template or [ 'admin/%s/app_index.html' % app_label, 'admin/app_index.html' ], context, current_app=self.name)
def get_comment_app(): """ Get the comment app (i.e. "django.contrib.comments") as defined in the settings """ try: app_config = app_cache.get_app_config(get_comment_app_name().rpartition(".")[2]) except LookupError: raise ImproperlyConfigured("The COMMENTS_APP (%r) " "must be in INSTALLED_APPS" % settings.COMMENTS_APP) return app_config.app_module
def test_suite_override(self): """ Validate that you can define a custom suite when running tests with ``django.test.simple.DjangoTestSuiteRunner`` (which builds up a test suite using ``build_suite``). """ from django.test.simple import build_suite app_config = app_cache.get_app_config("test_suite_override") suite = build_suite(app_config) self.assertEqual(suite.countTestCases(), 1)
def test_dynamic_load(self): """ Makes a new model at runtime and ensures it goes into the right place. """ old_models = app_cache.get_models(app_cache.get_app_config("app_cache").models_module) # Construct a new model in a new app cache body = {} new_app_cache = AppCache() meta_contents = { 'app_label': "app_cache", 'app_cache': new_app_cache, } meta = type("Meta", tuple(), meta_contents) body['Meta'] = meta body['__module__'] = TotallyNormal.__module__ temp_model = type("SouthPonies", (models.Model,), body) # Make sure it appeared in the right place! self.assertEqual( old_models, app_cache.get_models(app_cache.get_app_config("app_cache").models_module), ) self.assertEqual(new_app_cache.get_model("app_cache", "SouthPonies"), temp_model)
def test_invalid_models(self): module = app_cache.get_app_config("invalid_models").models_module get_validation_errors(self.stdout, module) self.stdout.seek(0) error_log = self.stdout.read() actual = error_log.split('\n') expected = module.model_errors.split('\n') unexpected = [err for err in actual if err not in expected] missing = [err for err in expected if err not in actual] self.assertFalse(unexpected, "Unexpected Errors: " + '\n'.join(unexpected)) self.assertFalse(missing, "Missing Errors: " + '\n'.join(missing))
def test_ticket_11936(self): # Regression for #11936 - loading.get_models should not return deferred # models by default. # Run a couple of defer queries so that app-cache must contain some # deferred classes. It might contain a lot more classes depending on # the order the tests are ran. list(Item.objects.defer("name")) list(Child.objects.defer("value")) klasses = set( map( attrgetter("__name__"), app_cache.get_models(app_cache.get_app_config("defer_regress").models_module) ) ) self.assertIn("Child", klasses) self.assertIn("Item", klasses) self.assertNotIn("Child_Deferred_value", klasses) self.assertNotIn("Item_Deferred_name", klasses) self.assertFalse(any( k._deferred for k in app_cache.get_models(app_cache.get_app_config("defer_regress").models_module))) klasses_with_deferred = set( map( attrgetter("__name__"), app_cache.get_models( app_cache.get_app_config("defer_regress").models_module, include_deferred=True ), ) ) self.assertIn("Child", klasses_with_deferred) self.assertIn("Item", klasses_with_deferred) self.assertIn("Child_Deferred_value", klasses_with_deferred) self.assertIn("Item_Deferred_name", klasses_with_deferred) self.assertTrue(any( k._deferred for k in app_cache.get_models( app_cache.get_app_config("defer_regress").models_module, include_deferred=True)) )
def path(self): migrations_package_name = MigrationLoader.migrations_module(self.migration.app_label) # See if we can import the migrations module directly try: migrations_module = import_module(migrations_package_name) basedir = os.path.dirname(migrations_module.__file__) except ImportError: app_config = app_cache.get_app_config(self.migration.app_label) migrations_package_basename = migrations_package_name.split(".")[-1] # Alright, see if it's a direct submodule of the app if "%s.%s" % (app_config.name, migrations_package_basename) == migrations_package_name: basedir = os.path.join(app_config.path, migrations_package_basename) else: raise ImportError( "Cannot open migrations module %s for app %s" % (migrations_package_name, self.migration.app_label) ) return os.path.join(basedir, self.filename)
def build_suite(self, test_labels, extra_tests=None, **kwargs): suite = unittest.TestSuite() if test_labels: for label in test_labels: if '.' in label: suite.addTest(build_test(label)) else: app_config = app_cache.get_app_config(label) suite.addTest(build_suite(app_config)) else: for app_config in app_cache.get_app_configs(): suite.addTest(build_suite(app_config)) if extra_tests: for test in extra_tests: suite.addTest(test) return runner.reorder_suite(suite, (unittest.TestCase,))
def ask_initial(self, app_label): "Should we create an initial migration for the app?" # If it was specified on the command line, definitely true if app_label in self.specified_apps: return True # Otherwise, we look to see if it has a migrations module # without any Python files in it, apart from __init__.py. # Apps from the new app template will have these; the python # file check will ensure we skip South ones. try: app_config = app_cache.get_app_config(app_label) except LookupError: # It's a fake app. return self.defaults.get("ask_initial", False) migrations_import_path = "%s.migrations" % app_config.name try: migrations_module = importlib.import_module(migrations_import_path) except ImportError: return self.defaults.get("ask_initial", False) else: filenames = os.listdir(os.path.dirname(migrations_module.__file__)) return not any(x.endswith(".py") for x in filenames if x != "__init__.py")
def handle(self, *app_labels, **options): from django.apps import app_cache if not app_labels: raise CommandError('Enter at least one appname.') # Populate models and don't use only_with_models_module=True when # calling get_app_config() to tell apart missing apps from apps # without a model module -- which can't be supported with the legacy # API since it passes the models module to handle_app(). app_cache.populate_models() try: app_configs = [app_cache.get_app_config(app_label) for app_label in app_labels] except (LookupError, ImportError) as e: raise CommandError("%s. Are you sure your INSTALLED_APPS setting is correct?" % e) output = [] for app_config in app_configs: if app_config.models_module is None: raise CommandError( "AppCommand cannot handle app %r because it doesn't have " "a models module." % app_config.label) app_output = self.handle_app(app_config.models_module, **options) if app_output: output.append(app_output) return '\n'.join(output)
def custom_sql_for_model(model, style, connection): opts = model._meta app_dirs = [] app_dir = app_cache.get_app_config(model._meta.app_label).path app_dirs.append(os.path.normpath(os.path.join(app_dir, 'sql'))) # Deprecated location -- remove in Django 1.9 old_app_dir = os.path.normpath(os.path.join(app_dir, 'models/sql')) if os.path.exists(old_app_dir): warnings.warn("Custom SQL location '<app_label>/models/sql' is " "deprecated, use '<app_label>/sql' instead.", PendingDeprecationWarning) app_dirs.append(old_app_dir) output = [] # Post-creation SQL should come before any initial SQL data is loaded. # However, this should not be done for models that are unmanaged or # for fields that are part of a parent model (via model inheritance). if opts.managed: post_sql_fields = [f for f in opts.local_fields if hasattr(f, 'post_create_sql')] for f in post_sql_fields: output.extend(f.post_create_sql(style, model._meta.db_table)) # Find custom SQL, if it's available. backend_name = connection.settings_dict['ENGINE'].split('.')[-1] sql_files = [] for app_dir in app_dirs: sql_files.append(os.path.join(app_dir, "%s.%s.sql" % (opts.model_name, backend_name))) sql_files.append(os.path.join(app_dir, "%s.sql" % opts.model_name)) for sql_file in sql_files: if os.path.exists(sql_file): with codecs.open(sql_file, 'U', encoding=settings.FILE_CHARSET) as fp: # Some backends can't execute more than one SQL statement at a time, # so split into separate statements. output.extend(_split_statements(fp.read())) return output
def handle(self, *app_labels, **options): from django.apps import app_cache format = options.get('format') indent = options.get('indent') using = options.get('database') excludes = options.get('exclude') show_traceback = options.get('traceback') use_natural_keys = options.get('use_natural_keys') if use_natural_keys: warnings.warn("``--natural`` is deprecated; use ``--natural-foreign`` instead.", PendingDeprecationWarning) use_natural_foreign_keys = options.get('use_natural_foreign_keys') or use_natural_keys use_natural_primary_keys = options.get('use_natural_primary_keys') use_base_manager = options.get('use_base_manager') pks = options.get('primary_keys') if pks: primary_keys = pks.split(',') else: primary_keys = [] excluded_apps = set() excluded_models = set() for exclude in excludes: if '.' in exclude: app_label, model_name = exclude.split('.', 1) model_obj = app_cache.get_model(app_label, model_name) if not model_obj: raise CommandError('Unknown model in excludes: %s' % exclude) excluded_models.add(model_obj) else: try: app_obj = app_cache.get_app_config(exclude).models_module if app_obj is not None: excluded_apps.add(app_obj) except LookupError: raise CommandError('Unknown app in excludes: %s' % exclude) if len(app_labels) == 0: if primary_keys: raise CommandError("You can only use --pks option with one model") app_list = OrderedDict((app_config.models_module, None) for app_config in app_cache.get_app_configs(only_with_models_module=True) if app_config.models_module not in excluded_apps) else: if len(app_labels) > 1 and primary_keys: raise CommandError("You can only use --pks option with one model") app_list = OrderedDict() for label in app_labels: try: app_label, model_label = label.split('.') try: app = app_cache.get_app_config(app_label).models_module except LookupError: raise CommandError("Unknown application: %s" % app_label) if app is None or app in excluded_apps: continue model = app_cache.get_model(app_label, model_label) if model is None: raise CommandError("Unknown model: %s.%s" % (app_label, model_label)) if app in app_list.keys(): if app_list[app] and model not in app_list[app]: app_list[app].append(model) else: app_list[app] = [model] except ValueError: if primary_keys: raise CommandError("You can only use --pks option with one model") # This is just an app - no model qualifier app_label = label try: app = app_cache.get_app_config(app_label).models_module except LookupError: raise CommandError("Unknown application: %s" % app_label) if app is None or app in excluded_apps: continue app_list[app] = None # Check that the serialization format exists; this is a shortcut to # avoid collating all the objects and _then_ failing. if format not in serializers.get_public_serializer_formats(): try: serializers.get_serializer(format) except serializers.SerializerDoesNotExist: pass raise CommandError("Unknown serialization format: %s" % format) def get_objects(): # Collate the objects to be serialized. for model in sort_dependencies(app_list.items()): if model in excluded_models: continue if not model._meta.proxy and router.allow_migrate(using, model): if use_base_manager: objects = model._base_manager else: objects = model._default_manager queryset = objects.using(using).order_by(model._meta.pk.name) if primary_keys: queryset = queryset.filter(pk__in=primary_keys) for obj in queryset.iterator(): yield obj try: self.stdout.ending = None serializers.serialize(format, get_objects(), indent=indent, use_natural_foreign_keys=use_natural_foreign_keys, use_natural_primary_keys=use_natural_primary_keys, stream=self.stdout) except Exception as e: if show_traceback: raise raise CommandError("Unable to serialize database: %s" % e)
def test_sql_destroy_indexes(self): app = app_cache.get_app_config("commands_sql").models_module output = sql_destroy_indexes(app, no_style(), connections[DEFAULT_DB_ALIAS]) # PostgreSQL creates one additional index for CharField self.assertIn(self.count_ddl(output, "DROP INDEX"), [3, 4])
def index(self, request, extra_context=None): """ Displays the main admin index page, which lists all of the installed apps that have been registered in this site. """ app_dict = {} user = request.user for model, model_admin in self._registry.items(): app_label = model._meta.app_label has_module_perms = user.has_module_perms(app_label) if has_module_perms: perms = model_admin.get_model_perms(request) # Check whether user has any perm for this module. # If so, add the module to the model_list. if True in perms.values(): info = (app_label, model._meta.model_name) model_dict = { 'name': capfirst(model._meta.verbose_name_plural), 'object_name': model._meta.object_name, 'perms': perms, } if perms.get('change', False): try: model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=self.name) except NoReverseMatch: pass if perms.get('add', False): try: model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=self.name) except NoReverseMatch: pass if app_label in app_dict: app_dict[app_label]['models'].append(model_dict) else: app_dict[app_label] = { 'name': app_cache.get_app_config(app_label).verbose_name, 'app_label': app_label, 'app_url': reverse('admin:app_list', kwargs={'app_label': app_label}, current_app=self.name), 'has_module_perms': has_module_perms, 'models': [model_dict], } # Sort the apps alphabetically. app_list = list(six.itervalues(app_dict)) app_list.sort(key=lambda x: x['name'].lower()) # Sort the models alphabetically within each app. for app in app_list: app['models'].sort(key=lambda x: x['name']) context = dict( self.each_context(), title=self.index_title, app_list=app_list, ) context.update(extra_context or {}) return TemplateResponse(request, self.index_template or 'admin/index.html', context, current_app=self.name)
def test_router_honored(self): app = app_cache.get_app_config("commands_sql").models_module for sql_command in (sql_all, sql_create, sql_delete, sql_indexes, sql_destroy_indexes): output = sql_command(app, no_style(), connections[DEFAULT_DB_ALIAS]) self.assertEqual(len(output), 0, "%s command is not honoring routers" % sql_command.__name__)
def build_test(label): """ Construct a test case with the specified label. Label should be of the form app_label.TestClass or app_label.TestClass.test_method. Returns an instantiated test or test suite corresponding to the label provided. """ parts = label.split('.') if len(parts) < 2 or len(parts) > 3: raise ValueError("Test label '%s' should be of the form app.TestCase " "or app.TestCase.test_method" % label) app_config = app_cache.get_app_config(parts[0]) models_module = app_config.models_module tests_module = get_tests(app_config) test_modules = [] if models_module: test_modules.append(models_module) if tests_module: test_modules.append(tests_module) TestClass = None for module in test_modules: TestClass = getattr(models_module, parts[1], None) if TestClass is not None: break try: if issubclass(TestClass, (unittest.TestCase, real_unittest.TestCase)): if len(parts) == 2: # label is app.TestClass try: return unittest.TestLoader().loadTestsFromTestCase( TestClass) except TypeError: raise ValueError( "Test label '%s' does not refer to a test class" % label) else: # label is app.TestClass.test_method return TestClass(parts[2]) except TypeError: # TestClass isn't a TestClass - it must be a method or normal class pass # # If there isn't a TestCase, look for a doctest that matches # tests = [] for module in test_modules: try: doctests = make_doctest(module) # Now iterate over the suite, looking for doctests whose name # matches the pattern that was given for test in doctests: if test._dt_test.name in ( '%s.%s' % (module.__name__, '.'.join(parts[1:])), '%s.__test__.%s' % ( module.__name__, '.'.join(parts[1:]))): tests.append(test) except ValueError: # No doctests found. pass # If no tests were found, then we were given a bad test label. if not tests: raise ValueError("Test label '%s' does not refer to a test" % label) # Construct a suite out of the tests that matched. return unittest.TestSuite(tests)
def test_username_non_unique(self): "A non-unique USERNAME_FIELD should raise a model validation error." new_io = StringIO() get_validation_errors(new_io, app_cache.get_app_config('auth').models_module) self.assertIn("The USERNAME_FIELD must be unique. Add unique=True to the field parameters.", new_io.getvalue())
def test_username_not_in_required_fields(self): "USERNAME_FIELD should not appear in REQUIRED_FIELDS." new_io = StringIO() get_validation_errors(new_io, app_cache.get_app_config('auth').models_module) self.assertIn("The field named as the USERNAME_FIELD should not be included in REQUIRED_FIELDS on a swappable User model.", new_io.getvalue())
def test_required_fields_is_list(self): "REQUIRED_FIELDS should be a list." new_io = StringIO() get_validation_errors(new_io, app_cache.get_app_config('auth').models_module) self.assertIn("The REQUIRED_FIELDS must be a list or tuple.", new_io.getvalue())
def handle(self, *app_labels, **options): self.verbosity = int(options.get('verbosity')) self.interactive = options.get('interactive') self.dry_run = options.get('dry_run', False) self.merge = options.get('merge', False) # Make sure the app they asked for exists app_labels = set(app_labels) bad_app_labels = set() for app_label in app_labels: try: app_cache.get_app_config(app_label) except LookupError: bad_app_labels.add(app_label) if bad_app_labels: for app_label in bad_app_labels: self.stderr.write("App '%s' could not be found. Is it in INSTALLED_APPS?" % app_label) sys.exit(2) # Load the current graph state. Takes a connection, but it's not used # (makemigrations doesn't look at the database state). loader = MigrationLoader(connections[DEFAULT_DB_ALIAS]) # Before anything else, see if there's conflicting apps and drop out # hard if there are any and they don't want to merge conflicts = loader.detect_conflicts() if conflicts and not self.merge: name_str = "; ".join( "%s in %s" % (", ".join(names), app) for app, names in conflicts.items() ) raise CommandError("Conflicting migrations detected (%s).\nTo fix them run 'python manage.py makemigrations --merge'" % name_str) # If they want to merge and there's nothing to merge, then politely exit if self.merge and not conflicts: self.stdout.write("No conflicts detected to merge.") return # If they want to merge and there is something to merge, then # divert into the merge code if self.merge and conflicts: return self.handle_merge(loader, conflicts) # Detect changes autodetector = MigrationAutodetector( loader.graph.project_state(), ProjectState.from_app_cache(app_cache), InteractiveMigrationQuestioner(specified_apps=app_labels), ) changes = autodetector.changes(graph=loader.graph, trim_to_apps=app_labels or None) # No changes? Tell them. if not changes and self.verbosity >= 1: if len(app_labels) == 1: self.stdout.write("No changes detected in app '%s'" % app_labels.pop()) elif len(app_labels) > 1: self.stdout.write("No changes detected in apps '%s'" % ("', '".join(app_labels))) else: self.stdout.write("No changes detected") return directory_created = {} for app_label, app_migrations in changes.items(): if self.verbosity >= 1: self.stdout.write(self.style.MIGRATE_HEADING("Migrations for '%s':" % app_label) + "\n") for migration in app_migrations: # Describe the migration writer = MigrationWriter(migration) if self.verbosity >= 1: self.stdout.write(" %s:\n" % (self.style.MIGRATE_LABEL(writer.filename),)) for operation in migration.operations: self.stdout.write(" - %s\n" % operation.describe()) # Write it if not self.dry_run: migrations_directory = os.path.dirname(writer.path) if not directory_created.get(app_label, False): if not os.path.isdir(migrations_directory): os.mkdir(migrations_directory) init_path = os.path.join(migrations_directory, "__init__.py") if not os.path.isfile(init_path): open(init_path, "w").close() # We just do this once per app directory_created[app_label] = True migration_string = writer.as_string() with open(writer.path, "wb") as fh: fh.write(migration_string)
def test_no_models(self): """Test that it's possible to load an app with no models.py file.""" app_config = app_cache.get_app_config('no_models') self.assertIsNone(app_config.models_module)
def migrations_module(cls, app_label): if app_label in settings.MIGRATION_MODULES: return settings.MIGRATION_MODULES[app_label] else: return "%s.migrations" % app_cache.get_app_config(app_label).name
def get_context_data(self, **kwargs): # Get the model class. try: app_cache.get_app_config(self.kwargs['app_label']) except LookupError: raise Http404(_("App %(app_label)r not found") % self.kwargs) model = app_cache.get_model(self.kwargs['app_label'], self.kwargs['model_name']) if model is None: raise Http404(_("Model %(model_name)r not found in app %(app_label)r") % self.kwargs) opts = model._meta # Gather fields/field descriptions. fields = [] for field in opts.fields: # ForeignKey is a special case since the field will actually be a # descriptor that returns the other object if isinstance(field, models.ForeignKey): data_type = field.rel.to.__name__ app_label = field.rel.to._meta.app_label verbose = utils.parse_rst( (_("the related `%(app_label)s.%(data_type)s` object") % { 'app_label': app_label, 'data_type': data_type, }), 'model', _('model:') + data_type, ) else: data_type = get_readable_field_data_type(field) verbose = field.verbose_name fields.append({ 'name': field.name, 'data_type': data_type, 'verbose': verbose, 'help_text': field.help_text, }) # Gather many-to-many fields. for field in opts.many_to_many: data_type = field.rel.to.__name__ app_label = field.rel.to._meta.app_label verbose = _("related `%(app_label)s.%(object_name)s` objects") % {'app_label': app_label, 'object_name': data_type} fields.append({ 'name': "%s.all" % field.name, "data_type": 'List', 'verbose': utils.parse_rst(_("all %s") % verbose, 'model', _('model:') + opts.model_name), }) fields.append({ 'name': "%s.count" % field.name, 'data_type': 'Integer', 'verbose': utils.parse_rst(_("number of %s") % verbose, 'model', _('model:') + opts.model_name), }) # Gather model methods. for func_name, func in model.__dict__.items(): if (inspect.isfunction(func) and len(inspect.getargspec(func)[0]) == 1): try: for exclude in MODEL_METHODS_EXCLUDE: if func_name.startswith(exclude): raise StopIteration except StopIteration: continue verbose = func.__doc__ if verbose: verbose = utils.parse_rst(utils.trim_docstring(verbose), 'model', _('model:') + opts.model_name) fields.append({ 'name': func_name, 'data_type': get_return_data_type(func_name), 'verbose': verbose, }) # Gather related objects for rel in opts.get_all_related_objects() + opts.get_all_related_many_to_many_objects(): verbose = _("related `%(app_label)s.%(object_name)s` objects") % {'app_label': rel.opts.app_label, 'object_name': rel.opts.object_name} accessor = rel.get_accessor_name() fields.append({ 'name': "%s.all" % accessor, 'data_type': 'List', 'verbose': utils.parse_rst(_("all %s") % verbose, 'model', _('model:') + opts.model_name), }) fields.append({ 'name': "%s.count" % accessor, 'data_type': 'Integer', 'verbose': utils.parse_rst(_("number of %s") % verbose, 'model', _('model:') + opts.model_name), }) kwargs.update({ 'name': '%s.%s' % (opts.app_label, opts.object_name), # Translators: %s is an object type name 'summary': _("Attributes on %s objects") % opts.object_name, 'description': model.__doc__, 'fields': fields, }) return super(ModelDetailView, self).get_context_data(**kwargs)