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
134ca4d4
Kaydet (Commit)
134ca4d4
authored
Agu 03, 2015
tarafından
Alex Hill
Kaydeden (comit)
Josh Smeaton
Eyl 22, 2015
Dosyalara gözat
Seçenekler
Dosyalara Gözat
İndir
Eposta Yamaları
Sade Fark
Fixed #24509 -- Added Expression support to SQLInsertCompiler
üst
6e51d5d0
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
247 additions
and
67 deletions
+247
-67
operations.py
django/contrib/gis/db/backends/oracle/operations.py
+5
-6
operations.py
django/db/backends/base/operations.py
+1
-1
operations.py
django/db/backends/mysql/operations.py
+4
-3
operations.py
django/db/backends/oracle/operations.py
+5
-3
operations.py
django/db/backends/postgresql/operations.py
+4
-3
operations.py
django/db/backends/sqlite3/operations.py
+5
-7
expressions.py
django/db/models/expressions.py
+21
-0
query.py
django/db/models/query.py
+2
-0
compiler.py
django/db/models/sql/compiler.py
+112
-33
subqueries.py
django/db/models/sql/subqueries.py
+3
-3
expressions.txt
docs/ref/models/expressions.txt
+22
-6
1.9.txt
docs/releases/1.9.txt
+4
-0
tests.py
tests/bulk_create/tests.py
+11
-0
tests.py
tests/expressions/tests.py
+42
-1
tests.py
tests/model_fields/tests.py
+6
-1
No files found.
django/contrib/gis/db/backends/oracle/operations.py
Dosyayı görüntüle @
134ca4d4
...
@@ -263,11 +263,10 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations):
...
@@ -263,11 +263,10 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations):
from
django.contrib.gis.db.backends.oracle.models
import
OracleSpatialRefSys
from
django.contrib.gis.db.backends.oracle.models
import
OracleSpatialRefSys
return
OracleSpatialRefSys
return
OracleSpatialRefSys
def
modify_insert_params
(
self
,
placeholder
s
,
params
):
def
modify_insert_params
(
self
,
placeholder
,
params
):
"""Drop out insert parameters for NULL placeholder. Needed for Oracle Spatial
"""Drop out insert parameters for NULL placeholder. Needed for Oracle Spatial
backend due to #10888
backend due to #10888
.
"""
"""
# This code doesn't work for bulk insert cases.
if
placeholder
==
'NULL'
:
assert
len
(
placeholders
)
==
1
return
[]
return
[[
param
for
pholder
,
param
return
super
(
OracleOperations
,
self
)
.
modify_insert_params
(
placeholder
,
params
)
in
six
.
moves
.
zip
(
placeholders
[
0
],
params
[
0
])
if
pholder
!=
'NULL'
],
]
django/db/backends/base/operations.py
Dosyayı görüntüle @
134ca4d4
...
@@ -576,7 +576,7 @@ class BaseDatabaseOperations(object):
...
@@ -576,7 +576,7 @@ class BaseDatabaseOperations(object):
def
combine_duration_expression
(
self
,
connector
,
sub_expressions
):
def
combine_duration_expression
(
self
,
connector
,
sub_expressions
):
return
self
.
combine_expression
(
connector
,
sub_expressions
)
return
self
.
combine_expression
(
connector
,
sub_expressions
)
def
modify_insert_params
(
self
,
placeholder
s
,
params
):
def
modify_insert_params
(
self
,
placeholder
,
params
):
"""Allow modification of insert parameters. Needed for Oracle Spatial
"""Allow modification of insert parameters. Needed for Oracle Spatial
backend due to #10888.
backend due to #10888.
"""
"""
...
...
django/db/backends/mysql/operations.py
Dosyayı görüntüle @
134ca4d4
...
@@ -166,9 +166,10 @@ class DatabaseOperations(BaseDatabaseOperations):
...
@@ -166,9 +166,10 @@ class DatabaseOperations(BaseDatabaseOperations):
def
max_name_length
(
self
):
def
max_name_length
(
self
):
return
64
return
64
def
bulk_insert_sql
(
self
,
fields
,
num_values
):
def
bulk_insert_sql
(
self
,
fields
,
placeholder_rows
):
items_sql
=
"(
%
s)"
%
", "
.
join
([
"
%
s"
]
*
len
(
fields
))
placeholder_rows_sql
=
(
", "
.
join
(
row
)
for
row
in
placeholder_rows
)
return
"VALUES "
+
", "
.
join
([
items_sql
]
*
num_values
)
values_sql
=
", "
.
join
(
"(
%
s)"
%
sql
for
sql
in
placeholder_rows_sql
)
return
"VALUES "
+
values_sql
def
combine_expression
(
self
,
connector
,
sub_expressions
):
def
combine_expression
(
self
,
connector
,
sub_expressions
):
"""
"""
...
...
django/db/backends/oracle/operations.py
Dosyayı görüntüle @
134ca4d4
...
@@ -439,6 +439,8 @@ WHEN (new.%(col_name)s IS NULL)
...
@@ -439,6 +439,8 @@ WHEN (new.%(col_name)s IS NULL)
name_length
=
self
.
max_name_length
()
-
3
name_length
=
self
.
max_name_length
()
-
3
return
'
%
s_TR'
%
truncate_name
(
table
,
name_length
)
.
upper
()
return
'
%
s_TR'
%
truncate_name
(
table
,
name_length
)
.
upper
()
def
bulk_insert_sql
(
self
,
fields
,
num_values
):
def
bulk_insert_sql
(
self
,
fields
,
placeholder_rows
):
items_sql
=
"SELECT
%
s FROM DUAL"
%
", "
.
join
([
"
%
s"
]
*
len
(
fields
))
return
" UNION ALL "
.
join
(
return
" UNION ALL "
.
join
([
items_sql
]
*
num_values
)
"SELECT
%
s FROM DUAL"
%
", "
.
join
(
row
)
for
row
in
placeholder_rows
)
django/db/backends/postgresql/operations.py
Dosyayı görüntüle @
134ca4d4
...
@@ -221,9 +221,10 @@ class DatabaseOperations(BaseDatabaseOperations):
...
@@ -221,9 +221,10 @@ class DatabaseOperations(BaseDatabaseOperations):
def
return_insert_id
(
self
):
def
return_insert_id
(
self
):
return
"RETURNING
%
s"
,
()
return
"RETURNING
%
s"
,
()
def
bulk_insert_sql
(
self
,
fields
,
num_values
):
def
bulk_insert_sql
(
self
,
fields
,
placeholder_rows
):
items_sql
=
"(
%
s)"
%
", "
.
join
([
"
%
s"
]
*
len
(
fields
))
placeholder_rows_sql
=
(
", "
.
join
(
row
)
for
row
in
placeholder_rows
)
return
"VALUES "
+
", "
.
join
([
items_sql
]
*
num_values
)
values_sql
=
", "
.
join
(
"(
%
s)"
%
sql
for
sql
in
placeholder_rows_sql
)
return
"VALUES "
+
values_sql
def
adapt_datefield_value
(
self
,
value
):
def
adapt_datefield_value
(
self
,
value
):
return
value
return
value
...
...
django/db/backends/sqlite3/operations.py
Dosyayı görüntüle @
134ca4d4
...
@@ -226,13 +226,11 @@ class DatabaseOperations(BaseDatabaseOperations):
...
@@ -226,13 +226,11 @@ class DatabaseOperations(BaseDatabaseOperations):
value
=
uuid
.
UUID
(
value
)
value
=
uuid
.
UUID
(
value
)
return
value
return
value
def
bulk_insert_sql
(
self
,
fields
,
num_values
):
def
bulk_insert_sql
(
self
,
fields
,
placeholder_rows
):
res
=
[]
return
" UNION ALL "
.
join
(
res
.
append
(
"SELECT
%
s"
%
", "
.
join
(
"SELECT
%
s"
%
", "
.
join
(
row
)
"
%%
s AS
%
s"
%
self
.
quote_name
(
f
.
column
)
for
f
in
fields
for
row
in
placeholder_rows
))
)
res
.
extend
([
"UNION ALL SELECT
%
s"
%
", "
.
join
([
"
%
s"
]
*
len
(
fields
))]
*
(
num_values
-
1
))
return
" "
.
join
(
res
)
def
combine_expression
(
self
,
connector
,
sub_expressions
):
def
combine_expression
(
self
,
connector
,
sub_expressions
):
# SQLite doesn't have a power function, so we fake it with a
# SQLite doesn't have a power function, so we fake it with a
...
...
django/db/models/expressions.py
Dosyayı görüntüle @
134ca4d4
...
@@ -180,6 +180,13 @@ class BaseExpression(object):
...
@@ -180,6 +180,13 @@ class BaseExpression(object):
return
True
return
True
return
False
return
False
@cached_property
def
contains_column_references
(
self
):
for
expr
in
self
.
get_source_expressions
():
if
expr
and
expr
.
contains_column_references
:
return
True
return
False
def
resolve_expression
(
self
,
query
=
None
,
allow_joins
=
True
,
reuse
=
None
,
summarize
=
False
,
for_save
=
False
):
def
resolve_expression
(
self
,
query
=
None
,
allow_joins
=
True
,
reuse
=
None
,
summarize
=
False
,
for_save
=
False
):
"""
"""
Provides the chance to do any preprocessing or validation before being
Provides the chance to do any preprocessing or validation before being
...
@@ -339,6 +346,17 @@ class BaseExpression(object):
...
@@ -339,6 +346,17 @@ class BaseExpression(object):
def
reverse_ordering
(
self
):
def
reverse_ordering
(
self
):
return
self
return
self
def
flatten
(
self
):
"""
Recursively yield this expression and all subexpressions, in
depth-first order.
"""
yield
self
for
expr
in
self
.
get_source_expressions
():
if
expr
:
for
inner_expr
in
expr
.
flatten
():
yield
inner_expr
class
Expression
(
BaseExpression
,
Combinable
):
class
Expression
(
BaseExpression
,
Combinable
):
"""
"""
...
@@ -613,6 +631,9 @@ class Random(Expression):
...
@@ -613,6 +631,9 @@ class Random(Expression):
class
Col
(
Expression
):
class
Col
(
Expression
):
contains_column_references
=
True
def
__init__
(
self
,
alias
,
target
,
output_field
=
None
):
def
__init__
(
self
,
alias
,
target
,
output_field
=
None
):
if
output_field
is
None
:
if
output_field
is
None
:
output_field
=
target
output_field
=
target
...
...
django/db/models/query.py
Dosyayı görüntüle @
134ca4d4
...
@@ -458,6 +458,8 @@ class QuerySet(object):
...
@@ -458,6 +458,8 @@ class QuerySet(object):
specifying whether an object was created.
specifying whether an object was created.
"""
"""
lookup
,
params
=
self
.
_extract_model_params
(
defaults
,
**
kwargs
)
lookup
,
params
=
self
.
_extract_model_params
(
defaults
,
**
kwargs
)
# The get() needs to be targeted at the write database in order
# to avoid potential transaction consistency problems.
self
.
_for_write
=
True
self
.
_for_write
=
True
try
:
try
:
return
self
.
get
(
**
lookup
),
False
return
self
.
get
(
**
lookup
),
False
...
...
django/db/models/sql/compiler.py
Dosyayı görüntüle @
134ca4d4
...
@@ -909,17 +909,102 @@ class SQLInsertCompiler(SQLCompiler):
...
@@ -909,17 +909,102 @@ class SQLInsertCompiler(SQLCompiler):
self
.
return_id
=
False
self
.
return_id
=
False
super
(
SQLInsertCompiler
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
super
(
SQLInsertCompiler
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
def
placeholder
(
self
,
field
,
val
):
def
field_as_sql
(
self
,
field
,
val
):
"""
Take a field and a value intended to be saved on that field, and
return placeholder SQL and accompanying params. Checks for raw values,
expressions and fields with get_placeholder() defined in that order.
When field is None, the value is considered raw and is used as the
placeholder, with no corresponding parameters returned.
"""
if
field
is
None
:
if
field
is
None
:
# A field value of None means the value is raw.
# A field value of None means the value is raw.
return
val
sql
,
params
=
val
,
[]
elif
hasattr
(
val
,
'as_sql'
):
# This is an expression, let's compile it.
sql
,
params
=
self
.
compile
(
val
)
elif
hasattr
(
field
,
'get_placeholder'
):
elif
hasattr
(
field
,
'get_placeholder'
):
# Some fields (e.g. geo fields) need special munging before
# Some fields (e.g. geo fields) need special munging before
# they can be inserted.
# they can be inserted.
return
field
.
get_placeholder
(
val
,
self
,
self
.
connection
)
sql
,
params
=
field
.
get_placeholder
(
val
,
self
,
self
.
connection
),
[
val
]
else
:
else
:
# Return the common case for the placeholder
# Return the common case for the placeholder
return
'
%
s'
sql
,
params
=
'
%
s'
,
[
val
]
# The following hook is only used by Oracle Spatial, which sometimes
# needs to yield 'NULL' and [] as its placeholder and params instead
# of '%s' and [None]. The 'NULL' placeholder is produced earlier by
# OracleOperations.get_geom_placeholder(). The following line removes
# the corresponding None parameter. See ticket #10888.
params
=
self
.
connection
.
ops
.
modify_insert_params
(
sql
,
params
)
return
sql
,
params
def
prepare_value
(
self
,
field
,
value
):
"""
Prepare a value to be used in a query by resolving it if it is an
expression and otherwise calling the field's get_db_prep_save().
"""
if
hasattr
(
value
,
'resolve_expression'
):
value
=
value
.
resolve_expression
(
self
.
query
,
allow_joins
=
False
,
for_save
=
True
)
# Don't allow values containing Col expressions. They refer to
# existing columns on a row, but in the case of insert the row
# doesn't exist yet.
if
value
.
contains_column_references
:
raise
ValueError
(
'Failed to insert expression "
%
s" on
%
s. F() expressions '
'can only be used to update, not to insert.'
%
(
value
,
field
)
)
if
value
.
contains_aggregate
:
raise
FieldError
(
"Aggregate functions are not allowed in this query"
)
else
:
value
=
field
.
get_db_prep_save
(
value
,
connection
=
self
.
connection
)
return
value
def
pre_save_val
(
self
,
field
,
obj
):
"""
Get the given field's value off the given obj. pre_save() is used for
things like auto_now on DateTimeField. Skip it if this is a raw query.
"""
if
self
.
query
.
raw
:
return
getattr
(
obj
,
field
.
attname
)
return
field
.
pre_save
(
obj
,
add
=
True
)
def
assemble_as_sql
(
self
,
fields
,
value_rows
):
"""
Take a sequence of N fields and a sequence of M rows of values,
generate placeholder SQL and parameters for each field and value, and
return a pair containing:
* a sequence of M rows of N SQL placeholder strings, and
* a sequence of M rows of corresponding parameter values.
Each placeholder string may contain any number of '
%
s' interpolation
strings, and each parameter row will contain exactly as many params
as the total number of '
%
s's in the corresponding placeholder row.
"""
if
not
value_rows
:
return
[],
[]
# list of (sql, [params]) tuples for each object to be saved
# Shape: [n_objs][n_fields][2]
rows_of_fields_as_sql
=
(
(
self
.
field_as_sql
(
field
,
v
)
for
field
,
v
in
zip
(
fields
,
row
))
for
row
in
value_rows
)
# tuple like ([sqls], [[params]s]) for each object to be saved
# Shape: [n_objs][2][n_fields]
sql_and_param_pair_rows
=
(
zip
(
*
row
)
for
row
in
rows_of_fields_as_sql
)
# Extract separate lists for placeholders and params.
# Each of these has shape [n_objs][n_fields]
placeholder_rows
,
param_rows
=
zip
(
*
sql_and_param_pair_rows
)
# Params for each field are still lists, and need to be flattened.
param_rows
=
[[
p
for
ps
in
row
for
p
in
ps
]
for
row
in
param_rows
]
return
placeholder_rows
,
param_rows
def
as_sql
(
self
):
def
as_sql
(
self
):
# We don't need quote_name_unless_alias() here, since these are all
# We don't need quote_name_unless_alias() here, since these are all
...
@@ -933,35 +1018,27 @@ class SQLInsertCompiler(SQLCompiler):
...
@@ -933,35 +1018,27 @@ class SQLInsertCompiler(SQLCompiler):
result
.
append
(
'(
%
s)'
%
', '
.
join
(
qn
(
f
.
column
)
for
f
in
fields
))
result
.
append
(
'(
%
s)'
%
', '
.
join
(
qn
(
f
.
column
)
for
f
in
fields
))
if
has_fields
:
if
has_fields
:
params
=
values
=
[
value_rows
=
[
[
[
self
.
prepare_value
(
field
,
self
.
pre_save_val
(
field
,
obj
))
for
field
in
fields
]
f
.
get_db_prep_save
(
getattr
(
obj
,
f
.
attname
)
if
self
.
query
.
raw
else
f
.
pre_save
(
obj
,
True
),
connection
=
self
.
connection
)
for
f
in
fields
]
for
obj
in
self
.
query
.
objs
for
obj
in
self
.
query
.
objs
]
]
else
:
else
:
values
=
[[
self
.
connection
.
ops
.
pk_default_value
()]
for
obj
in
self
.
query
.
objs
]
# An empty object.
params
=
[[]
]
value_rows
=
[[
self
.
connection
.
ops
.
pk_default_value
()]
for
_
in
self
.
query
.
objs
]
fields
=
[
None
]
fields
=
[
None
]
can_bulk
=
(
not
any
(
hasattr
(
field
,
"get_placeholder"
)
for
field
in
fields
)
and
not
self
.
return_id
and
self
.
connection
.
features
.
has_bulk_insert
)
if
can_bulk
:
# Currently the backends just accept values when generating bulk
placeholders
=
[[
"
%
s"
]
*
len
(
fields
)]
# queries and generate their own placeholders. Doing that isn't
else
:
# necessary and it should be possible to use placeholders and
placeholders
=
[
# expressions in bulk inserts too.
[
self
.
placeholder
(
field
,
v
)
for
field
,
v
in
zip
(
fields
,
val
)]
can_bulk
=
(
not
self
.
return_id
and
self
.
connection
.
features
.
has_bulk_insert
)
for
val
in
values
]
placeholder_rows
,
param_rows
=
self
.
assemble_as_sql
(
fields
,
value_rows
)
# Oracle Spatial needs to remove some values due to #10888
params
=
self
.
connection
.
ops
.
modify_insert_params
(
placeholders
,
params
)
if
self
.
return_id
and
self
.
connection
.
features
.
can_return_id_from_insert
:
if
self
.
return_id
and
self
.
connection
.
features
.
can_return_id_from_insert
:
params
=
params
[
0
]
params
=
param
_row
s
[
0
]
col
=
"
%
s.
%
s"
%
(
qn
(
opts
.
db_table
),
qn
(
opts
.
pk
.
column
))
col
=
"
%
s.
%
s"
%
(
qn
(
opts
.
db_table
),
qn
(
opts
.
pk
.
column
))
result
.
append
(
"VALUES (
%
s)"
%
", "
.
join
(
placeholders
[
0
]))
result
.
append
(
"VALUES (
%
s)"
%
", "
.
join
(
placeholder
_row
s
[
0
]))
r_fmt
,
r_params
=
self
.
connection
.
ops
.
return_insert_id
()
r_fmt
,
r_params
=
self
.
connection
.
ops
.
return_insert_id
()
# Skip empty r_fmt to allow subclasses to customize behavior for
# Skip empty r_fmt to allow subclasses to customize behavior for
# 3rd party backends. Refs #19096.
# 3rd party backends. Refs #19096.
...
@@ -969,13 +1046,14 @@ class SQLInsertCompiler(SQLCompiler):
...
@@ -969,13 +1046,14 @@ class SQLInsertCompiler(SQLCompiler):
result
.
append
(
r_fmt
%
col
)
result
.
append
(
r_fmt
%
col
)
params
+=
r_params
params
+=
r_params
return
[(
" "
.
join
(
result
),
tuple
(
params
))]
return
[(
" "
.
join
(
result
),
tuple
(
params
))]
if
can_bulk
:
if
can_bulk
:
result
.
append
(
self
.
connection
.
ops
.
bulk_insert_sql
(
fields
,
len
(
values
)
))
result
.
append
(
self
.
connection
.
ops
.
bulk_insert_sql
(
fields
,
placeholder_rows
))
return
[(
" "
.
join
(
result
),
tuple
(
v
for
val
in
values
for
v
in
val
))]
return
[(
" "
.
join
(
result
),
tuple
(
p
for
ps
in
param_rows
for
p
in
ps
))]
else
:
else
:
return
[
return
[
(
" "
.
join
(
result
+
[
"VALUES (
%
s)"
%
", "
.
join
(
p
)]),
vals
)
(
" "
.
join
(
result
+
[
"VALUES (
%
s)"
%
", "
.
join
(
p
)]),
vals
)
for
p
,
vals
in
zip
(
placeholder
s
,
param
s
)
for
p
,
vals
in
zip
(
placeholder
_rows
,
param_row
s
)
]
]
def
execute_sql
(
self
,
return_id
=
False
):
def
execute_sql
(
self
,
return_id
=
False
):
...
@@ -1034,10 +1112,11 @@ class SQLUpdateCompiler(SQLCompiler):
...
@@ -1034,10 +1112,11 @@ class SQLUpdateCompiler(SQLCompiler):
connection
=
self
.
connection
,
connection
=
self
.
connection
,
)
)
else
:
else
:
raise
TypeError
(
"Database is trying to update a relational field "
raise
TypeError
(
"of type
%
s with a value of type
%
s. Make sure "
"Tried to update field
%
s with a model instance,
%
r. "
"you are setting the correct relations"
%
"Use a value compatible with
%
s."
(
field
.
__class__
.
__name__
,
val
.
__class__
.
__name__
))
%
(
field
,
val
,
field
.
__class__
.
__name__
)
)
else
:
else
:
val
=
field
.
get_db_prep_save
(
val
,
connection
=
self
.
connection
)
val
=
field
.
get_db_prep_save
(
val
,
connection
=
self
.
connection
)
...
...
django/db/models/sql/subqueries.py
Dosyayı görüntüle @
134ca4d4
...
@@ -139,9 +139,9 @@ class UpdateQuery(Query):
...
@@ -139,9 +139,9 @@ class UpdateQuery(Query):
def
add_update_fields
(
self
,
values_seq
):
def
add_update_fields
(
self
,
values_seq
):
"""
"""
Turn a sequence of (field, model, value) triples into an update query.
Append a sequence of (field, model, value) triples to the internal list
Used by add_update_values() as well as the "fast" update path when
that will be used to generate the UPDATE query. Might be more usefully
saving models
.
called add_update_targets() to hint at the extra information here
.
"""
"""
self
.
values
.
extend
(
values_seq
)
self
.
values
.
extend
(
values_seq
)
...
...
docs/ref/models/expressions.txt
Dosyayı görüntüle @
134ca4d4
...
@@ -5,10 +5,14 @@ Query Expressions
...
@@ -5,10 +5,14 @@ Query Expressions
.. currentmodule:: django.db.models
.. currentmodule:: django.db.models
Query expressions describe a value or a computation that can be used as part of
Query expressions describe a value or a computation that can be used as part of
a filter, order by, annotation, or aggregate. There are a number of built-in
an update, create, filter, order by, annotation, or aggregate. There are a
expressions (documented below) that can be used to help you write queries.
number of built-in expressions (documented below) that can be used to help you
Expressions can be combined, or in some cases nested, to form more complex
write queries. Expressions can be combined, or in some cases nested, to form
computations.
more complex computations.
.. versionchanged:: 1.9
Support for using expressions when creating new model instances was added.
Supported arithmetic
Supported arithmetic
====================
====================
...
@@ -27,7 +31,7 @@ Some examples
...
@@ -27,7 +31,7 @@ Some examples
.. code-block:: python
.. code-block:: python
from django.db.models import F, Count
from django.db.models import F, Count
from django.db.models.functions import Length
from django.db.models.functions import Length
, Upper, Value
# Find companies that have more employees than chairs.
# Find companies that have more employees than chairs.
Company.objects.filter(num_employees__gt=F('num_chairs'))
Company.objects.filter(num_employees__gt=F('num_chairs'))
...
@@ -49,6 +53,13 @@ Some examples
...
@@ -49,6 +53,13 @@ Some examples
>>> company.chairs_needed
>>> company.chairs_needed
70
70
# Create a new company using expressions.
>>> company = Company.objects.create(name='Google', ticker=Upper(Value('goog')))
# Be sure to refresh it if you need to access the field.
>>> company.refresh_from_db()
>>> company.ticker
'GOOG'
# Annotate models with an aggregated value. Both forms
# Annotate models with an aggregated value. Both forms
# below are equivalent.
# below are equivalent.
Company.objects.annotate(num_products=Count('products'))
Company.objects.annotate(num_products=Count('products'))
...
@@ -122,6 +133,8 @@ and describe the operation.
...
@@ -122,6 +133,8 @@ and describe the operation.
will need to be reloaded::
will need to be reloaded::
reporter = Reporters.objects.get(pk=reporter.pk)
reporter = Reporters.objects.get(pk=reporter.pk)
# Or, more succinctly:
reporter.refresh_from_db()
As well as being used in operations on single instances as above, ``F()`` can
As well as being used in operations on single instances as above, ``F()`` can
be used on ``QuerySets`` of object instances, with ``update()``. This reduces
be used on ``QuerySets`` of object instances, with ``update()``. This reduces
...
@@ -356,7 +369,10 @@ boolean, or string within an expression, you can wrap that value within a
...
@@ -356,7 +369,10 @@ boolean, or string within an expression, you can wrap that value within a
You will rarely need to use ``Value()`` directly. When you write the expression
You will rarely need to use ``Value()`` directly. When you write the expression
``F('field') + 1``, Django implicitly wraps the ``1`` in a ``Value()``,
``F('field') + 1``, Django implicitly wraps the ``1`` in a ``Value()``,
allowing simple values to be used in more complex expressions.
allowing simple values to be used in more complex expressions. You will need to
use ``Value()`` when you want to pass a string to an expression. Most
expressions interpret a string argument as the name of a field, like
``Lower('name')``.
The ``value`` argument describes the value to be included in the expression,
The ``value`` argument describes the value to be included in the expression,
such as ``1``, ``True``, or ``None``. Django knows how to convert these Python
such as ``1``, ``True``, or ``None``. Django knows how to convert these Python
...
...
docs/releases/1.9.txt
Dosyayı görüntüle @
134ca4d4
...
@@ -542,6 +542,10 @@ Models
...
@@ -542,6 +542,10 @@ Models
* Added a new model field check that makes sure
* Added a new model field check that makes sure
:attr:`~django.db.models.Field.default` is a valid value.
:attr:`~django.db.models.Field.default` is a valid value.
* :doc:`Query expressions </ref/models/expressions>` can now be used when
creating new model instances using ``save()``, ``create()``, and
``bulk_create()``.
Requests and Responses
Requests and Responses
^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^
...
...
tests/bulk_create/tests.py
Dosyayı görüntüle @
134ca4d4
...
@@ -3,6 +3,8 @@ from __future__ import unicode_literals
...
@@ -3,6 +3,8 @@ from __future__ import unicode_literals
from
operator
import
attrgetter
from
operator
import
attrgetter
from
django.db
import
connection
from
django.db
import
connection
from
django.db.models
import
Value
from
django.db.models.functions
import
Lower
from
django.test
import
(
from
django.test
import
(
TestCase
,
override_settings
,
skipIfDBFeature
,
skipUnlessDBFeature
,
TestCase
,
override_settings
,
skipIfDBFeature
,
skipUnlessDBFeature
,
)
)
...
@@ -183,3 +185,12 @@ class BulkCreateTests(TestCase):
...
@@ -183,3 +185,12 @@ class BulkCreateTests(TestCase):
TwoFields
.
objects
.
all
()
.
delete
()
TwoFields
.
objects
.
all
()
.
delete
()
with
self
.
assertNumQueries
(
1
):
with
self
.
assertNumQueries
(
1
):
TwoFields
.
objects
.
bulk_create
(
objs
,
len
(
objs
))
TwoFields
.
objects
.
bulk_create
(
objs
,
len
(
objs
))
@skipUnlessDBFeature
(
'has_bulk_insert'
)
def
test_bulk_insert_expressions
(
self
):
Restaurant
.
objects
.
bulk_create
([
Restaurant
(
name
=
"Sam's Shake Shack"
),
Restaurant
(
name
=
Lower
(
Value
(
"Betty's Beetroot Bar"
)))
])
bbb
=
Restaurant
.
objects
.
filter
(
name
=
"betty's beetroot bar"
)
self
.
assertEqual
(
bbb
.
count
(),
1
)
tests/expressions/tests.py
Dosyayı görüntüle @
134ca4d4
...
@@ -249,6 +249,32 @@ class BasicExpressionsTests(TestCase):
...
@@ -249,6 +249,32 @@ class BasicExpressionsTests(TestCase):
test_gmbh
=
Company
.
objects
.
get
(
pk
=
test_gmbh
.
pk
)
test_gmbh
=
Company
.
objects
.
get
(
pk
=
test_gmbh
.
pk
)
self
.
assertEqual
(
test_gmbh
.
num_employees
,
36
)
self
.
assertEqual
(
test_gmbh
.
num_employees
,
36
)
def
test_new_object_save
(
self
):
# We should be able to use Funcs when inserting new data
test_co
=
Company
(
name
=
Lower
(
Value
(
"UPPER"
)),
num_employees
=
32
,
num_chairs
=
1
,
ceo
=
Employee
.
objects
.
create
(
firstname
=
"Just"
,
lastname
=
"Doit"
,
salary
=
30
),
)
test_co
.
save
()
test_co
.
refresh_from_db
()
self
.
assertEqual
(
test_co
.
name
,
"upper"
)
def
test_new_object_create
(
self
):
test_co
=
Company
.
objects
.
create
(
name
=
Lower
(
Value
(
"UPPER"
)),
num_employees
=
32
,
num_chairs
=
1
,
ceo
=
Employee
.
objects
.
create
(
firstname
=
"Just"
,
lastname
=
"Doit"
,
salary
=
30
),
)
test_co
.
refresh_from_db
()
self
.
assertEqual
(
test_co
.
name
,
"upper"
)
def
test_object_create_with_aggregate
(
self
):
# Aggregates are not allowed when inserting new data
with
self
.
assertRaisesMessage
(
FieldError
,
'Aggregate functions are not allowed in this query'
):
Company
.
objects
.
create
(
name
=
'Company'
,
num_employees
=
Max
(
Value
(
1
)),
num_chairs
=
1
,
ceo
=
Employee
.
objects
.
create
(
firstname
=
"Just"
,
lastname
=
"Doit"
,
salary
=
30
),
)
def
test_object_update_fk
(
self
):
def
test_object_update_fk
(
self
):
# F expressions cannot be used to update attributes which are foreign
# F expressions cannot be used to update attributes which are foreign
# keys, or attributes which involve joins.
# keys, or attributes which involve joins.
...
@@ -272,7 +298,22 @@ class BasicExpressionsTests(TestCase):
...
@@ -272,7 +298,22 @@ class BasicExpressionsTests(TestCase):
ceo
=
test_gmbh
.
ceo
ceo
=
test_gmbh
.
ceo
)
)
acme
.
num_employees
=
F
(
"num_employees"
)
+
16
acme
.
num_employees
=
F
(
"num_employees"
)
+
16
self
.
assertRaises
(
TypeError
,
acme
.
save
)
msg
=
(
'Failed to insert expression "Col(expressions_company, '
'expressions.Company.num_employees) + Value(16)" on '
'expressions.Company.num_employees. F() expressions can only be '
'used to update, not to insert.'
)
self
.
assertRaisesMessage
(
ValueError
,
msg
,
acme
.
save
)
acme
.
num_employees
=
12
acme
.
name
=
Lower
(
F
(
'name'
))
msg
=
(
'Failed to insert expression "Lower(Col(expressions_company, '
'expressions.Company.name))" on expressions.Company.name. F() '
'expressions can only be used to update, not to insert.'
)
self
.
assertRaisesMessage
(
ValueError
,
msg
,
acme
.
save
)
def
test_ticket_11722_iexact_lookup
(
self
):
def
test_ticket_11722_iexact_lookup
(
self
):
Employee
.
objects
.
create
(
firstname
=
"John"
,
lastname
=
"Doe"
)
Employee
.
objects
.
create
(
firstname
=
"John"
,
lastname
=
"Doe"
)
...
...
tests/model_fields/tests.py
Dosyayı görüntüle @
134ca4d4
...
@@ -98,8 +98,13 @@ class BasicFieldTests(test.TestCase):
...
@@ -98,8 +98,13 @@ class BasicFieldTests(test.TestCase):
self
.
assertTrue
(
instance
.
id
)
self
.
assertTrue
(
instance
.
id
)
# Set field to object on saved instance
# Set field to object on saved instance
instance
.
size
=
instance
instance
.
size
=
instance
msg
=
(
"Tried to update field model_fields.FloatModel.size with a model "
"instance, <FloatModel: FloatModel object>. Use a value "
"compatible with FloatField."
)
with
transaction
.
atomic
():
with
transaction
.
atomic
():
with
self
.
assertRaises
(
TypeError
):
with
self
.
assertRaises
Message
(
TypeError
,
msg
):
instance
.
save
()
instance
.
save
()
# Try setting field to object on retrieved object
# Try setting field to object on retrieved object
obj
=
FloatModel
.
objects
.
get
(
pk
=
instance
.
id
)
obj
=
FloatModel
.
objects
.
get
(
pk
=
instance
.
id
)
...
...
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