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. | ||||
| """ | ||||
|  | ||||
| from collections import OrderedDict | ||||
| from xml.dom import pulldom | ||||
| from xml.sax import handler | ||||
| 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)) | ||||
|  | ||||
|         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'): | ||||
|             obj_pk = obj.pk | ||||
|             if obj_pk is not None: | ||||
| @@ -68,10 +67,10 @@ class Serializer(base.Serializer): | ||||
|         ManyToManyFields). | ||||
|         """ | ||||
|         self.indent(2) | ||||
|         self.xml.startElement("field", OrderedDict([ | ||||
|             ("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: | ||||
| @@ -140,11 +139,11 @@ class Serializer(base.Serializer): | ||||
|     def _start_relational_field(self, field): | ||||
|         """Output the <field> element for relational fields.""" | ||||
|         self.indent(2) | ||||
|         self.xml.startElement("field", OrderedDict([ | ||||
|             ("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): | ||||
|   | ||||
| @@ -3,6 +3,7 @@ Utilities for XML generation/parsing. | ||||
| """ | ||||
|  | ||||
| import re | ||||
| from collections import OrderedDict | ||||
| from xml.sax.saxutils import XMLGenerator | ||||
|  | ||||
|  | ||||
| @@ -26,3 +27,8 @@ class SimplerXMLGenerator(XMLGenerator): | ||||
|             # See http://www.w3.org/International/questions/qa-controls | ||||
|             raise UnserializableContentError("Control characters are not supported in XML 1.0") | ||||
|         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.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): | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user