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
c979c0a2
Kaydet (Commit)
c979c0a2
authored
Şub 20, 2018
tarafından
Dmitry Dygalo
Kaydeden (comit)
Tim Graham
Nis 04, 2018
Dosyalara gözat
Seçenekler
Dosyalara Gözat
İndir
Eposta Yamaları
Sade Fark
Fixed #25718 -- Made a JSONField lookup value of None match keys that have a null value.
üst
4fe5d846
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
70 additions
and
5 deletions
+70
-5
jsonb.py
django/contrib/postgres/fields/jsonb.py
+1
-0
lookups.py
django/contrib/postgres/lookups.py
+10
-0
lookups.py
django/db/models/lookups.py
+1
-0
query.py
django/db/models/sql/query.py
+3
-3
fields.txt
docs/ref/contrib/postgres/fields.txt
+18
-1
2.1.txt
docs/releases/2.1.txt
+4
-0
models.py
tests/lookup/models.py
+7
-0
tests.py
tests/lookup/tests.py
+11
-1
test_json.py
tests/postgres_tests/test_json.py
+15
-0
No files found.
django/contrib/postgres/fields/jsonb.py
Dosyayı görüntüle @
c979c0a2
...
@@ -88,6 +88,7 @@ JSONField.register_lookup(lookups.ContainedBy)
...
@@ -88,6 +88,7 @@ JSONField.register_lookup(lookups.ContainedBy)
JSONField
.
register_lookup
(
lookups
.
HasKey
)
JSONField
.
register_lookup
(
lookups
.
HasKey
)
JSONField
.
register_lookup
(
lookups
.
HasKeys
)
JSONField
.
register_lookup
(
lookups
.
HasKeys
)
JSONField
.
register_lookup
(
lookups
.
HasAnyKeys
)
JSONField
.
register_lookup
(
lookups
.
HasAnyKeys
)
JSONField
.
register_lookup
(
lookups
.
JSONExact
)
class
KeyTransform
(
Transform
):
class
KeyTransform
(
Transform
):
...
...
django/contrib/postgres/lookups.py
Dosyayı görüntüle @
c979c0a2
from
django.db.models
import
Lookup
,
Transform
from
django.db.models
import
Lookup
,
Transform
from
django.db.models.lookups
import
Exact
from
.search
import
SearchVector
,
SearchVectorExact
,
SearchVectorField
from
.search
import
SearchVector
,
SearchVectorExact
,
SearchVectorField
...
@@ -64,3 +65,12 @@ class SearchLookup(SearchVectorExact):
...
@@ -64,3 +65,12 @@ class SearchLookup(SearchVectorExact):
class
TrigramSimilar
(
PostgresSimpleLookup
):
class
TrigramSimilar
(
PostgresSimpleLookup
):
lookup_name
=
'trigram_similar'
lookup_name
=
'trigram_similar'
operator
=
'
%%
'
operator
=
'
%%
'
class
JSONExact
(
Exact
):
can_use_none_as_rhs
=
True
def
process_rhs
(
self
,
compiler
,
connection
):
result
=
super
()
.
process_rhs
(
compiler
,
connection
)
# Treat None lookup values as null.
return
(
"'null'"
,
[])
if
result
==
(
'
%
s'
,
[
None
])
else
result
django/db/models/lookups.py
Dosyayı görüntüle @
c979c0a2
...
@@ -12,6 +12,7 @@ from django.utils.functional import cached_property
...
@@ -12,6 +12,7 @@ from django.utils.functional import cached_property
class
Lookup
:
class
Lookup
:
lookup_name
=
None
lookup_name
=
None
prepare_rhs
=
True
prepare_rhs
=
True
can_use_none_as_rhs
=
False
def
__init__
(
self
,
lhs
,
rhs
):
def
__init__
(
self
,
lhs
,
rhs
):
self
.
lhs
,
self
.
rhs
=
lhs
,
rhs
self
.
lhs
,
self
.
rhs
=
lhs
,
rhs
...
...
django/db/models/sql/query.py
Dosyayı görüntüle @
c979c0a2
...
@@ -1083,8 +1083,8 @@ class Query:
...
@@ -1083,8 +1083,8 @@ class Query:
lookup
=
lookup_class
(
lhs
,
rhs
)
lookup
=
lookup_class
(
lhs
,
rhs
)
# Interpret '__exact=None' as the sql 'is NULL'; otherwise, reject all
# Interpret '__exact=None' as the sql 'is NULL'; otherwise, reject all
# uses of None as a query value.
# uses of None as a query value
unless the lookup supports it
.
if
lookup
.
rhs
is
None
:
if
lookup
.
rhs
is
None
and
not
lookup
.
can_use_none_as_rhs
:
if
lookup_name
not
in
(
'exact'
,
'iexact'
):
if
lookup_name
not
in
(
'exact'
,
'iexact'
):
raise
ValueError
(
"Cannot use None as a query value"
)
raise
ValueError
(
"Cannot use None as a query value"
)
return
lhs
.
get_lookup
(
'isnull'
)(
lhs
,
True
)
return
lhs
.
get_lookup
(
'isnull'
)(
lhs
,
True
)
...
@@ -1215,7 +1215,7 @@ class Query:
...
@@ -1215,7 +1215,7 @@ class Query:
clause
.
add
(
condition
,
AND
)
clause
.
add
(
condition
,
AND
)
require_outer
=
lookup_type
==
'isnull'
and
condition
.
rhs
is
True
and
not
current_negated
require_outer
=
lookup_type
==
'isnull'
and
condition
.
rhs
is
True
and
not
current_negated
if
current_negated
and
(
lookup_type
!=
'isnull'
or
condition
.
rhs
is
False
):
if
current_negated
and
(
lookup_type
!=
'isnull'
or
condition
.
rhs
is
False
)
and
condition
.
rhs
is
not
None
:
require_outer
=
True
require_outer
=
True
if
(
lookup_type
!=
'isnull'
and
(
if
(
lookup_type
!=
'isnull'
and
(
self
.
is_nullable
(
targets
[
0
])
or
self
.
is_nullable
(
targets
[
0
])
or
...
...
docs/ref/contrib/postgres/fields.txt
Dosyayı görüntüle @
c979c0a2
...
@@ -544,7 +544,7 @@ name::
...
@@ -544,7 +544,7 @@ name::
... }],
... }],
... },
... },
... })
... })
>>> Dog.objects.create(name='Meg', data={'breed': 'collie'})
>>> Dog.objects.create(name='Meg', data={'breed': 'collie'
, 'owner': None
})
>>> Dog.objects.filter(data__breed='collie')
>>> Dog.objects.filter(data__breed='collie')
<QuerySet [<Dog: Meg>]>
<QuerySet [<Dog: Meg>]>
...
@@ -566,6 +566,23 @@ the :lookup:`jsonfield.contains` lookup instead.
...
@@ -566,6 +566,23 @@ the :lookup:`jsonfield.contains` lookup instead.
If only one key or index is used, the SQL operator ``->`` is used. If multiple
If only one key or index is used, the SQL operator ``->`` is used. If multiple
operators are used then the ``#>`` operator is used.
operators are used then the ``#>`` operator is used.
To query for ``null`` in JSON data, use ``None`` as a value::
>>> Dog.objects.filter(data__owner=None)
<QuerySet [<Dog: Meg>]>
To query for missing keys, use the ``isnull`` lookup::
>>> Dog.objects.create(name='Shep', data={'breed': 'collie'})
>>> Dog.objects.filter(data__owner__isnull=True)
<QuerySet [<Dog: Shep>]>
.. versionchanged:: 2.1
In older versions, using ``None`` as a lookup value matches objects that
don't have the key rather than objects that have the key with a ``None``
value.
.. warning::
.. warning::
Since any string could be a key in a JSON object, any lookup other than
Since any string could be a key in a JSON object, any lookup other than
...
...
docs/releases/2.1.txt
Dosyayı görüntüle @
c979c0a2
...
@@ -372,6 +372,10 @@ Miscellaneous
...
@@ -372,6 +372,10 @@ Miscellaneous
* Since migrations are now loaded from ``.pyc`` files, you might need to delete
* Since migrations are now loaded from ``.pyc`` files, you might need to delete
them if you're working in a mixed Python 2 and Python 3 environment.
them if you're working in a mixed Python 2 and Python 3 environment.
* Using ``None`` as a :class:`~django.contrib.postgres.fields.JSONField` lookup
value now matches objects that have the specified key and a null value rather
than objects that don't have the key.
.. _deprecated-features-2.1:
.. _deprecated-features-2.1:
Features deprecated in 2.1
Features deprecated in 2.1
...
...
tests/lookup/models.py
Dosyayı görüntüle @
c979c0a2
...
@@ -5,6 +5,7 @@ This demonstrates features of the database API.
...
@@ -5,6 +5,7 @@ This demonstrates features of the database API.
"""
"""
from
django.db
import
models
from
django.db
import
models
from
django.db.models.lookups
import
IsNull
class
Alarm
(
models
.
Model
):
class
Alarm
(
models
.
Model
):
...
@@ -55,6 +56,12 @@ class NulledTransform(models.Transform):
...
@@ -55,6 +56,12 @@ class NulledTransform(models.Transform):
template
=
'NULL'
template
=
'NULL'
@NulledTextField.register_lookup
class
IsNullWithNoneAsRHS
(
IsNull
):
lookup_name
=
'isnull_none_rhs'
can_use_none_as_rhs
=
True
class
Season
(
models
.
Model
):
class
Season
(
models
.
Model
):
year
=
models
.
PositiveSmallIntegerField
()
year
=
models
.
PositiveSmallIntegerField
()
gt
=
models
.
IntegerField
(
null
=
True
,
blank
=
True
)
gt
=
models
.
IntegerField
(
null
=
True
,
blank
=
True
)
...
...
tests/lookup/tests.py
Dosyayı görüntüle @
c979c0a2
...
@@ -8,7 +8,9 @@ from django.db import connection
...
@@ -8,7 +8,9 @@ from django.db import connection
from
django.db.models.functions
import
Substr
from
django.db.models.functions
import
Substr
from
django.test
import
TestCase
,
skipUnlessDBFeature
from
django.test
import
TestCase
,
skipUnlessDBFeature
from
.models
import
Article
,
Author
,
Game
,
Player
,
Season
,
Tag
from
.models
import
(
Article
,
Author
,
Game
,
IsNullWithNoneAsRHS
,
Player
,
Season
,
Tag
,
)
class
LookupTests
(
TestCase
):
class
LookupTests
(
TestCase
):
...
@@ -895,3 +897,11 @@ class LookupTests(TestCase):
...
@@ -895,3 +897,11 @@ class LookupTests(TestCase):
with
self
.
subTest
(
lookup
=
lookup
):
with
self
.
subTest
(
lookup
=
lookup
):
authors
=
Author
.
objects
.
filter
(
**
{
'name__
%
s'
%
lookup
:
Substr
(
'alias'
,
1
,
3
)})
authors
=
Author
.
objects
.
filter
(
**
{
'name__
%
s'
%
lookup
:
Substr
(
'alias'
,
1
,
3
)})
self
.
assertCountEqual
(
authors
,
result
)
self
.
assertCountEqual
(
authors
,
result
)
def
test_custom_lookup_none_rhs
(
self
):
"""Lookup.can_use_none_as_rhs=True allows None as a lookup value."""
season
=
Season
.
objects
.
create
(
year
=
2012
,
nulled_text_field
=
None
)
query
=
Season
.
objects
.
get_queryset
()
.
query
field
=
query
.
model
.
_meta
.
get_field
(
'nulled_text_field'
)
self
.
assertIsInstance
(
query
.
build_lookup
([
'isnull_none_rhs'
],
field
,
None
),
IsNullWithNoneAsRHS
)
self
.
assertTrue
(
Season
.
objects
.
filter
(
pk
=
season
.
pk
,
nulled_text_field__isnull_none_rhs
=
True
))
tests/postgres_tests/test_json.py
Dosyayı görüntüle @
c979c0a2
...
@@ -4,6 +4,7 @@ from decimal import Decimal
...
@@ -4,6 +4,7 @@ from decimal import Decimal
from
django.core
import
checks
,
exceptions
,
serializers
from
django.core
import
checks
,
exceptions
,
serializers
from
django.core.serializers.json
import
DjangoJSONEncoder
from
django.core.serializers.json
import
DjangoJSONEncoder
from
django.db.models
import
Q
from
django.forms
import
CharField
,
Form
,
widgets
from
django.forms
import
CharField
,
Form
,
widgets
from
django.test.utils
import
isolate_apps
from
django.test.utils
import
isolate_apps
from
django.utils.html
import
escape
from
django.utils.html
import
escape
...
@@ -177,6 +178,20 @@ class TestQuerying(PostgreSQLTestCase):
...
@@ -177,6 +178,20 @@ class TestQuerying(PostgreSQLTestCase):
[
self
.
objs
[
7
],
self
.
objs
[
8
]]
[
self
.
objs
[
7
],
self
.
objs
[
8
]]
)
)
def
test_none_key
(
self
):
self
.
assertSequenceEqual
(
JSONModel
.
objects
.
filter
(
field__j
=
None
),
[
self
.
objs
[
8
]])
def
test_none_key_exclude
(
self
):
obj
=
JSONModel
.
objects
.
create
(
field
=
{
'j'
:
1
})
self
.
assertSequenceEqual
(
JSONModel
.
objects
.
exclude
(
field__j
=
None
),
[
obj
])
def
test_isnull_key_or_none
(
self
):
obj
=
JSONModel
.
objects
.
create
(
field
=
{
'a'
:
None
})
self
.
assertSequenceEqual
(
JSONModel
.
objects
.
filter
(
Q
(
field__a__isnull
=
True
)
|
Q
(
field__a
=
None
)),
self
.
objs
[:
7
]
+
self
.
objs
[
9
:]
+
[
obj
]
)
def
test_contains
(
self
):
def
test_contains
(
self
):
self
.
assertSequenceEqual
(
self
.
assertSequenceEqual
(
JSONModel
.
objects
.
filter
(
field__contains
=
{
'a'
:
'b'
}),
JSONModel
.
objects
.
filter
(
field__contains
=
{
'a'
:
'b'
}),
...
...
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