Skip to content
Projeler
Gruplar
Parçacıklar
Yardım
Yükleniyor...
Oturum aç / Kaydol
Gezinmeyi değiştir
D
django
Proje
Proje
Ayrıntılar
Etkinlik
Cycle Analytics
Depo (repository)
Depo (repository)
Dosyalar
Kayıtlar (commit)
Dallar (branch)
Etiketler
Katkıda bulunanlar
Grafik
Karşılaştır
Grafikler
Konular (issue)
0
Konular (issue)
0
Liste
Pano
Etiketler
Kilometre Taşları
Birleştirme (merge) Talepleri
0
Birleştirme (merge) Talepleri
0
CI / CD
CI / CD
İş akışları (pipeline)
İşler
Zamanlamalar
Grafikler
Paketler
Paketler
Wiki
Wiki
Parçacıklar
Parçacıklar
Üyeler
Üyeler
Collapse sidebar
Close sidebar
Etkinlik
Grafik
Grafikler
Yeni bir konu (issue) oluştur
İşler
Kayıtlar (commit)
Konu (issue) Panoları
Kenar çubuğunu aç
Batuhan Osman TASKAYA
django
Commits
f3dc1732
Kaydet (Commit)
f3dc1732
authored
Eki 30, 2014
tarafından
Zan Anderle
Kaydeden (comit)
Tim Graham
Eyl 07, 2015
Dosyalara gözat
Seçenekler
Dosyalara Gözat
İndir
Eposta Yamaları
Sade Fark
Fixed #24917 -- Made admindocs display model methods that take arguments.
üst
3c5862cc
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
201 additions
and
14 deletions
+201
-14
model_detail.html
...o/contrib/admindocs/templates/admin_doc/model_detail.html
+25
-0
views.py
django/contrib/admindocs/views.py
+31
-8
inspect.py
django/utils/inspect.py
+52
-1
admindocs.txt
docs/ref/contrib/admin/admindocs.txt
+9
-4
1.9.txt
docs/releases/1.9.txt
+6
-0
models.py
tests/admin_docs/models.py
+11
-0
tests.py
tests/admin_docs/tests.py
+32
-1
test_inspect.py
tests/utils_tests/test_inspect.py
+35
-0
No files found.
django/contrib/admindocs/templates/admin_doc/model_detail.html
Dosyayı görüntüle @
f3dc1732
...
...
@@ -27,6 +27,7 @@
{{ description }}
<h3>
{% trans 'Fields' %}
</h3>
<div
class=
"module"
>
<table
class=
"model"
>
<thead>
...
...
@@ -48,6 +49,30 @@
</table>
</div>
{% if methods %}
<h3>
{% trans 'Methods with arguments' %}
</h3>
<div
class=
"module"
>
<table
class=
"model"
>
<thead>
<tr>
<th>
{% trans 'Method' %}
</th>
<th>
{% trans 'Arguments' %}
</th>
<th>
{% trans 'Description' %}
</th>
</tr>
</thead>
<tbody>
{% for method in methods|dictsort:"name" %}
<tr>
<td>
{{ method.name }}
</td>
<td>
{{ method.arguments }}
</td>
<td>
{{ method.verbose }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
<p
class=
"small"
><a
href=
"{% url 'django-admindocs-models-index' %}"
>
‹
{% trans 'Back to Model Documentation' %}
</a></p>
</div>
{% endblock %}
django/contrib/admindocs/views.py
Dosyayı görüntüle @
f3dc1732
...
...
@@ -14,7 +14,10 @@ from django.db import models
from
django.http
import
Http404
from
django.template.engine
import
Engine
from
django.utils.decorators
import
method_decorator
from
django.utils.inspect
import
func_has_no_args
from
django.utils.inspect
import
(
func_accepts_kwargs
,
func_accepts_var_args
,
func_has_no_args
,
get_func_full_args
,
)
from
django.utils.translation
import
ugettext
as
_
from
django.views.generic
import
TemplateView
...
...
@@ -219,7 +222,7 @@ class ModelDetailView(BaseAdminDocsView):
fields
.
append
({
'name'
:
field
.
name
,
'data_type'
:
data_type
,
'verbose'
:
verbose
,
'verbose'
:
verbose
or
''
,
'help_text'
:
field
.
help_text
,
})
...
...
@@ -242,9 +245,10 @@ class ModelDetailView(BaseAdminDocsView):
'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
)
and
func_has_no_args
(
func
)
:
if
inspect
.
isfunction
(
func
):
try
:
for
exclude
in
MODEL_METHODS_EXCLUDE
:
if
func_name
.
startswith
(
exclude
):
...
...
@@ -254,11 +258,29 @@ class ModelDetailView(BaseAdminDocsView):
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
,
})
# 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
)
print_arguments
=
arguments
# 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
:
...
...
@@ -282,6 +304,7 @@ class ModelDetailView(BaseAdminDocsView):
'summary'
:
title
,
'description'
:
body
,
'fields'
:
fields
,
'methods'
:
methods
,
})
return
super
(
ModelDetailView
,
self
)
.
get_context_data
(
**
kwargs
)
...
...
django/utils/inspect.py
Dosyayı görüntüle @
f3dc1732
...
...
@@ -43,6 +43,44 @@ def get_func_args(func):
]
def
get_func_full_args
(
func
):
"""
Return a list of (argument name, default value) tuples. If the argument
does not have a default value, omit it in the tuple. Arguments such as
*args and **kwargs are also included.
"""
if
six
.
PY2
:
argspec
=
inspect
.
getargspec
(
func
)
args
=
argspec
.
args
[
1
:]
# ignore 'self'
defaults
=
argspec
.
defaults
or
[]
# Split args into two lists depending on whether they have default value
no_default
=
args
[:
len
(
args
)
-
len
(
defaults
)]
with_default
=
args
[
len
(
args
)
-
len
(
defaults
):]
# Join the two lists and combine it with default values
args
=
[(
arg
,)
for
arg
in
no_default
]
+
zip
(
with_default
,
defaults
)
# Add possible *args and **kwargs and prepend them with '*' or '**'
varargs
=
[(
'*'
+
argspec
.
varargs
,)]
if
argspec
.
varargs
else
[]
kwargs
=
[(
'**'
+
argspec
.
keywords
,)]
if
argspec
.
keywords
else
[]
return
args
+
varargs
+
kwargs
sig
=
inspect
.
signature
(
func
)
args
=
[]
for
arg_name
,
param
in
sig
.
parameters
.
items
():
name
=
arg_name
# Ignore 'self'
if
name
==
'self'
:
continue
if
param
.
kind
==
inspect
.
Parameter
.
VAR_POSITIONAL
:
name
=
'*'
+
name
elif
param
.
kind
==
inspect
.
Parameter
.
VAR_KEYWORD
:
name
=
'**'
+
name
if
param
.
default
!=
inspect
.
Parameter
.
empty
:
args
.
append
((
name
,
param
.
default
))
else
:
args
.
append
((
name
,))
return
args
def
func_accepts_kwargs
(
func
):
if
six
.
PY2
:
# Not all callables are inspectable with getargspec, so we'll
...
...
@@ -64,10 +102,23 @@ def func_accepts_kwargs(func):
)
def
func_accepts_var_args
(
func
):
"""
Return True if function 'func' accepts positional arguments *args.
"""
if
six
.
PY2
:
return
inspect
.
getargspec
(
func
)[
1
]
is
not
None
return
any
(
p
for
p
in
inspect
.
signature
(
func
)
.
parameters
.
values
()
if
p
.
kind
==
p
.
VAR_POSITIONAL
)
def
func_has_no_args
(
func
):
args
=
inspect
.
getargspec
(
func
)[
0
]
if
six
.
PY2
else
[
p
for
p
in
inspect
.
signature
(
func
)
.
parameters
.
values
()
if
p
.
kind
==
p
.
POSITIONAL_OR_KEYWORD
and
p
.
default
is
p
.
empty
if
p
.
kind
==
p
.
POSITIONAL_OR_KEYWORD
]
return
len
(
args
)
==
1
...
...
docs/ref/contrib/admin/admindocs.txt
Dosyayı görüntüle @
f3dc1732
...
...
@@ -60,10 +60,15 @@ Model reference
===============
The **models** section of the ``admindocs`` page describes each model in the
system along with all the fields and methods (without any arguments) available
on it. While model properties don't have any arguments, they are not listed.
Relationships to other models appear as hyperlinks. Descriptions are pulled
from ``help_text`` attributes on fields or from docstrings on model methods.
system along with all the fields and methods available on it. Relationships
to other models appear as hyperlinks. Descriptions are pulled from ``help_text``
attributes on fields or from docstrings on model methods.
.. versionchanged:: 1.9
The **models** section of the ``admindocs`` now describes methods that take
arguments as well. In previous versions it was restricted to methods
without arguments.
A model with useful documentation might look like this::
...
...
docs/releases/1.9.txt
Dosyayı görüntüle @
f3dc1732
...
...
@@ -163,6 +163,12 @@ Minor features
* JavaScript slug generation now supports Romanian characters.
:mod:`django.contrib.admindocs`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* The model section of the ``admindocs`` now also describes methods that take
arguments, rather than ignoring them.
:mod:`django.contrib.auth`
^^^^^^^^^^^^^^^^^^^^^^^^^^
...
...
tests/admin_docs/models.py
Dosyayı görüntüle @
f3dc1732
...
...
@@ -44,6 +44,17 @@ class Person(models.Model):
def
_get_full_name
(
self
):
return
"
%
s
%
s"
%
(
self
.
first_name
,
self
.
last_name
)
def
rename_company
(
self
,
new_name
):
self
.
company
.
name
=
new_name
self
.
company
.
save
()
return
new_name
def
dummy_function
(
self
,
baz
,
rox
,
*
some_args
,
**
some_kwargs
):
return
some_kwargs
def
suffix_company_name
(
self
,
suffix
=
'ltd'
):
return
self
.
company
.
name
+
suffix
def
add_image
(
self
):
pass
...
...
tests/admin_docs/tests.py
Dosyayı görüntüle @
f3dc1732
...
...
@@ -246,7 +246,7 @@ class TestModelDetailView(TestDataMixin, AdminDocsTestCase):
def
setUp
(
self
):
self
.
client
.
login
(
username
=
'super'
,
password
=
'secret'
)
with
captured_stderr
()
as
self
.
docutils_stderr
:
self
.
response
=
self
.
client
.
get
(
reverse
(
'django-admindocs-models-detail'
,
args
=
[
'admin_docs'
,
'
p
erson'
]))
self
.
response
=
self
.
client
.
get
(
reverse
(
'django-admindocs-models-detail'
,
args
=
[
'admin_docs'
,
'
P
erson'
]))
def
test_method_excludes
(
self
):
"""
...
...
@@ -261,6 +261,34 @@ class TestModelDetailView(TestDataMixin, AdminDocsTestCase):
self
.
assertNotContains
(
self
.
response
,
"<td>set_status</td>"
)
self
.
assertNotContains
(
self
.
response
,
"<td>save_changes</td>"
)
def
test_methods_with_arguments
(
self
):
"""
Methods that take arguments should also displayed.
"""
self
.
assertContains
(
self
.
response
,
"<h3>Methods with arguments</h3>"
)
self
.
assertContains
(
self
.
response
,
"<td>rename_company</td>"
)
self
.
assertContains
(
self
.
response
,
"<td>dummy_function</td>"
)
self
.
assertContains
(
self
.
response
,
"<td>suffix_company_name</td>"
)
def
test_methods_with_arguments_display_arguments
(
self
):
"""
Methods with arguments should have their arguments displayed.
"""
self
.
assertContains
(
self
.
response
,
"<td>new_name</td>"
)
def
test_methods_with_arguments_display_arguments_default_value
(
self
):
"""
Methods with keyword arguments should have their arguments displayed.
"""
self
.
assertContains
(
self
.
response
,
"<td>suffix='ltd'</td>"
)
def
test_methods_with_multiple_arguments_display_arguments
(
self
):
"""
Methods with multiple arguments should have all their arguments
displayed, but omitting 'self'.
"""
self
.
assertContains
(
self
.
response
,
"<td>baz, rox, *some_args, **some_kwargs</td>"
)
def
test_method_data_types
(
self
):
"""
We should be able to get a basic idea of the type returned
...
...
@@ -368,6 +396,9 @@ class TestModelDetailView(TestDataMixin, AdminDocsTestCase):
self
.
assertContains
(
self
.
response
,
body
,
html
=
True
)
self
.
assertContains
(
self
.
response
,
model_body
,
html
=
True
)
def
test_model_detail_title
(
self
):
self
.
assertContains
(
self
.
response
,
'<h1>admin_docs.Person</h1>'
,
html
=
True
)
@unittest.skipUnless
(
utils
.
docutils_is_available
,
"no docutils installed."
)
class
TestUtils
(
AdminDocsTestCase
):
...
...
tests/utils_tests/test_inspect.py
0 → 100644
Dosyayı görüntüle @
f3dc1732
import
unittest
from
django.utils
import
inspect
class
Person
(
object
):
def
no_arguments
(
self
):
return
None
def
one_argument
(
self
,
something
):
return
something
def
just_args
(
self
,
*
args
):
return
args
def
all_kinds
(
self
,
name
,
address
=
'home'
,
age
=
25
,
*
args
,
**
kwargs
):
return
kwargs
class
TestInspectMethods
(
unittest
.
TestCase
):
def
test_get_func_full_args_no_arguments
(
self
):
self
.
assertEqual
(
inspect
.
get_func_full_args
(
Person
.
no_arguments
),
[])
def
test_get_func_full_args_one_argument
(
self
):
self
.
assertEqual
(
inspect
.
get_func_full_args
(
Person
.
one_argument
),
[(
'something'
,)])
def
test_get_func_full_args_all_arguments
(
self
):
arguments
=
[(
'name'
,),
(
'address'
,
'home'
),
(
'age'
,
25
),
(
'*args'
,),
(
'**kwargs'
,)]
self
.
assertEqual
(
inspect
.
get_func_full_args
(
Person
.
all_kinds
),
arguments
)
def
test_func_accepts_var_args_has_var_args
(
self
):
self
.
assertEqual
(
inspect
.
func_accepts_var_args
(
Person
.
just_args
),
True
)
def
test_func_accepts_var_args_no_var_args
(
self
):
self
.
assertEqual
(
inspect
.
func_accepts_var_args
(
Person
.
one_argument
),
False
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment