Create an instance of a Document (subclass) from a PyMongo SON (dict)
(cls, son, _auto_dereference=True, created=False)
| 777 | |
| 778 | @classmethod |
| 779 | def _from_son(cls, son, _auto_dereference=True, created=False): |
| 780 | """Create an instance of a Document (subclass) from a PyMongo SON (dict)""" |
| 781 | if son and not isinstance(son, dict): |
| 782 | raise ValueError( |
| 783 | "The source SON object needs to be of type 'dict' but a '%s' was found" |
| 784 | % type(son) |
| 785 | ) |
| 786 | |
| 787 | # Get the class name from the document, falling back to the given |
| 788 | # class if unavailable |
| 789 | class_name = son.get("_cls", cls._class_name) |
| 790 | |
| 791 | # Convert SON to a data dict, making sure each key is a string and |
| 792 | # corresponds to the right db field. |
| 793 | # This is needed as _from_son is currently called both from BaseDocument.__init__ |
| 794 | # and from EmbeddedDocumentField.to_python |
| 795 | data = {} |
| 796 | for key, value in son.items(): |
| 797 | key = str(key) |
| 798 | key = cls._db_field_map.get(key, key) |
| 799 | data[key] = value |
| 800 | |
| 801 | # Return correct subclass for document type |
| 802 | if class_name != cls._class_name: |
| 803 | cls = get_document(class_name) |
| 804 | |
| 805 | errors_dict = {} |
| 806 | |
| 807 | fields = cls._fields |
| 808 | if not _auto_dereference: |
| 809 | # if auto_deref is turned off, we copy the fields so |
| 810 | # we can mutate the auto_dereference of the fields |
| 811 | fields = copy.deepcopy(fields) |
| 812 | |
| 813 | # Apply field-name / db-field conversion |
| 814 | for field_name, field in fields.items(): |
| 815 | field.set_auto_dereferencing( |
| 816 | _auto_dereference |
| 817 | ) # align the field's auto-dereferencing with the document's |
| 818 | if field.db_field in data: |
| 819 | value = data[field.db_field] |
| 820 | try: |
| 821 | data[field_name] = ( |
| 822 | value if value is None else field.to_python(value) |
| 823 | ) |
| 824 | if field_name != field.db_field: |
| 825 | del data[field.db_field] |
| 826 | except (AttributeError, ValueError) as e: |
| 827 | errors_dict[field_name] = e |
| 828 | |
| 829 | if errors_dict: |
| 830 | errors = "\n".join([f"Field '{k}' - {v}" for k, v in errors_dict.items()]) |
| 831 | msg = "Invalid data to create a `{}` instance.\n{}".format( |
| 832 | cls._class_name, |
| 833 | errors, |
| 834 | ) |
| 835 | raise InvalidDocumentError(msg) |
| 836 |