mirror of
https://github.com/django/django.git
synced 2025-08-04 10:59:45 +00:00
Fixed #29049 -- Added slicing notation to F expressions.
Co-authored-by: Priyansh Saxena <askpriyansh@gmail.com> Co-authored-by: Niclas Olofsson <n@niclasolofsson.se> Co-authored-by: David Smith <smithdc@gmail.com> Co-authored-by: Mariusz Felisiak <felisiak.mariusz@gmail.com> Co-authored-by: Abhinav Yadav <abhinav.sny.2002@gmail.com>
This commit is contained in:
parent
561e16d6a7
commit
94b6f101f7
8 changed files with 256 additions and 4 deletions
|
@ -84,6 +84,7 @@ from .models import (
|
|||
RemoteEmployee,
|
||||
Result,
|
||||
SimulationRun,
|
||||
Text,
|
||||
Time,
|
||||
)
|
||||
|
||||
|
@ -205,6 +206,100 @@ class BasicExpressionsTests(TestCase):
|
|||
],
|
||||
)
|
||||
|
||||
def _test_slicing_of_f_expressions(self, model):
|
||||
tests = [
|
||||
(F("name")[:], "Example Inc.", "Example Inc."),
|
||||
(F("name")[:7], "Example Inc.", "Example"),
|
||||
(F("name")[:6][:5], "Example", "Examp"), # Nested slicing.
|
||||
(F("name")[0], "Examp", "E"),
|
||||
(F("name")[5], "E", ""),
|
||||
(F("name")[7:], "Foobar Ltd.", "Ltd."),
|
||||
(F("name")[0:10], "Ltd.", "Ltd."),
|
||||
(F("name")[2:7], "Test GmbH", "st Gm"),
|
||||
(F("name")[1:][:3], "st Gm", "t G"),
|
||||
(F("name")[2:2], "t G", ""),
|
||||
]
|
||||
for expression, name, expected in tests:
|
||||
with self.subTest(expression=expression, name=name, expected=expected):
|
||||
obj = model.objects.get(name=name)
|
||||
obj.name = expression
|
||||
obj.save()
|
||||
obj.refresh_from_db()
|
||||
self.assertEqual(obj.name, expected)
|
||||
|
||||
def test_slicing_of_f_expressions_charfield(self):
|
||||
self._test_slicing_of_f_expressions(Company)
|
||||
|
||||
def test_slicing_of_f_expressions_textfield(self):
|
||||
Text.objects.bulk_create(
|
||||
[Text(name=company.name) for company in Company.objects.all()]
|
||||
)
|
||||
self._test_slicing_of_f_expressions(Text)
|
||||
|
||||
def test_slicing_of_f_expressions_with_annotate(self):
|
||||
qs = Company.objects.annotate(
|
||||
first_three=F("name")[:3],
|
||||
after_three=F("name")[3:],
|
||||
random_four=F("name")[2:5],
|
||||
first_letter_slice=F("name")[:1],
|
||||
first_letter_index=F("name")[0],
|
||||
)
|
||||
tests = [
|
||||
("first_three", ["Exa", "Foo", "Tes"]),
|
||||
("after_three", ["mple Inc.", "bar Ltd.", "t GmbH"]),
|
||||
("random_four", ["amp", "oba", "st "]),
|
||||
("first_letter_slice", ["E", "F", "T"]),
|
||||
("first_letter_index", ["E", "F", "T"]),
|
||||
]
|
||||
for annotation, expected in tests:
|
||||
with self.subTest(annotation):
|
||||
self.assertCountEqual(qs.values_list(annotation, flat=True), expected)
|
||||
|
||||
def test_slicing_of_f_expression_with_annotated_expression(self):
|
||||
qs = Company.objects.annotate(
|
||||
new_name=Case(
|
||||
When(based_in_eu=True, then=Concat(Value("EU:"), F("name"))),
|
||||
default=F("name"),
|
||||
),
|
||||
first_two=F("new_name")[:3],
|
||||
)
|
||||
self.assertCountEqual(
|
||||
qs.values_list("first_two", flat=True),
|
||||
["Exa", "EU:", "Tes"],
|
||||
)
|
||||
|
||||
def test_slicing_of_f_expressions_with_negative_index(self):
|
||||
msg = "Negative indexing is not supported."
|
||||
indexes = [slice(0, -4), slice(-4, 0), slice(-4), -5]
|
||||
for i in indexes:
|
||||
with self.subTest(i=i), self.assertRaisesMessage(ValueError, msg):
|
||||
F("name")[i]
|
||||
|
||||
def test_slicing_of_f_expressions_with_slice_stop_less_than_slice_start(self):
|
||||
msg = "Slice stop must be greater than slice start."
|
||||
with self.assertRaisesMessage(ValueError, msg):
|
||||
F("name")[4:2]
|
||||
|
||||
def test_slicing_of_f_expressions_with_invalid_type(self):
|
||||
msg = "Argument to slice must be either int or slice instance."
|
||||
with self.assertRaisesMessage(TypeError, msg):
|
||||
F("name")["error"]
|
||||
|
||||
def test_slicing_of_f_expressions_with_step(self):
|
||||
msg = "Step argument is not supported."
|
||||
with self.assertRaisesMessage(ValueError, msg):
|
||||
F("name")[::4]
|
||||
|
||||
def test_slicing_of_f_unsupported_field(self):
|
||||
msg = "This field does not support slicing."
|
||||
with self.assertRaisesMessage(NotSupportedError, msg):
|
||||
Company.objects.update(num_chairs=F("num_chairs")[:4])
|
||||
|
||||
def test_slicing_of_outerref(self):
|
||||
inner = Company.objects.filter(name__startswith=OuterRef("ceo__firstname")[0])
|
||||
outer = Company.objects.filter(Exists(inner)).values_list("name", flat=True)
|
||||
self.assertSequenceEqual(outer, ["Foobar Ltd."])
|
||||
|
||||
def test_arithmetic(self):
|
||||
# We can perform arithmetic operations in expressions
|
||||
# Make sure we have 2 spare chairs
|
||||
|
@ -2359,6 +2454,12 @@ class ReprTests(SimpleTestCase):
|
|||
repr(Func("published", function="TO_CHAR")),
|
||||
"Func(F(published), function=TO_CHAR)",
|
||||
)
|
||||
self.assertEqual(
|
||||
repr(F("published")[0:2]), "Sliced(F(published), slice(0, 2, None))"
|
||||
)
|
||||
self.assertEqual(
|
||||
repr(OuterRef("name")[1:5]), "Sliced(OuterRef(name), slice(1, 5, None))"
|
||||
)
|
||||
self.assertEqual(repr(OrderBy(Value(1))), "OrderBy(Value(1), descending=False)")
|
||||
self.assertEqual(repr(RawSQL("table.col", [])), "RawSQL(table.col, [])")
|
||||
self.assertEqual(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue