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
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
197 additions
and
10 deletions
+197
-10
model_detail.html
...o/contrib/admindocs/templates/admin_doc/model_detail.html
+25
-0
views.py
django/contrib/admindocs/views.py
+27
-4
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 @@
...
@@ -27,6 +27,7 @@
{{ description }}
{{ description }}
<h3>
{% trans 'Fields' %}
</h3>
<div
class=
"module"
>
<div
class=
"module"
>
<table
class=
"model"
>
<table
class=
"model"
>
<thead>
<thead>
...
@@ -48,6 +49,30 @@
...
@@ -48,6 +49,30 @@
</table>
</table>
</div>
</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>
<p
class=
"small"
><a
href=
"{% url 'django-admindocs-models-index' %}"
>
‹
{% trans 'Back to Model Documentation' %}
</a></p>
</div>
</div>
{% endblock %}
{% endblock %}
django/contrib/admindocs/views.py
Dosyayı görüntüle @
f3dc1732
...
@@ -14,7 +14,10 @@ from django.db import models
...
@@ -14,7 +14,10 @@ from django.db import models
from
django.http
import
Http404
from
django.http
import
Http404
from
django.template.engine
import
Engine
from
django.template.engine
import
Engine
from
django.utils.decorators
import
method_decorator
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.utils.translation
import
ugettext
as
_
from
django.views.generic
import
TemplateView
from
django.views.generic
import
TemplateView
...
@@ -219,7 +222,7 @@ class ModelDetailView(BaseAdminDocsView):
...
@@ -219,7 +222,7 @@ class ModelDetailView(BaseAdminDocsView):
fields
.
append
({
fields
.
append
({
'name'
:
field
.
name
,
'name'
:
field
.
name
,
'data_type'
:
data_type
,
'data_type'
:
data_type
,
'verbose'
:
verbose
,
'verbose'
:
verbose
or
''
,
'help_text'
:
field
.
help_text
,
'help_text'
:
field
.
help_text
,
})
})
...
@@ -242,9 +245,10 @@ class ModelDetailView(BaseAdminDocsView):
...
@@ -242,9 +245,10 @@ class ModelDetailView(BaseAdminDocsView):
'verbose'
:
utils
.
parse_rst
(
_
(
"number of
%
s"
)
%
verbose
,
'model'
,
_
(
'model:'
)
+
opts
.
model_name
),
'verbose'
:
utils
.
parse_rst
(
_
(
"number of
%
s"
)
%
verbose
,
'model'
,
_
(
'model:'
)
+
opts
.
model_name
),
})
})
methods
=
[]
# Gather model methods.
# Gather model methods.
for
func_name
,
func
in
model
.
__dict__
.
items
():
for
func_name
,
func
in
model
.
__dict__
.
items
():
if
inspect
.
isfunction
(
func
)
and
func_has_no_args
(
func
)
:
if
inspect
.
isfunction
(
func
):
try
:
try
:
for
exclude
in
MODEL_METHODS_EXCLUDE
:
for
exclude
in
MODEL_METHODS_EXCLUDE
:
if
func_name
.
startswith
(
exclude
):
if
func_name
.
startswith
(
exclude
):
...
@@ -254,10 +258,28 @@ class ModelDetailView(BaseAdminDocsView):
...
@@ -254,10 +258,28 @@ class ModelDetailView(BaseAdminDocsView):
verbose
=
func
.
__doc__
verbose
=
func
.
__doc__
if
verbose
:
if
verbose
:
verbose
=
utils
.
parse_rst
(
utils
.
trim_docstring
(
verbose
),
'model'
,
_
(
'model:'
)
+
opts
.
model_name
)
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
({
fields
.
append
({
'name'
:
func_name
,
'name'
:
func_name
,
'data_type'
:
get_return_data_type
(
func_name
),
'data_type'
:
get_return_data_type
(
func_name
),
'verbose'
:
verbose
,
'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
# Gather related objects
...
@@ -282,6 +304,7 @@ class ModelDetailView(BaseAdminDocsView):
...
@@ -282,6 +304,7 @@ class ModelDetailView(BaseAdminDocsView):
'summary'
:
title
,
'summary'
:
title
,
'description'
:
body
,
'description'
:
body
,
'fields'
:
fields
,
'fields'
:
fields
,
'methods'
:
methods
,
})
})
return
super
(
ModelDetailView
,
self
)
.
get_context_data
(
**
kwargs
)
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):
...
@@ -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
):
def
func_accepts_kwargs
(
func
):
if
six
.
PY2
:
if
six
.
PY2
:
# Not all callables are inspectable with getargspec, so we'll
# Not all callables are inspectable with getargspec, so we'll
...
@@ -64,10 +102,23 @@ def func_accepts_kwargs(func):
...
@@ -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
):
def
func_has_no_args
(
func
):
args
=
inspect
.
getargspec
(
func
)[
0
]
if
six
.
PY2
else
[
args
=
inspect
.
getargspec
(
func
)[
0
]
if
six
.
PY2
else
[
p
for
p
in
inspect
.
signature
(
func
)
.
parameters
.
values
()
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
return
len
(
args
)
==
1
...
...
docs/ref/contrib/admin/admindocs.txt
Dosyayı görüntüle @
f3dc1732
...
@@ -60,10 +60,15 @@ Model reference
...
@@ -60,10 +60,15 @@ Model reference
===============
===============
The **models** section of the ``admindocs`` page describes each model in the
The **models** section of the ``admindocs`` page describes each model in the
system along with all the fields and methods (without any arguments) available
system along with all the fields and methods available on it. Relationships
on it. While model properties don't have any arguments, they are not listed.
to other models appear as hyperlinks. Descriptions are pulled from ``help_text``
Relationships to other models appear as hyperlinks. Descriptions are pulled
attributes on fields or from docstrings on model methods.
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::
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
...
@@ -163,6 +163,12 @@ Minor features
* JavaScript slug generation now supports Romanian characters.
* 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`
:mod:`django.contrib.auth`
^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^
...
...
tests/admin_docs/models.py
Dosyayı görüntüle @
f3dc1732
...
@@ -44,6 +44,17 @@ class Person(models.Model):
...
@@ -44,6 +44,17 @@ class Person(models.Model):
def
_get_full_name
(
self
):
def
_get_full_name
(
self
):
return
"
%
s
%
s"
%
(
self
.
first_name
,
self
.
last_name
)
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
):
def
add_image
(
self
):
pass
pass
...
...
tests/admin_docs/tests.py
Dosyayı görüntüle @
f3dc1732
...
@@ -246,7 +246,7 @@ class TestModelDetailView(TestDataMixin, AdminDocsTestCase):
...
@@ -246,7 +246,7 @@ class TestModelDetailView(TestDataMixin, AdminDocsTestCase):
def
setUp
(
self
):
def
setUp
(
self
):
self
.
client
.
login
(
username
=
'super'
,
password
=
'secret'
)
self
.
client
.
login
(
username
=
'super'
,
password
=
'secret'
)
with
captured_stderr
()
as
self
.
docutils_stderr
:
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
):
def
test_method_excludes
(
self
):
"""
"""
...
@@ -261,6 +261,34 @@ class TestModelDetailView(TestDataMixin, AdminDocsTestCase):
...
@@ -261,6 +261,34 @@ class TestModelDetailView(TestDataMixin, AdminDocsTestCase):
self
.
assertNotContains
(
self
.
response
,
"<td>set_status</td>"
)
self
.
assertNotContains
(
self
.
response
,
"<td>set_status</td>"
)
self
.
assertNotContains
(
self
.
response
,
"<td>save_changes</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
):
def
test_method_data_types
(
self
):
"""
"""
We should be able to get a basic idea of the type returned
We should be able to get a basic idea of the type returned
...
@@ -368,6 +396,9 @@ class TestModelDetailView(TestDataMixin, AdminDocsTestCase):
...
@@ -368,6 +396,9 @@ class TestModelDetailView(TestDataMixin, AdminDocsTestCase):
self
.
assertContains
(
self
.
response
,
body
,
html
=
True
)
self
.
assertContains
(
self
.
response
,
body
,
html
=
True
)
self
.
assertContains
(
self
.
response
,
model_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."
)
@unittest.skipUnless
(
utils
.
docutils_is_available
,
"no docutils installed."
)
class
TestUtils
(
AdminDocsTestCase
):
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