Skip to content

Commit 9941d44

Browse files
Merge branch 'main' into optional-with-pipe-syntax
2 parents cd6dd89 + 47a7a06 commit 9941d44

File tree

4 files changed

+43
-4
lines changed

4 files changed

+43
-4
lines changed

docs/persistence.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,18 @@ field needs to be referenced, such as when specifying sort options in a
273273
When specifying sorting order, the ``+`` and ``-`` unary operators can be used
274274
on the class field attributes to indicate ascending and descending order.
275275

276+
Finally, the ``ClassVar`` annotation can be used to define a regular class
277+
attribute that should not be mapped to the Elasticsearch index::
278+
279+
.. code:: python
280+
281+
from typing import ClassVar
282+
283+
class MyDoc(Document):
284+
title: M[str]
285+
created_at: M[datetime] = mapped_field(default_factory=datetime.now)
286+
my_var: ClassVar[str] # regular class variable, ignored by Elasticsearch
287+
276288
Note on dates
277289
~~~~~~~~~~~~~
278290

elasticsearch_dsl/document_base.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
TYPE_CHECKING,
2222
Any,
2323
Callable,
24+
ClassVar,
2425
Dict,
2526
Generic,
2627
List,
@@ -165,7 +166,10 @@ def __init__(self, name: str, bases: Tuple[type, ...], attrs: Dict[str, Any]):
165166
# field8: M[str] = mapped_field(MyCustomText(), default="foo")
166167
#
167168
# # legacy format without Python typing
168-
# field8 = Text()
169+
# field9 = Text()
170+
#
171+
# # ignore attributes
172+
# field10: ClassVar[string] = "a regular class variable"
169173
annotations = attrs.get("__annotations__", {})
170174
fields = set([n for n in attrs if isinstance(attrs[n], Field)])
171175
fields.update(annotations.keys())
@@ -178,10 +182,14 @@ def __init__(self, name: str, bases: Tuple[type, ...], attrs: Dict[str, Any]):
178182
# the field has a type annotation, so next we try to figure out
179183
# what field type we can use
180184
type_ = annotations[name]
185+
skip = False
181186
required = True
182187
multi = False
183188
while hasattr(type_, "__origin__"):
184-
if type_.__origin__ == Mapped:
189+
if type_.__origin__ == ClassVar:
190+
skip = True
191+
break
192+
elif type_.__origin__ == Mapped:
185193
# M[type] -> extract the wrapped type
186194
type_ = type_.__args__[0]
187195
elif type_.__origin__ == Union:
@@ -198,6 +206,9 @@ def __init__(self, name: str, bases: Tuple[type, ...], attrs: Dict[str, Any]):
198206
type_ = type_.__args__[0]
199207
else:
200208
break
209+
if skip or type_ == ClassVar:
210+
# skip ClassVar attributes
211+
continue
201212
if type(type_) is UnionType:
202213
# a union given with the pipe syntax
203214
args = get_args(type_)

tests/_async/test_document.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import sys
2828
from datetime import datetime
2929
from hashlib import md5
30-
from typing import Any, Dict, List, Optional
30+
from typing import Any, ClassVar, Dict, List, Optional
3131

3232
import pytest
3333
from pytest import raises
@@ -676,6 +676,8 @@ class TypedDoc(AsyncDocument):
676676
s4: M[Optional[Secret]] = mapped_field(
677677
SecretField(), default_factory=lambda: "foo"
678678
)
679+
i1: ClassVar
680+
i2: ClassVar[int]
679681

680682
props = TypedDoc._doc_type.mapping.to_dict()["properties"]
681683
assert props == {
@@ -709,6 +711,9 @@ class TypedDoc(AsyncDocument):
709711
"s4": {"type": "text"},
710712
}
711713

714+
TypedDoc.i1 = "foo"
715+
TypedDoc.i2 = 123
716+
712717
doc = TypedDoc()
713718
assert doc.k3 == "foo"
714719
assert doc.s4 == "foo"
@@ -724,6 +729,9 @@ class TypedDoc(AsyncDocument):
724729
"s3",
725730
}
726731

732+
assert TypedDoc.i1 == "foo"
733+
assert TypedDoc.i2 == 123
734+
727735
doc.st = "s"
728736
doc.li = [1, 2, 3]
729737
doc.k1 = "k1"

tests/_sync/test_document.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import sys
2828
from datetime import datetime
2929
from hashlib import md5
30-
from typing import Any, Dict, List, Optional
30+
from typing import Any, ClassVar, Dict, List, Optional
3131

3232
import pytest
3333
from pytest import raises
@@ -676,6 +676,8 @@ class TypedDoc(Document):
676676
s4: M[Optional[Secret]] = mapped_field(
677677
SecretField(), default_factory=lambda: "foo"
678678
)
679+
i1: ClassVar
680+
i2: ClassVar[int]
679681

680682
props = TypedDoc._doc_type.mapping.to_dict()["properties"]
681683
assert props == {
@@ -709,6 +711,9 @@ class TypedDoc(Document):
709711
"s4": {"type": "text"},
710712
}
711713

714+
TypedDoc.i1 = "foo"
715+
TypedDoc.i2 = 123
716+
712717
doc = TypedDoc()
713718
assert doc.k3 == "foo"
714719
assert doc.s4 == "foo"
@@ -724,6 +729,9 @@ class TypedDoc(Document):
724729
"s3",
725730
}
726731

732+
assert TypedDoc.i1 == "foo"
733+
assert TypedDoc.i2 == 123
734+
727735
doc.st = "s"
728736
doc.li = [1, 2, 3]
729737
doc.k1 = "k1"

0 commit comments

Comments
 (0)