mirror of
https://github.com/django/django.git
synced 2025-01-11 02:46:13 +00:00
Fixed #28324 -- Made feedgenerators write feeds with deterministically ordered attributes.
This commit is contained in:
parent
335a8d7895
commit
d0f59054d0
@ -2,7 +2,6 @@
|
|||||||
XML serializer.
|
XML serializer.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from collections import OrderedDict
|
|
||||||
from xml.dom import pulldom
|
from xml.dom import pulldom
|
||||||
from xml.sax import handler
|
from xml.sax import handler
|
||||||
from xml.sax.expatreader import ExpatParser as _ExpatParser
|
from xml.sax.expatreader import ExpatParser as _ExpatParser
|
||||||
@ -47,7 +46,7 @@ class Serializer(base.Serializer):
|
|||||||
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)
|
self.indent(1)
|
||||||
attrs = OrderedDict([("model", str(obj._meta))])
|
attrs = {'model': str(obj._meta)}
|
||||||
if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'):
|
if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'):
|
||||||
obj_pk = obj.pk
|
obj_pk = obj.pk
|
||||||
if obj_pk is not None:
|
if obj_pk is not None:
|
||||||
@ -68,10 +67,10 @@ class Serializer(base.Serializer):
|
|||||||
ManyToManyFields).
|
ManyToManyFields).
|
||||||
"""
|
"""
|
||||||
self.indent(2)
|
self.indent(2)
|
||||||
self.xml.startElement("field", OrderedDict([
|
self.xml.startElement('field', {
|
||||||
("name", field.name),
|
'name': field.name,
|
||||||
("type", field.get_internal_type()),
|
'type': field.get_internal_type(),
|
||||||
]))
|
})
|
||||||
|
|
||||||
# Get a "string version" of the object's data.
|
# Get a "string version" of the object's data.
|
||||||
if getattr(obj, field.name) is not None:
|
if getattr(obj, field.name) is not None:
|
||||||
@ -140,11 +139,11 @@ class Serializer(base.Serializer):
|
|||||||
def _start_relational_field(self, field):
|
def _start_relational_field(self, field):
|
||||||
"""Output the <field> element for relational fields."""
|
"""Output the <field> element for relational fields."""
|
||||||
self.indent(2)
|
self.indent(2)
|
||||||
self.xml.startElement("field", OrderedDict([
|
self.xml.startElement('field', {
|
||||||
("name", field.name),
|
'name': field.name,
|
||||||
("rel", field.remote_field.__class__.__name__),
|
'rel': field.remote_field.__class__.__name__,
|
||||||
("to", str(field.remote_field.model._meta)),
|
'to': str(field.remote_field.model._meta),
|
||||||
]))
|
})
|
||||||
|
|
||||||
|
|
||||||
class Deserializer(base.Deserializer):
|
class Deserializer(base.Deserializer):
|
||||||
|
@ -3,6 +3,7 @@ Utilities for XML generation/parsing.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
from collections import OrderedDict
|
||||||
from xml.sax.saxutils import XMLGenerator
|
from xml.sax.saxutils import XMLGenerator
|
||||||
|
|
||||||
|
|
||||||
@ -26,3 +27,8 @@ class SimplerXMLGenerator(XMLGenerator):
|
|||||||
# See http://www.w3.org/International/questions/qa-controls
|
# See http://www.w3.org/International/questions/qa-controls
|
||||||
raise UnserializableContentError("Control characters are not supported in XML 1.0")
|
raise UnserializableContentError("Control characters are not supported in XML 1.0")
|
||||||
XMLGenerator.characters(self, content)
|
XMLGenerator.characters(self, content)
|
||||||
|
|
||||||
|
def startElement(self, name, attrs):
|
||||||
|
# Sort attrs for a deterministic output.
|
||||||
|
sorted_attrs = OrderedDict(sorted(attrs.items())) if attrs else attrs
|
||||||
|
super().startElement(name, sorted_attrs)
|
||||||
|
@ -126,6 +126,11 @@ class FeedgeneratorTest(unittest.TestCase):
|
|||||||
feed.add_item('item_title', 'item_link', 'item_description')
|
feed.add_item('item_title', 'item_link', 'item_description')
|
||||||
feed.writeString('utf-8')
|
feed.writeString('utf-8')
|
||||||
|
|
||||||
|
def test_deterministic_attribute_order(self):
|
||||||
|
feed = feedgenerator.Atom1Feed('title', '/link/', 'desc')
|
||||||
|
feed_content = feed.writeString('utf-8')
|
||||||
|
self.assertIn('href="/link/" rel="alternate"', feed_content)
|
||||||
|
|
||||||
|
|
||||||
class FeedgeneratorDBTest(TestCase):
|
class FeedgeneratorDBTest(TestCase):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user