def build_query_parameters(self): parameter_type = 'query' params = self.get_method_parameter_data(parameter_type) if params: return params params = [] docstring = self.retrieve_docstring() or '' docstring += "\n" + get_view_description(self.callback) docstring = trim_docstring(docstring) if not docstring: return params split_lines = docstring.split('\n') # TODO: use filter_class instead of docstring if available for line in split_lines: param = line.split(' -- ') if len(param) == 2: params.append({'paramType': parameter_type, 'name': param[0].strip(), 'description': param[1].strip(), 'dataType': ''}) return params
def __get_notes__(self, callback, method=None): """ Returns the body of the docstring trimmed before any parameters are listed. First, get the class docstring and then get the method's. The methods will always inherit the class comments. """ docstring = "" if method is not None: docs = [] class_docs = self.__get_notes__(callback) method_docs = self.__eval_method_docstring_(callback, method) if class_docs is not None: docs.append(class_docs) if method_docs is not None: docs.append(method_docs) docstring += "\n".join(docs) else: docstring = trim_docstring(get_view_description(callback)) docstring = self.__strip_params_from_docstring__(docstring) docstring = docstring.replace("\n", "<br/>") return docstring
def get_field_help_text(cls, field): help_text = getattr(field, 'help_text') if not help_text: # attempt to read help text from underlying Model, if any such # model exists try: model = field.parent.Meta.model model_source = getattr(model, field.source, None) model_source_doc = getattr(model_source, '__doc__', None) if model_source_doc: # source is a function or property on the model so use it's # docstring help_text = trim_docstring(model_source_doc) else: # source is a Model field instead of callable so attempt # to read help_text from that model_field = model._meta.get_field_by_name(field.source) help_text = model_field[0].help_text except (AttributeError, IndexError, TypeError, FieldDoesNotExist): pass if getattr(field, 'read_only', False): help_text = help_text.strip() if help_text[-1] == '.': help_text = help_text[:-1] help_text += u'. Read-only' return help_text
def get_notes(self): """ Returns the body of the docstring trimmed before any parameters are listed. First, get the class docstring and then get the method's. The methods will always inherit the class comments. """ sw_notes = self._get_swdocs('notes') if sw_notes is not None: return sw_notes docstring = "" class_docs = trim_docstring(get_view_description(self.callback)) method_docs = self.get_docs() if class_docs is not None: docstring += class_docs if method_docs is not None: docstring += '\n' + method_docs docstring = IntrospectorHelper.strip_params_from_docstring(docstring) docstring = re.sub(r'\n\s+\n', "<br/>", docstring) docstring = docstring.replace("\n", " ") return docstring
def __eval_method_docstring_(self, callback, method): """ Attempts to fetch the docs for a class method. If method does not exist tries class based views method name because we might have subclassed GenericAPIView and some mixins. Returns None if the method does not exist after second try. """ mapping = { 'post': ['create'], 'get': ['retrieve', 'list'], 'put': ['update'], 'patch': ['partial_update'], 'delete': ['destroy'] } try: doc_str = None mapped = mapping[str(method).lower()] for m in mapped: try: doc_str = eval("callback.%s.__doc__" % (m)) except AttributeError: pass if doc_str: return self.__markdownize_method_docs( trim_docstring(doc_str)) except AttributeError: pass try: return eval("callback.%s.__doc__" % (str(method).lower())) except AttributeError: return None
def __eval_method_docstring_(self, callback, method): """ Attempts to fetch the docs for a class method. If method does not exist tries class based views method name because we might have subclassed GenericAPIView and some mixins. Returns None if the method does not exist after second try. """ mapping = { 'post': ['create'], 'get': ['retrieve', 'list'], 'put': ['update'], 'patch': ['partial_update'], 'delete': ['destroy'] } try: doc_str = None mapped = mapping[str(method).lower()] for m in mapped: try: doc_str = eval("callback.%s.__doc__" % (m)) except AttributeError: pass if doc_str: return self.__markdownize_method_docs(trim_docstring(doc_str)) except AttributeError: pass try: return eval("callback.%s.__doc__" % (str(method).lower())) except AttributeError: return None
def __build_query_params_from_docstring__(self, callback, method=None): params = [] # Combine class & method level comments. If parameters are specified if method is not None: try: docstring = trim_docstring(eval("callback.%s.__doc__" % (str(method).lower()))) except: if str(method).lower() == 'get': docstring = eval("callback.list.__doc__") elif str(method).lower() == 'post': docstring = eval("callback.create.__doc__") else: docstring = get_view_description(callback) params += self.__build_query_params_from_docstring__(callback) else: # Otherwise, get the class level docstring docstring = get_view_description(callback) split_lines = docstring.split('\n') if docstring else [] for line in split_lines: param = line.split(' -- ') if len(param) == 2: params.append({ 'paramType': 'query', 'name': param[0].strip(), 'description': param[1].strip(), 'dataType': '', }) return params
def __get_notes__(self, callback, method=None): """ Returns the body of the docstring trimmed before any parameters are listed. First, get the class docstring and then get the method's. The methods will always inherit the class comments. """ docstring = "" if method is not None: class_docs = self.__get_notes__(callback) try: method_docs = eval("callback.%s.__doc__" % (str(method).lower())) except: if str(method).lower() == 'get': method_docs = eval("callback.list.__doc__") elif str(method).lower() == 'post': method_docs = eval("callback.create.__doc__") else: method_docs = None if class_docs is not None: docstring += class_docs if method_docs is not None: docstring += method_docs else: docstring = trim_docstring(get_view_description(callback)) docstring = self.__strip_params_from_docstring__(docstring) docstring = docstring.replace("\n", "<br/>") return docstring
def _get_description(self): """Return the view's docstring as a description. This method is a customization of Django REST framework's. There are two changes: - Our parent's docstring is used if we don't have one ourselves. This makes it easy to use a base class with proper documentation on which items to expect. The documentation is propagated to every API that uses the base class. - The docstring is parsed with restructuredtext syntax instead of markdown (markdown is preferred by Django REST framework). """ description = self.__doc__ if description is None: # Trick to get our parent's docstring as a fallback if we don't # have one ourselves. From # http://stackoverflow.com/a/13937525/27401 . try: description = next( cls.__doc__ for cls in inspect.getmro(type(self)) if cls.__doc__ is not None) except StopIteration: pass return trim_docstring(description)
def _get_description(self): """Return the view's docstring as a description. This method is a customization of Django REST framework's. There are two changes: - Our parent's docstring is used if we don't have one ourselves. This makes it easy to use a base class with proper documentation on which items to expect. The documentation is propagated to every API that uses the base class. - The docstring is parsed with restructuredtext syntax instead of markdown (markdown is preferred by Django REST framework). """ description = self.__doc__ if description is None: # Trick to get our parent's docstring as a fallback if we don't # have one ourselves. From # http://stackoverflow.com/a/13937525/27401 . try: description = next(cls.__doc__ for cls in inspect.getmro(type(self)) if cls.__doc__ is not None) except StopIteration: pass return trim_docstring(description)
def get_method_summary(self, callback, method, path=None): docs = self.get_method_docs(callback, method, path) # If there is no docstring on the method, get class docs if docs is None: docs = self.get_description(callback) docs = trim_docstring(docs).split('\n')[0] return docs
def user_docs(self): for mod_prefix in ('botbot_plugins.plugins.', 'botbot.apps.plugins.core.'): try: docs = import_module(mod_prefix + self.slug).Plugin.__doc__ return trim_docstring(docs) except (ImportError, AttributeError): continue return ''
def retrieve_docstring(self): """ Attempts to fetch the docs for a class method. Returns None if the method does not exist """ method = self._get_method_callback() if not method: return None return trim_docstring(method.__doc__)
def get_summary(self): docs = self.get_docs() # If there is no docstring on the method, get class docs if docs is None: docs = self.parent.get_description() docs = trim_docstring(docs).split('\n')[0] return docs
def split_docstring(cls, docstring): docstring = trim_docstring(docstring) splitter_re = "^|\n{}".format(re.escape(cls.SPLITTER)) ptn = re.compile(splitter_re) splitted = ptn.split(docstring, maxsplit=1) # `rsplit` would be more useful though. if len(splitted) != 2: return None, None return splitted
def test_trim_docstring(self): trim_docstring_output = utils.trim_docstring(self.docstring) trimmed_docstring = ( 'This __doc__ output is required for testing. I copied this ' 'example from\n`admindocs` documentation. (TITLE)\n\n' 'Display an individual :model:`myapp.MyModel`.\n\n' '**Context**\n\n``RequestContext``\n\n``mymodel``\n' ' An instance of :model:`myapp.MyModel`.\n\n' '**Template:**\n\n:template:`myapp/my_template.html` ' '(DESCRIPTION)\n\nsome_metadata: some data') self.assertEqual(trim_docstring_output, trimmed_docstring)
def get_notes(self): """ Returns the body of the docstring trimmed before any parameters are listed. First, get the class docstring and then get the method's. The methods will always inherit the class comments. """ docstring = "" class_docs = trim_docstring(get_view_description(self.callback)) method_docs = trim_docstring(self.get_docs()) if class_docs is not None: docstring += class_docs if method_docs is not None: docstring += '\n\n' + method_docs docstring = IntrospectorHelper.strip_params_from_docstring(docstring) docstring = markdown.markdown(escape(docstring)) return docstring
def doc_functions(request): group_list = [] for group_name, functions in AVAILABLE_API_FUNCTIONS.items(): function_list = [] for name, function in functions.items(): heading, body, metadata = parse_docstring(function.__doc__) body = trim_docstring(body) function_list.append({ 'name': name, 'description': build_function_description(function), 'docstring': trim_docstring('{0}\n\n{1}'.format(heading, body)), }) function_list.sort(key=itemgetter('name')) group_list.append({'name': group_name, 'function_list': function_list}) group_list.sort(key=itemgetter('name')) return TemplateResponse(request, 'api/list_functions.html', {'group_list': group_list})
def get_summary(self): docs = self.get_docs() # If there is no docstring on the method, get class docs if docs is None: docs = self.parent.get_description() docs = trim_docstring(docs).split("\n")[0].split(".")[0] if apply_markdown: from .compat import strip_tags docs = strip_tags(do_markdown(docs)) return docs
def test_strip_params_from_docstring(self): class AnAPIView(APIView): """ My comments are here param -- my param """ pass docstring = IntrospectorHelper.strip_params_from_docstring(trim_docstring(AnAPIView.__doc__)) self.assertEqual("My comments are here<br/>", docstring)
def test_trim_docstring(self): trim_docstring_output = utils.trim_docstring(self.docstring) trimmed_docstring = ( 'This __doc__ output is required for testing. I copied this ' 'example from\n`admindocs` documentation. (TITLE)\n\n' 'Display an individual :model:`myapp.MyModel`.\n\n' '**Context**\n\n``RequestContext``\n\n``mymodel``\n' ' An instance of :model:`myapp.MyModel`.\n\n' '**Template:**\n\n:template:`myapp/my_template.html` ' '(DESCRIPTION)\n\nsome_metadata: some data' ) self.assertEqual(trim_docstring_output, trimmed_docstring)
def create_yaml_object(self, docstring): """Create YAML object from docstring""" docstring = trim_docstring(docstring) p = re.compile('^|\n{}'.format(YAMLDocstringParser.SPLITTER)) splitted = p.split(docstring) if len(splitted) < 2: return None yaml_string = splitted[1] yaml_string = formatting.dedent(yaml_string) try: return yaml.load(yaml_string) except yaml.YAMLError as e: return None
def test_strip_params_from_docstring(self): class AnAPIView(APIView): """ My comments are here param -- my param """ pass docgen = DocumentationGenerator() docstring = docgen.__strip_params_from_docstring__(trim_docstring(AnAPIView.__doc__)) self.assertEqual("My comments are here<br/><br/>", docstring)
def __get_method_docs__(self, callback, method): """ Attempts to retrieve method specific docs for an endpoint. If none are available, the class docstring will be used """ docs = self.__eval_method_docstring_(callback, method) if docs is None: docs = self.__get_description__(callback) docs = trim_docstring(docs).split('\n')[0] return docs
def test_strip_yaml_from_docstring(self): class AnAPIView(APIView): """ My comments are here --- # This is YAML param: my param """ pass docstring = IntrospectorHelper.strip_yaml_from_docstring(trim_docstring(AnAPIView.__doc__)) self.assertEqual("My comments are here", docstring)
def test_strip_params_from_docstring(self): class AnAPIView(APIView): """ My comments are here param -- my param """ pass docgen = DocumentationGenerator() docstring = docgen.__strip_params_from_docstring__(trim_docstring(AnAPIView.__doc__)) self.assertEqual("My comments are here<br/>", docstring)
def load_obj_from_docstring(self, docstring): """Loads YAML from docstring""" # update one dict by other recursively def recursive_update(obj, other): for key, value in other.iteritems(): if key in obj and key != 'overwrite': # if value is dictionary we need to update it if isinstance(value, dict) and not value.get('overwrite', False): recursive_update(obj[key], other[key]) # if value is a list we need to extend it elif isinstance(value, list): obj[key].extend(value) else: obj[key] = value else: obj[key] = value if not docstring: return {} split_lines = trim_docstring(docstring).split('\n') # Cut YAML from rest of docstring for index, line in enumerate(split_lines): line = line.strip() if line.startswith('---'): cut_from = index break else: return {} yaml_string = formatting.dedent("\n".join(split_lines[cut_from:])) try: yaml_obj = yaml.load(yaml_string) # if is parent view specified, we need to get docs from parent view if 'inherit_docs_from' in yaml_obj: parent_class = self._load_class( yaml_obj['inherit_docs_from'], self.method_introspector.callback) parent_docs = self.method_introspector.get_inherited_docs( parent_class) parent_obj = self.load_obj_from_docstring( docstring=parent_docs) recursive_update(parent_obj, yaml_obj) yaml_obj = parent_obj except yaml.YAMLError as e: self.yaml_error = e return {} return yaml_obj
def test_strip_yaml_from_docstring(self): class AnAPIView(APIView): """ My comments are here --- # This is YAML param: my param """ pass docstring = IntrospectorHelper.strip_yaml_from_docstring( trim_docstring(AnAPIView.__doc__)) self.assertEqual("My comments are here", docstring)
def strip_yaml_from_docstring(docstring): """ Strips YAML from the docstring. """ split_lines = trim_docstring(docstring).split('\n') cut_off = None for index, line in enumerate(split_lines): line = line.strip() if line.startswith('---'): cut_off = index break if cut_off is not None: split_lines = split_lines[0:cut_off] return "\n".join(split_lines)
def strip_params_from_docstring(docstring): """ Strips the params from the docstring (ie. myparam -- Some param) """ split_lines = trim_docstring(docstring).split('\n') cut_off = None for index, line in enumerate(split_lines): line = line.strip() if line.find('--') != -1: cut_off = index break if cut_off is not None: split_lines = split_lines[0:cut_off] return u'\n'.join(split_lines)
def __strip_params_from_docstring__(self, docstring): """ Strips the params from the docstring (ie. myparam -- Some param) will not be removed from the text body """ split_lines = trim_docstring(docstring).split('\n') cut_off = None for index, line in enumerate(split_lines): line = line.strip() if line.find('--') != -1: cut_off = index break if cut_off is not None: split_lines = split_lines[0:cut_off] return "<br/>".join(split_lines)
def strip_params_from_docstring(docstring): """ Strips the params from the docstring (ie. myparam -- Some param) will not be removed from the text body """ split_lines = trim_docstring(docstring).split('\n') cut_off = None for index, line in enumerate(split_lines): line = line.strip() if PARAMS_PATTERN.search(line): cut_off = index break if cut_off is not None: split_lines = split_lines[0:cut_off] return "\n".join(split_lines)
def strip_params_from_docstring(self, docstring): """ Strips the params from the docstring (ie. myparam -- Some param) will not be removed from the text body """ split_lines = trim_docstring(docstring).split('\n') cut_off = None for index, line in enumerate(split_lines): line = line.strip() if line.find('--') != -1: cut_off = index break if cut_off is not None: split_lines = split_lines[0:cut_off] return "<br/>".join(split_lines)
def strip_yaml_from_docstring(docstring): """ Strips YAML from the docstring. """ split_lines = trim_docstring(docstring).split('\n') cut_off = None for index in range(len(split_lines) - 1, -1, -1): line = split_lines[index] line = line.strip() if line == '---': cut_off = index break if cut_off is not None: split_lines = split_lines[0:cut_off] return "\n".join(split_lines)
def load_obj_from_docstring(self, docstring): """Loads YAML from docstring""" # update one dict by other recursively def recursive_update(obj, other): for key, value in other.iteritems(): if key in obj and key != 'overwrite': # if value is dictionary we need to update it if isinstance(value, dict) and not value.get('overwrite', False): recursive_update(obj[key], other[key]) # if value is a list we need to extend it elif isinstance(value, list): obj[key].extend(value) else: obj[key] = value else: obj[key] = value if not docstring: return {} split_lines = trim_docstring(docstring).split('\n') # Cut YAML from rest of docstring for index, line in enumerate(split_lines): line = line.strip() if line.startswith('---'): cut_from = index break else: return {} yaml_string = formatting.dedent("\n".join(split_lines[cut_from:])) try: yaml_obj = yaml.load(yaml_string) # if is parent view specified, we need to get docs from parent view if 'inherit_docs_from' in yaml_obj: parent_class = self._load_class(yaml_obj['inherit_docs_from'], self.method_introspector.callback) parent_docs = self.method_introspector.get_inherited_docs(parent_class) parent_obj = self.load_obj_from_docstring(docstring=parent_docs) recursive_update(parent_obj, yaml_obj) yaml_obj = parent_obj except yaml.YAMLError as e: self.yaml_error = e return {} return yaml_obj
def get_parsed_docstring(docstring): docstring = trim_docstring(docstring) split_lines = docstring.split('\n') trimmed = False # Flag if string needs to be trimmed _params = [] description = docstring version = '0' auth_req = False output_method = '' attr_found = False for line in split_lines: if not trimmed: needle = line.find('--') if needle != -1: trim_at = docstring.find(line) description = docstring[:trim_at] trimmed = True params = line.split(' -- ') if len(params) == 2: param = params[0] optional = False if params[0].startswith('!'): optional = True param = param[1:] _params.append([param.strip(), params[1].strip(), optional]) if line.find('version:') != -1: version = line.split(':')[1].strip() attr_found = True if line.find('auth_req:') != -1: auth_req = str2bool(line.split(':')[1].strip()) attr_found = True if line.find('output_method:') != -1: output_method = line.split(':', 1)[1].strip() attr_found = True if attr_found and not trimmed: trim_at = docstring.find(line) description = docstring[:trim_at - 1] trimmed = True return {'description': description, 'params': _params, 'version': version, 'auth_req': auth_req, 'output_method': output_method}
def strip_params_from_docstring(docstring): """ Strips the params from the docstring (ie. myparam -- Some param) will not be removed from the text body """ params_pattern = re.compile(r'(?:^|[^-])--(?:$|[^-])') split_lines = trim_docstring(docstring).split('\n') cut_off = None for index, line in enumerate(split_lines): line = line.strip() if params_pattern.search(line): cut_off = index break if cut_off is not None: split_lines = split_lines[0:cut_off] return "\n".join(split_lines)
def strip_params_from_docstring(docstring): """ Strips the params from the docstring (ie. myparam -- Some param) will not be removed from the text body """ params_pattern = re.compile(r' -- ') split_lines = trim_docstring(docstring).split('\n') cut_off = None for index, line in enumerate(split_lines): line = line.strip() if params_pattern.search(line): cut_off = index break if cut_off is not None: split_lines = split_lines[0:cut_off] return "\n".join(split_lines)
def get_callback_property_data(cls, callback, property_name): """ Returns the data parsed from the docstring defined for the given property attached to the callback. Returns None if no such docstring has been defined. """ doc = getattr(callback, property_name, '') doc = trim_docstring(doc) data = None if doc: try: data = yaml.load(StringIO(doc)) except yaml.ScannerError: logger.exception('Failed to parse docstring defined for %s', property_name) return data
def load_obj_from_docstring(self, docstring): """Loads YAML from docstring""" split_lines = trim_docstring(docstring).split('\n') # Cut YAML from rest of docstring for index, line in enumerate(split_lines): line = line.strip() if line.startswith('---'): cut_from = index break else: return None yaml_string = "\n".join(split_lines[cut_from:]) yaml_string = formatting.dedent(yaml_string) try: return yaml.load(yaml_string) except yaml.YAMLError as e: self.yaml_error = e return None
def get_docstring_fields(self, description): # bubble modify(add) fields = [] split_lines = trim_docstring(description).split('\n') temp_lines = [] for line in split_lines: param = line.split(' -- ') if len(param) == 2: fields.append(coreapi.Field(name=param[0].strip(), required=False, location='query', description=param[1].strip())) else: temp_lines.append(line) temp_lines.append('\n') description = ''.join(temp_lines) return fields, description
def __get_notes__(self, callback, method=None): """ Returns the body of the docstring trimmed before any parameters are listed. First, get the class docstring and then get the method's. The methods will always inherit the class comments. """ docstring = "" if method is not None: class_docs = self.__get_notes__(callback) method_docs = self.__eval_method_docstring_(callback, method) if class_docs is not None: docstring += class_docs if method_docs is not None: docstring += method_docs else: docstring = trim_docstring(get_view_description(callback, html=True)) docstring = self.__strip_params_from_docstring__(docstring) return docstring
def get_notes(self, callback, method=None, path=None): """ Returns the body of the docstring trimmed before any parameters are listed. First, get the class docstring and then get the method's. The methods will always inherit the class comments. """ docstring = "" if method is not None: class_docs = self.get_notes(callback) method_docs = self.get_method_docs(callback, method, path) if class_docs is not None: docstring += class_docs if method_docs is not None: docstring = '%s \n %s' % (class_docs, method_docs) else: docstring = trim_docstring(get_view_description(callback)) docstring = self.strip_params_from_docstring(docstring) docstring = docstring.replace("\n\n", "<br/>") return docstring
def __get_notes__(self, callback, method=None): """ Returns the body of the docstring trimmed before any parameters are listed. First, get the class docstring and then get the method's. The methods will always inherit the class comments. """ docstring = "" if method is not None: class_docs = self.__get_notes__(callback) method_docs = self.__eval_method_docstring_(callback, method) if class_docs is not None: docstring += class_docs if method_docs is not None: docstring += method_docs else: docstring = trim_docstring(get_view_description(callback)) docstring = self.__strip_params_from_docstring__(docstring) docstring = docstring.replace("\n", "<br/>") return docstring
def model_detail(request, app_label, model_name): if not utils.docutils_is_available: return missing_docutils_page(request) # Get the model class. try: app_mod = models.get_app(app_label) except ImproperlyConfigured: raise Http404, _("App %r not found") % app_label model = None for m in models.get_models(app_mod): if m._meta.object_name.lower() == model_name: model = m break if model is None: raise Http404, _( "Model %(model_name)r not found in app %(app_label)r") % { 'model_name': model_name, 'app_label': app_label } 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 = related_object_name = 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 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.module_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(): 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.module_name), }) fields.append({ 'name': "%s.count" % accessor, 'data_type': 'Integer', 'verbose': utils.parse_rst( _("number of %s") % verbose, 'model', _('model:') + opts.module_name), }) return render_to_response( 'admin_doc/model_detail.html', { 'root_path': get_root_path(), 'name': '%s.%s' % (opts.app_label, opts.object_name), 'summary': _("Fields on %s objects") % opts.object_name, 'description': model.__doc__, 'fields': fields, }, context_instance=RequestContext(request))
def get_context_data(self, **kwargs): model_name = self.kwargs['model_name'] # Get the model class. try: app_config = apps.get_app_config(self.kwargs['app_label']) except LookupError: raise Http404(_("App %(app_label)r not found") % self.kwargs) try: model = app_config.get_model(model_name) except LookupError: raise Http404( _("Model %(model_name)r not found in app %(app_label)r") % self.kwargs) opts = model._meta title, body, metadata = utils.parse_docstring(model.__doc__) if title: title = utils.parse_rst(title, 'model', _('model:') + model_name) if body: body = utils.parse_rst(body, 'model', _('model:') + model_name) # 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.related_objects: verbose = _("related `%(app_label)s.%(object_name)s` objects") % { 'app_label': rel.related_model._meta.app_label, 'object_name': rel.related_model._meta.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), 'summary': title, 'description': body, 'fields': fields, }) return super(ModelDetailView, self).get_context_data(**kwargs)
def __trim(self, docstring): """ Trims whitespace from docstring """ return trim_docstring(docstring)
def get_context_data(self, **kwargs): model_name = self.kwargs['model_name'] # Get the model class. try: app_config = apps.get_app_config(self.kwargs['app_label']) except LookupError: raise Http404(_("App %(app_label)r not found") % self.kwargs) try: model = app_config.get_model(model_name) except LookupError: raise Http404( _("Model %(model_name)r not found in app %(app_label)r") % self.kwargs) opts = model._meta title, body, metadata = utils.parse_docstring(model.__doc__) if title: title = utils.parse_rst(title, 'model', _('model:') + model_name) if body: body = utils.parse_rst(body, 'model', _('model:') + model_name) # 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.remote_field.model.__name__ app_label = field.remote_field.model._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 or '', 'help_text': field.help_text, }) # Gather many-to-many fields. for field in opts.many_to_many: data_type = field.remote_field.model.__name__ app_label = field.remote_field.model._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), }) methods = [] # Gather model methods. for func_name, func in model.__dict__.items(): if inspect.isfunction(func): 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) # If a method has no arguments, show it as a 'field', otherwise # as a 'method with arguments'. if func_has_no_args(func) and not func_accepts_kwargs( func) and not func_accepts_var_args(func): fields.append({ 'name': func_name, 'data_type': get_return_data_type(func_name), 'verbose': verbose or '', }) else: arguments = get_func_full_args(func) # Join arguments with ', ' and in case of default value, # join it with '='. Use repr() so that strings will be # correctly displayed. print_arguments = ', '.join([ '='.join( list(arg_el[:1]) + [repr(el) for el in arg_el[1:]]) for arg_el in arguments ]) methods.append({ 'name': func_name, 'arguments': print_arguments, 'verbose': verbose or '', }) # Gather related objects for rel in opts.related_objects: verbose = _("related `%(app_label)s.%(object_name)s` objects") % { 'app_label': rel.related_model._meta.app_label, 'object_name': rel.related_model._meta.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), 'summary': title, 'description': body, 'fields': fields, 'methods': methods, }) return super(ModelDetailView, self).get_context_data(**kwargs)