A dictionary field that wraps a standard Python dictionary. This is similar to an embedded document, but the structure is not defined. .. note:: Required means it cannot be empty - as the default for DictFields is {}
| 1031 | |
| 1032 | |
| 1033 | class DictField(ComplexBaseField): |
| 1034 | """A dictionary field that wraps a standard Python dictionary. This is |
| 1035 | similar to an embedded document, but the structure is not defined. |
| 1036 | |
| 1037 | .. note:: |
| 1038 | Required means it cannot be empty - as the default for DictFields is {} |
| 1039 | """ |
| 1040 | |
| 1041 | def __init__(self, field=None, *args, **kwargs): |
| 1042 | kwargs.setdefault("default", dict) |
| 1043 | super().__init__(*args, field=field, **kwargs) |
| 1044 | self.set_auto_dereferencing(False) |
| 1045 | |
| 1046 | def validate(self, value): |
| 1047 | """Make sure that a list of valid fields is being used.""" |
| 1048 | if not isinstance(value, dict): |
| 1049 | self.error("Only dictionaries may be used in a DictField") |
| 1050 | |
| 1051 | if key_not_string(value): |
| 1052 | msg = "Invalid dictionary key - documents must have only string keys" |
| 1053 | self.error(msg) |
| 1054 | |
| 1055 | # Following condition applies to MongoDB >= 3.6 |
| 1056 | # older Mongo has stricter constraints but |
| 1057 | # it will be rejected upon insertion anyway |
| 1058 | # Having a validation that depends on the MongoDB version |
| 1059 | # is not straightforward as the field isn't aware of the connected Mongo |
| 1060 | if key_starts_with_dollar(value): |
| 1061 | self.error( |
| 1062 | 'Invalid dictionary key name - keys may not startswith "$" characters' |
| 1063 | ) |
| 1064 | super().validate(value) |
| 1065 | |
| 1066 | def lookup_member(self, member_name): |
| 1067 | return DictField(db_field=member_name) |
| 1068 | |
| 1069 | def prepare_query_value(self, op, value): |
| 1070 | match_operators = [*STRING_OPERATORS] |
| 1071 | |
| 1072 | if op in match_operators and isinstance(value, str): |
| 1073 | return StringField().prepare_query_value(op, value) |
| 1074 | |
| 1075 | if hasattr( |
| 1076 | self.field, "field" |
| 1077 | ): # Used for instance when using DictField(ListField(IntField())) |
| 1078 | if op in ("set", "unset") and isinstance(value, dict): |
| 1079 | return { |
| 1080 | k: self.field.prepare_query_value(op, v) for k, v in value.items() |
| 1081 | } |
| 1082 | return self.field.prepare_query_value(op, value) |
| 1083 | |
| 1084 | return super().prepare_query_value(op, value) |
| 1085 | |
| 1086 | |
| 1087 | class MapField(DictField): |