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
779bb82f
Kaydet (Commit)
779bb82f
authored
Mar 30, 2016
tarafından
Alex Hill
Kaydeden (comit)
Tim Graham
May 20, 2016
Dosyalara gözat
Seçenekler
Dosyalara Gözat
İndir
Eposta Yamaları
Sade Fark
Fixed #26421 -- Refactored ModelSignal to use Apps.lazy_model_operation()
üst
2ff7ef15
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
86 additions
and
120 deletions
+86
-120
model_checks.py
django/core/checks/model_checks.py
+30
-34
signals.py
django/db/models/signals.py
+11
-39
checks.txt
docs/ref/checks.txt
+2
-1
tests.py
tests/model_validation/tests.py
+39
-42
tests.py
tests/signals/tests.py
+4
-4
No files found.
django/core/checks/model_checks.py
Dosyayı görüntüle @
779bb82f
...
...
@@ -31,40 +31,6 @@ def check_all_models(app_configs=None, **kwargs):
return
errors
@register
(
Tags
.
models
,
Tags
.
signals
)
def
check_model_signals
(
app_configs
=
None
,
**
kwargs
):
"""
Ensure lazily referenced model signals senders are installed.
"""
# Avoid circular import
from
django.db
import
models
errors
=
[]
for
name
in
dir
(
models
.
signals
):
obj
=
getattr
(
models
.
signals
,
name
)
if
isinstance
(
obj
,
models
.
signals
.
ModelSignal
):
for
reference
,
receivers
in
obj
.
unresolved_references
.
items
():
for
receiver
,
_
,
_
in
receivers
:
# The receiver is either a function or an instance of class
# defining a `__call__` method.
if
isinstance
(
receiver
,
types
.
FunctionType
):
description
=
"The '
%
s' function"
%
receiver
.
__name__
else
:
description
=
"An instance of the '
%
s' class"
%
receiver
.
__class__
.
__name__
errors
.
append
(
Error
(
"
%
s was connected to the '
%
s' signal "
"with a lazy reference to the '
%
s' sender, "
"which has not been installed."
%
(
description
,
name
,
'.'
.
join
(
reference
)
),
obj
=
receiver
.
__module__
,
id
=
'signals.E001'
)
)
return
errors
def
_check_lazy_references
(
apps
,
ignore
=
None
):
"""
Ensure all lazy (i.e. string) model references have been resolved.
...
...
@@ -82,6 +48,12 @@ def _check_lazy_references(apps, ignore=None):
if
not
pending_models
:
return
[]
from
django.db.models
import
signals
model_signals
=
{
signal
:
name
for
name
,
signal
in
vars
(
signals
)
.
items
()
if
isinstance
(
signal
,
signals
.
ModelSignal
)
}
def
extract_operation
(
obj
):
"""
Take a callable found in Apps._pending_operations and identify the
...
...
@@ -127,6 +99,29 @@ def _check_lazy_references(apps, ignore=None):
}
return
Error
(
error_msg
%
params
,
obj
=
keywords
[
'field'
],
id
=
'fields.E307'
)
def
signal_connect_error
(
model_key
,
func
,
args
,
keywords
):
error_msg
=
(
"
%(receiver)
s was connected to the '
%(signal)
s' signal with a "
"lazy reference to the sender '
%(model)
s', but
%(model_error)
s."
)
receiver
=
args
[
0
]
# The receiver is either a function or an instance of class
# defining a `__call__` method.
if
isinstance
(
receiver
,
types
.
FunctionType
):
description
=
"The function '
%
s'"
%
receiver
.
__name__
elif
isinstance
(
receiver
,
types
.
MethodType
):
description
=
"Bound method '
%
s.
%
s'"
%
(
receiver
.
__self__
.
__class__
.
__name__
,
receiver
.
__name__
)
else
:
description
=
"An instance of class '
%
s'"
%
receiver
.
__class__
.
__name__
signal_name
=
model_signals
.
get
(
func
.
__self__
,
'unknown'
)
params
=
{
'model'
:
'.'
.
join
(
model_key
),
'receiver'
:
description
,
'signal'
:
signal_name
,
'model_error'
:
app_model_error
(
model_key
),
}
return
Error
(
error_msg
%
params
,
obj
=
receiver
.
__module__
,
id
=
'signals.E001'
)
def
default_error
(
model_key
,
func
,
args
,
keywords
):
error_msg
=
"
%(op)
s contains a lazy reference to
%(model)
s, but
%(model_error)
s."
params
=
{
...
...
@@ -142,6 +137,7 @@ def _check_lazy_references(apps, ignore=None):
known_lazy
=
{
(
'django.db.models.fields.related'
,
'resolve_related_class'
):
field_error
,
(
'django.db.models.fields.related'
,
'set_managed'
):
None
,
(
'django.dispatch.dispatcher'
,
'connect'
):
signal_connect_error
,
}
def
build_error
(
model_key
,
func
,
args
,
keywords
):
...
...
django/db/models/signals.py
Dosyayı görüntüle @
779bb82f
from
django.apps
import
apps
from
functools
import
partial
from
django.db.models.utils
import
make_model_tuple
from
django.dispatch
import
Signal
from
django.utils
import
six
class_prepared
=
Signal
(
providing_args
=
[
"class"
])
...
...
@@ -11,44 +12,15 @@ class ModelSignal(Signal):
Signal subclass that allows the sender to be lazily specified as a string
of the `app_label.ModelName` form.
"""
def
connect
(
self
,
receiver
,
sender
=
None
,
weak
=
True
,
dispatch_uid
=
None
,
apps
=
None
):
# Takes a single optional argument named "sender"
connect
=
partial
(
super
(
ModelSignal
,
self
)
.
connect
,
receiver
,
weak
=
weak
,
dispatch_uid
=
dispatch_uid
)
models
=
[
make_model_tuple
(
sender
)]
if
sender
else
[]
if
not
apps
:
from
django.db.models.base
import
Options
apps
=
sender
.
_meta
.
apps
if
hasattr
(
sender
,
'_meta'
)
else
Options
.
default_apps
apps
.
lazy_model_operation
(
connect
,
*
models
)
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
ModelSignal
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
unresolved_references
=
{}
class_prepared
.
connect
(
self
.
_resolve_references
)
def
_resolve_references
(
self
,
sender
,
**
kwargs
):
opts
=
sender
.
_meta
reference
=
(
opts
.
app_label
,
opts
.
object_name
)
try
:
receivers
=
self
.
unresolved_references
.
pop
(
reference
)
except
KeyError
:
pass
else
:
for
receiver
,
weak
,
dispatch_uid
in
receivers
:
super
(
ModelSignal
,
self
)
.
connect
(
receiver
,
sender
=
sender
,
weak
=
weak
,
dispatch_uid
=
dispatch_uid
)
def
connect
(
self
,
receiver
,
sender
=
None
,
weak
=
True
,
dispatch_uid
=
None
):
if
isinstance
(
sender
,
six
.
string_types
):
try
:
app_label
,
model_name
=
sender
.
split
(
'.'
)
except
ValueError
:
raise
ValueError
(
"Specified sender must either be a model or a "
"model name of the 'app_label.ModelName' form."
)
try
:
sender
=
apps
.
get_registered_model
(
app_label
,
model_name
)
except
LookupError
:
ref
=
(
app_label
,
model_name
)
refs
=
self
.
unresolved_references
.
setdefault
(
ref
,
[])
refs
.
append
((
receiver
,
weak
,
dispatch_uid
))
return
super
(
ModelSignal
,
self
)
.
connect
(
receiver
,
sender
=
sender
,
weak
=
weak
,
dispatch_uid
=
dispatch_uid
)
pre_init
=
ModelSignal
(
providing_args
=
[
"instance"
,
"args"
,
"kwargs"
],
use_caching
=
True
)
post_init
=
ModelSignal
(
providing_args
=
[
"instance"
],
use_caching
=
True
)
...
...
docs/ref/checks.txt
Dosyayı görüntüle @
779bb82f
...
...
@@ -248,7 +248,8 @@ Signals
~~~~~~~
* **signals.E001**: ``<handler>`` was connected to the ``<signal>`` signal with
a lazy reference to the ``<model>`` sender, which has not been installed.
a lazy reference to the sender ``<app label>.<model>``, but app ``<app label>``
isn't installed or doesn't provide model ``<model>``.
Backwards Compatibility
~~~~~~~~~~~~~~~~~~~~~~~
...
...
tests/model_validation/tests.py
Dosyayı görüntüle @
779bb82f
from
django.core
import
management
from
django.core.checks
import
Error
,
run_checks
from
django.core.checks
import
Error
from
django.core.checks.model_checks
import
_check_lazy_references
from
django.db
import
models
from
django.db.models.signals
import
post_init
...
...
@@ -8,19 +8,6 @@ from django.test.utils import isolate_apps, override_settings
from
django.utils
import
six
class
OnPostInit
(
object
):
def
__call__
(
self
,
**
kwargs
):
pass
def
on_post_init
(
**
kwargs
):
pass
def
dummy_function
(
model
):
pass
@override_settings
(
INSTALLED_APPS
=
[
'django.contrib.auth'
,
'django.contrib.contenttypes'
],
SILENCED_SYSTEM_CHECKS
=
[
'fields.W342'
],
# ForeignKey(unique=True)
...
...
@@ -35,32 +22,6 @@ class ModelValidationTest(SimpleTestCase):
# See: https://code.djangoproject.com/ticket/21375
management
.
call_command
(
"check"
,
stdout
=
six
.
StringIO
())
def
test_model_signal
(
self
):
unresolved_references
=
post_init
.
unresolved_references
.
copy
()
post_init
.
connect
(
on_post_init
,
sender
=
'missing-app.Model'
)
post_init
.
connect
(
OnPostInit
(),
sender
=
'missing-app.Model'
)
errors
=
run_checks
()
expected
=
[
Error
(
"The 'on_post_init' function was connected to the 'post_init' "
"signal with a lazy reference to the 'missing-app.Model' "
"sender, which has not been installed."
,
obj
=
'model_validation.tests'
,
id
=
'signals.E001'
,
),
Error
(
"An instance of the 'OnPostInit' class was connected to "
"the 'post_init' signal with a lazy reference to the "
"'missing-app.Model' sender, which has not been installed."
,
obj
=
'model_validation.tests'
,
id
=
'signals.E001'
,
)
]
self
.
assertEqual
(
errors
,
expected
)
post_init
.
unresolved_references
=
unresolved_references
@isolate_apps
(
'django.contrib.auth'
,
kwarg_name
=
'apps'
)
def
test_lazy_reference_checks
(
self
,
apps
):
...
...
@@ -70,11 +31,24 @@ class ModelValidationTest(SimpleTestCase):
class
Meta
:
app_label
=
"model_validation"
class
DummyClass
(
object
):
def
__call__
(
self
,
**
kwargs
):
pass
def
dummy_method
(
self
):
pass
def
dummy_function
(
*
args
,
**
kwargs
):
pass
apps
.
lazy_model_operation
(
dummy_function
,
(
'auth'
,
'imaginarymodel'
))
apps
.
lazy_model_operation
(
dummy_function
,
(
'fanciful_app'
,
'imaginarymodel'
))
errors
=
_check_lazy_references
(
apps
)
post_init
.
connect
(
dummy_function
,
sender
=
'missing-app.Model'
,
apps
=
apps
)
post_init
.
connect
(
DummyClass
(),
sender
=
'missing-app.Model'
,
apps
=
apps
)
post_init
.
connect
(
DummyClass
()
.
dummy_method
,
sender
=
'missing-app.Model'
,
apps
=
apps
)
errors
=
_check_lazy_references
(
apps
)
expected
=
[
Error
(
"
%
r contains a lazy reference to auth.imaginarymodel, "
...
...
@@ -88,6 +62,22 @@ class ModelValidationTest(SimpleTestCase):
obj
=
dummy_function
,
id
=
'models.E022'
,
),
Error
(
"An instance of class 'DummyClass' was connected to "
"the 'post_init' signal with a lazy reference to the sender "
"'missing-app.model', but app 'missing-app' isn't installed."
,
hint
=
None
,
obj
=
'model_validation.tests'
,
id
=
'signals.E001'
,
),
Error
(
"Bound method 'DummyClass.dummy_method' was connected to the "
"'post_init' signal with a lazy reference to the sender "
"'missing-app.model', but app 'missing-app' isn't installed."
,
hint
=
None
,
obj
=
'model_validation.tests'
,
id
=
'signals.E001'
,
),
Error
(
"The field model_validation.DummyModel.author was declared "
"with a lazy reference to 'model_validation.author', but app "
...
...
@@ -96,6 +86,13 @@ class ModelValidationTest(SimpleTestCase):
obj
=
DummyModel
.
author
.
field
,
id
=
'fields.E307'
,
),
Error
(
"The function 'dummy_function' was connected to the 'post_init' "
"signal with a lazy reference to the sender "
"'missing-app.model', but app 'missing-app' isn't installed."
,
hint
=
None
,
obj
=
'model_validation.tests'
,
id
=
'signals.E001'
,
),
]
self
.
assertEqual
(
errors
,
expected
)
tests/signals/tests.py
Dosyayı görüntüle @
779bb82f
...
...
@@ -267,7 +267,7 @@ class LazyModelRefTest(BaseSignalTest):
self
.
received
.
append
(
kwargs
)
def
test_invalid_sender_model_name
(
self
):
msg
=
"
Specified sender must either be a model or a model name of the 'app_label.ModelName' form
."
msg
=
"
Invalid model reference 'invalid'. String model references must be of the form 'app_label.ModelName'
."
with
self
.
assertRaisesMessage
(
ValueError
,
msg
):
signals
.
post_init
.
connect
(
self
.
receiver
,
sender
=
'invalid'
)
...
...
@@ -285,10 +285,10 @@ class LazyModelRefTest(BaseSignalTest):
finally
:
signals
.
post_init
.
disconnect
(
self
.
receiver
,
sender
=
Book
)
@isolate_apps
(
'signals'
)
def
test_not_loaded_model
(
self
):
@isolate_apps
(
'signals'
,
kwarg_name
=
'apps'
)
def
test_not_loaded_model
(
self
,
apps
):
signals
.
post_init
.
connect
(
self
.
receiver
,
sender
=
'signals.Created'
,
weak
=
False
self
.
receiver
,
sender
=
'signals.Created'
,
weak
=
False
,
apps
=
apps
)
try
:
...
...
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