mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #28324 -- Made feedgenerators write feeds with deterministically ordered attributes.
This commit is contained in:
		
				
					committed by
					
						 Tim Graham
						Tim Graham
					
				
			
			
				
	
			
			
			
						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): | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user