diff options
Diffstat (limited to 'django/core/serializers/xml_serializer.py')
| -rw-r--r-- | django/core/serializers/xml_serializer.py | 170 |
1 files changed, 113 insertions, 57 deletions
diff --git a/django/core/serializers/xml_serializer.py b/django/core/serializers/xml_serializer.py index 88bfa59032..8d3918cfaa 100644 --- a/django/core/serializers/xml_serializer.py +++ b/django/core/serializers/xml_serializer.py @@ -11,23 +11,25 @@ from django.conf import settings from django.core.exceptions import ObjectDoesNotExist from django.core.serializers import base from django.db import DEFAULT_DB_ALIAS, models -from django.utils.xmlutils import ( - SimplerXMLGenerator, UnserializableContentError, -) +from django.utils.xmlutils import SimplerXMLGenerator, UnserializableContentError class Serializer(base.Serializer): """Serialize a QuerySet to XML.""" def indent(self, level): - if self.options.get('indent') is not None: - self.xml.ignorableWhitespace('\n' + ' ' * self.options.get('indent') * level) + if self.options.get("indent") is not None: + self.xml.ignorableWhitespace( + "\n" + " " * self.options.get("indent") * level + ) def start_serialization(self): """ Start serialization -- open the XML document and the root element. """ - self.xml = SimplerXMLGenerator(self.stream, self.options.get("encoding", settings.DEFAULT_CHARSET)) + self.xml = SimplerXMLGenerator( + self.stream, self.options.get("encoding", settings.DEFAULT_CHARSET) + ) self.xml.startDocument() self.xml.startElement("django-objects", {"version": "1.0"}) @@ -44,14 +46,16 @@ class Serializer(base.Serializer): Called as each object is handled. """ if not hasattr(obj, "_meta"): - raise base.SerializationError("Non-model object (%s) encountered during serialization" % type(obj)) + raise base.SerializationError( + "Non-model object (%s) encountered during serialization" % type(obj) + ) self.indent(1) - attrs = {'model': str(obj._meta)} - if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'): + attrs = {"model": str(obj._meta)} + if not self.use_natural_primary_keys or not hasattr(obj, "natural_key"): obj_pk = obj.pk if obj_pk is not None: - attrs['pk'] = str(obj_pk) + attrs["pk"] = str(obj_pk) self.xml.startElement("object", attrs) @@ -68,23 +72,28 @@ class Serializer(base.Serializer): ManyToManyFields). """ self.indent(2) - self.xml.startElement('field', { - 'name': field.name, - 'type': field.get_internal_type(), - }) + self.xml.startElement( + "field", + { + "name": field.name, + "type": field.get_internal_type(), + }, + ) # Get a "string version" of the object's data. if getattr(obj, field.name) is not None: value = field.value_to_string(obj) - if field.get_internal_type() == 'JSONField': + if field.get_internal_type() == "JSONField": # Dump value since JSONField.value_to_string() doesn't output # strings. value = json.dumps(value, cls=field.encoder) try: self.xml.characters(value) except UnserializableContentError: - raise ValueError("%s.%s (pk:%s) contains unserializable characters" % ( - obj.__class__.__name__, field.name, obj.pk)) + raise ValueError( + "%s.%s (pk:%s) contains unserializable characters" + % (obj.__class__.__name__, field.name, obj.pk) + ) else: self.xml.addQuickElement("None") @@ -98,7 +107,9 @@ class Serializer(base.Serializer): self._start_relational_field(field) related_att = getattr(obj, field.get_attname()) if related_att is not None: - if self.use_natural_foreign_keys and hasattr(field.remote_field.model, 'natural_key'): + if self.use_natural_foreign_keys and hasattr( + field.remote_field.model, "natural_key" + ): related = getattr(obj, field.name) # If related object has a natural key, use it related = related.natural_key() @@ -121,7 +132,9 @@ class Serializer(base.Serializer): """ if field.remote_field.through._meta.auto_created: self._start_relational_field(field) - if self.use_natural_foreign_keys and hasattr(field.remote_field.model, 'natural_key'): + if self.use_natural_foreign_keys and hasattr( + field.remote_field.model, "natural_key" + ): # If the objects in the m2m have a natural key, use it def handle_m2m(value): natural = value.natural_key() @@ -132,12 +145,13 @@ class Serializer(base.Serializer): self.xml.characters(str(key_value)) self.xml.endElement("natural") self.xml.endElement("object") + else: + def handle_m2m(value): - self.xml.addQuickElement("object", attrs={ - 'pk': str(value.pk) - }) - m2m_iter = getattr(obj, '_prefetched_objects_cache', {}).get( + self.xml.addQuickElement("object", attrs={"pk": str(value.pk)}) + + m2m_iter = getattr(obj, "_prefetched_objects_cache", {}).get( field.name, getattr(obj, field.name).iterator(), ) @@ -149,19 +163,29 @@ class Serializer(base.Serializer): def _start_relational_field(self, field): """Output the <field> element for relational fields.""" self.indent(2) - self.xml.startElement('field', { - 'name': field.name, - 'rel': field.remote_field.__class__.__name__, - 'to': str(field.remote_field.model._meta), - }) + self.xml.startElement( + "field", + { + "name": field.name, + "rel": field.remote_field.__class__.__name__, + "to": str(field.remote_field.model._meta), + }, + ) class Deserializer(base.Deserializer): """Deserialize XML.""" - def __init__(self, stream_or_string, *, using=DEFAULT_DB_ALIAS, ignorenonexistent=False, **options): + def __init__( + self, + stream_or_string, + *, + using=DEFAULT_DB_ALIAS, + ignorenonexistent=False, + **options, + ): super().__init__(stream_or_string, **options) - self.handle_forward_references = options.pop('handle_forward_references', False) + self.handle_forward_references = options.pop("handle_forward_references", False) self.event_stream = pulldom.parse(self.stream, self._make_parser()) self.db = using self.ignore = ignorenonexistent @@ -185,9 +209,10 @@ class Deserializer(base.Deserializer): # Start building a data dictionary from the object. data = {} - if node.hasAttribute('pk'): + if node.hasAttribute("pk"): data[Model._meta.pk.attname] = Model._meta.pk.to_python( - node.getAttribute('pk')) + node.getAttribute("pk") + ) # Also start building a dict of m2m data (this is saved as # {m2m_accessor_attribute : [list_of_related_objects]}) @@ -201,7 +226,9 @@ class Deserializer(base.Deserializer): # sensing a pattern here?) field_name = field_node.getAttribute("name") if not field_name: - raise base.DeserializationError("<field> node is missing the 'name' attribute") + raise base.DeserializationError( + "<field> node is missing the 'name' attribute" + ) # Get the field from the Model. This will raise a # FieldDoesNotExist if, well, the field doesn't exist, which will @@ -211,34 +238,38 @@ class Deserializer(base.Deserializer): field = Model._meta.get_field(field_name) # As is usually the case, relation fields get the special treatment. - if field.remote_field and isinstance(field.remote_field, models.ManyToManyRel): + if field.remote_field and isinstance( + field.remote_field, models.ManyToManyRel + ): value = self._handle_m2m_field_node(field_node, field) if value == base.DEFER_FIELD: deferred_fields[field] = [ [ getInnerText(nat_node).strip() - for nat_node in obj_node.getElementsByTagName('natural') + for nat_node in obj_node.getElementsByTagName("natural") ] - for obj_node in field_node.getElementsByTagName('object') + for obj_node in field_node.getElementsByTagName("object") ] else: m2m_data[field.name] = value - elif field.remote_field and isinstance(field.remote_field, models.ManyToOneRel): + elif field.remote_field and isinstance( + field.remote_field, models.ManyToOneRel + ): value = self._handle_fk_field_node(field_node, field) if value == base.DEFER_FIELD: deferred_fields[field] = [ getInnerText(k).strip() - for k in field_node.getElementsByTagName('natural') + for k in field_node.getElementsByTagName("natural") ] else: data[field.attname] = value else: - if field_node.getElementsByTagName('None'): + if field_node.getElementsByTagName("None"): value = None else: value = field.to_python(getInnerText(field_node).strip()) # Load value since JSONField.to_python() outputs strings. - if field.get_internal_type() == 'JSONField': + if field.get_internal_type() == "JSONField": value = json.loads(value, cls=field.decoder) data[field.name] = value @@ -252,17 +283,19 @@ class Deserializer(base.Deserializer): Handle a <field> node for a ForeignKey """ # Check if there is a child node named 'None', returning None if so. - if node.getElementsByTagName('None'): + if node.getElementsByTagName("None"): return None else: model = field.remote_field.model - if hasattr(model._default_manager, 'get_by_natural_key'): - keys = node.getElementsByTagName('natural') + if hasattr(model._default_manager, "get_by_natural_key"): + keys = node.getElementsByTagName("natural") if keys: # If there are 'natural' subelements, it must be a natural key field_value = [getInnerText(k).strip() for k in keys] try: - obj = model._default_manager.db_manager(self.db).get_by_natural_key(*field_value) + obj = model._default_manager.db_manager( + self.db + ).get_by_natural_key(*field_value) except ObjectDoesNotExist: if self.handle_forward_references: return base.DEFER_FIELD @@ -276,11 +309,15 @@ class Deserializer(base.Deserializer): else: # Otherwise, treat like a normal PK field_value = getInnerText(node).strip() - obj_pk = model._meta.get_field(field.remote_field.field_name).to_python(field_value) + obj_pk = model._meta.get_field( + field.remote_field.field_name + ).to_python(field_value) return obj_pk else: field_value = getInnerText(node).strip() - return model._meta.get_field(field.remote_field.field_name).to_python(field_value) + return model._meta.get_field(field.remote_field.field_name).to_python( + field_value + ) def _handle_m2m_field_node(self, node, field): """ @@ -288,23 +325,31 @@ class Deserializer(base.Deserializer): """ model = field.remote_field.model default_manager = model._default_manager - if hasattr(default_manager, 'get_by_natural_key'): + if hasattr(default_manager, "get_by_natural_key"): + def m2m_convert(n): - keys = n.getElementsByTagName('natural') + keys = n.getElementsByTagName("natural") if keys: # If there are 'natural' subelements, it must be a natural key field_value = [getInnerText(k).strip() for k in keys] - obj_pk = default_manager.db_manager(self.db).get_by_natural_key(*field_value).pk + obj_pk = ( + default_manager.db_manager(self.db) + .get_by_natural_key(*field_value) + .pk + ) else: # Otherwise, treat like a normal PK value. - obj_pk = model._meta.pk.to_python(n.getAttribute('pk')) + obj_pk = model._meta.pk.to_python(n.getAttribute("pk")) return obj_pk + else: + def m2m_convert(n): - return model._meta.pk.to_python(n.getAttribute('pk')) + return model._meta.pk.to_python(n.getAttribute("pk")) + values = [] try: - for c in node.getElementsByTagName('object'): + for c in node.getElementsByTagName("object"): values.append(m2m_convert(c)) except Exception as e: if isinstance(e, ObjectDoesNotExist) and self.handle_forward_references: @@ -323,13 +368,15 @@ class Deserializer(base.Deserializer): if not model_identifier: raise base.DeserializationError( "<%s> node is missing the required '%s' attribute" - % (node.nodeName, attr)) + % (node.nodeName, attr) + ) try: return apps.get_model(model_identifier) except (LookupError, TypeError): raise base.DeserializationError( "<%s> node has invalid model identifier: '%s'" - % (node.nodeName, model_identifier)) + % (node.nodeName, model_identifier) + ) def getInnerText(node): @@ -337,7 +384,10 @@ def getInnerText(node): # inspired by https://mail.python.org/pipermail/xml-sig/2005-March/011022.html inner_text = [] for child in node.childNodes: - if child.nodeType == child.TEXT_NODE or child.nodeType == child.CDATA_SECTION_NODE: + if ( + child.nodeType == child.TEXT_NODE + or child.nodeType == child.CDATA_SECTION_NODE + ): inner_text.append(child.data) elif child.nodeType == child.ELEMENT_NODE: inner_text.extend(getInnerText(child)) @@ -355,6 +405,7 @@ class DefusedExpatParser(_ExpatParser): Forbid DTDs, external entity references """ + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setFeature(handler.feature_external_ges, False) @@ -363,8 +414,9 @@ class DefusedExpatParser(_ExpatParser): def start_doctype_decl(self, name, sysid, pubid, has_internal_subset): raise DTDForbidden(name, sysid, pubid) - def entity_decl(self, name, is_parameter_entity, value, base, - sysid, pubid, notation_name): + def entity_decl( + self, name, is_parameter_entity, value, base, sysid, pubid, notation_name + ): raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name) def unparsed_entity_decl(self, name, base, sysid, pubid, notation_name): @@ -385,12 +437,14 @@ class DefusedExpatParser(_ExpatParser): class DefusedXmlException(ValueError): """Base exception.""" + def __repr__(self): return str(self) class DTDForbidden(DefusedXmlException): """Document type definition is forbidden.""" + def __init__(self, name, sysid, pubid): super().__init__() self.name = name @@ -404,6 +458,7 @@ class DTDForbidden(DefusedXmlException): class EntitiesForbidden(DefusedXmlException): """Entity definition is forbidden.""" + def __init__(self, name, value, base, sysid, pubid, notation_name): super().__init__() self.name = name @@ -420,6 +475,7 @@ class EntitiesForbidden(DefusedXmlException): class ExternalReferenceForbidden(DefusedXmlException): """Resolving an external reference is forbidden.""" + def __init__(self, context, base, sysid, pubid): super().__init__() self.context = context |
