mirror of
				https://github.com/django/django.git
				synced 2025-10-30 17:16:10 +00:00 
			
		
		
		
	Added initial cut at serialization framework, along with some basic tests and a stab at some docs. This is all a bit rough right now, so expect some bumps.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@3225 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
		
							
								
								
									
										76
									
								
								django/core/serializers/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								django/core/serializers/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| """ | ||||
| Interfaces for serializing Django objects. | ||||
|  | ||||
| Usage:: | ||||
|  | ||||
|     >>> from django.core import serializers | ||||
|     >>> json = serializers.serialize("json", some_query_set) | ||||
|     >>> objects = list(serializers.deserialize("json", json)) | ||||
|          | ||||
| To add your own serializers, use the SERIALIZATION_MODULES setting:: | ||||
|  | ||||
|     SERIALIZATION_MODULES = { | ||||
|         "csv" : "path.to.csv.serializer", | ||||
|         "txt" : "path.to.txt.serializer", | ||||
|     } | ||||
|  | ||||
| """ | ||||
|  | ||||
| from django.conf import settings | ||||
|  | ||||
| # Built-in serializers | ||||
| BUILTIN_SERIALIZERS = { | ||||
|     "xml" : "django.core.serializers.xml_serializer", | ||||
| } | ||||
|  | ||||
| _serializers = {} | ||||
|          | ||||
| def register_serializer(format, serializer_module): | ||||
|     """Register a new serializer by passing in a module name.""" | ||||
|     module = __import__(serializer_module, '', '', ['']) | ||||
|     _serializers[format] = module | ||||
|      | ||||
| def unregister_serializer(format): | ||||
|     """Unregister a given serializer""" | ||||
|     del _serializers[format] | ||||
|      | ||||
| def get_serializer(format): | ||||
|     if not _serializers: | ||||
|         _load_serializers() | ||||
|     return _serializers[format].Serializer | ||||
|      | ||||
| def get_deserializer(format): | ||||
|     if not _serializers: | ||||
|         _load_serializers() | ||||
|     return _serializers[format].Deserializer | ||||
|      | ||||
| def serialize(format, queryset, **options): | ||||
|     """ | ||||
|     Serialize a queryset (or any iterator that returns database objects) using | ||||
|     a certain serializer. | ||||
|     """ | ||||
|     s = get_serializer(format)() | ||||
|     s.serialize(queryset, **options) | ||||
|     return s.getvalue() | ||||
|  | ||||
| def deserialize(format, stream_or_string): | ||||
|     """ | ||||
|     Deserialize a stream or a string. Returns an iterator that yields ``(obj, | ||||
|     m2m_relation_dict)``, where ``obj`` is a instantiated -- but *unsaved* -- | ||||
|     object, and ``m2m_relation_dict`` is a dictionary of ``{m2m_field_name : | ||||
|     list_of_related_objects}``. | ||||
|     """ | ||||
|     d = get_deserializer(format) | ||||
|     return d(stream_or_string) | ||||
|  | ||||
| def _load_serializers(): | ||||
|     """ | ||||
|     Register built-in and settings-defined serializers. This is done lazily so | ||||
|     that user code has a chance to (e.g.) set up custom settings without | ||||
|     needing to be careful of import order. | ||||
|     """ | ||||
|     for format in BUILTIN_SERIALIZERS: | ||||
|         register_serializer(format, BUILTIN_SERIALIZERS[format]) | ||||
|     if hasattr(settings, "SERIALIZATION_MODULES"): | ||||
|         for format in settings.SERIALIZATION_MODULES: | ||||
|             register_serializer(format, settings.SERIALIZATION_MODULES[format]) | ||||
							
								
								
									
										159
									
								
								django/core/serializers/base.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								django/core/serializers/base.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,159 @@ | ||||
| """ | ||||
| Module for abstract serializer/unserializer base classes. | ||||
| """ | ||||
|  | ||||
| try: | ||||
|     from cStringIO import StringIO | ||||
| except ImportError: | ||||
|     from StringIO import StringIO | ||||
| from django.db import models | ||||
|  | ||||
| class SerializationError(Exception): | ||||
|     """Something bad happened during serialization.""" | ||||
|     pass | ||||
|      | ||||
| class DeserializationError(Exception): | ||||
|     """Something bad happened during deserialization.""" | ||||
|     pass | ||||
|  | ||||
| class Serializer(object): | ||||
|     """ | ||||
|     Abstract serializer base class. | ||||
|     """ | ||||
|      | ||||
|     def serialize(self, queryset, **options): | ||||
|         """ | ||||
|         Serialize a queryset. | ||||
|         """ | ||||
|         self.options = options | ||||
|          | ||||
|         self.stream = options.get("stream", StringIO()) | ||||
|          | ||||
|         self.start_serialization() | ||||
|         for obj in queryset: | ||||
|             self.start_object(obj) | ||||
|             for field in obj._meta.fields: | ||||
|                 if field.rel is None: | ||||
|                     self.handle_field(obj, field) | ||||
|                 else: | ||||
|                     self.handle_fk_field(obj, field) | ||||
|             for field in obj._meta.many_to_many: | ||||
|                 self.handle_m2m_field(obj, field) | ||||
|             self.end_object(obj) | ||||
|         self.end_serialization() | ||||
|         return self.getvalue() | ||||
|      | ||||
|     def get_string_value(self, obj, field): | ||||
|         """ | ||||
|         Convert a field's value to a string. | ||||
|         """ | ||||
|         if isinstance(field, models.DateTimeField): | ||||
|             value = getattr(obj, field.name).strftime("%Y-%m-%d %H:%M:%S") | ||||
|         elif isinstance(field, models.FileField): | ||||
|             value = getattr(obj, "get_%s_url" % field.name, lambda: None)() | ||||
|         else: | ||||
|             value = field.flatten_data(follow=None, obj=obj).get(field.name, "") | ||||
|         return str(value) | ||||
|      | ||||
|     def start_serialization(self): | ||||
|         """ | ||||
|         Called when serializing of the queryset starts. | ||||
|         """ | ||||
|         raise NotImplementedError | ||||
|      | ||||
|     def end_serialization(self): | ||||
|         """ | ||||
|         Called when serializing of the queryset ends. | ||||
|         """ | ||||
|         pass | ||||
|      | ||||
|     def start_object(self, obj): | ||||
|         """ | ||||
|         Called when serializing of an object starts. | ||||
|         """ | ||||
|         raise NotImplementedError | ||||
|      | ||||
|     def end_object(self, obj): | ||||
|         """ | ||||
|         Called when serializing of an object ends. | ||||
|         """ | ||||
|         pass | ||||
|      | ||||
|     def handle_field(self, obj, field): | ||||
|         """ | ||||
|         Called to handle each individual (non-relational) field on an object. | ||||
|         """ | ||||
|         raise NotImplementedError | ||||
|      | ||||
|     def handle_fk_field(self, obj, field): | ||||
|         """ | ||||
|         Called to handle a ForeignKey field. | ||||
|         """ | ||||
|         raise NotImplementedError | ||||
|      | ||||
|     def handle_m2m_field(self, obj, field): | ||||
|         """ | ||||
|         Called to handle a ManyToManyField. | ||||
|         """ | ||||
|         raise NotImplementedError | ||||
|      | ||||
|     def getvalue(self): | ||||
|         """ | ||||
|         Return the fully serialized queryset. | ||||
|         """ | ||||
|         return self.stream.getvalue() | ||||
|  | ||||
| class Deserializer(object): | ||||
|     """ | ||||
|     Abstract base deserializer class. | ||||
|     """ | ||||
|      | ||||
|     def __init__(self, stream_or_string, **options): | ||||
|         """ | ||||
|         Init this serializer given a stream or a string | ||||
|         """ | ||||
|         self.options = options | ||||
|         if isinstance(stream_or_string, basestring): | ||||
|             self.stream = StringIO(stream_or_string) | ||||
|         else: | ||||
|             self.stream = stream_or_string | ||||
|         # hack to make sure that the models have all been loaded before | ||||
|         # deserialization starts (otherwise subclass calls to get_model() | ||||
|         # and friends might fail...) | ||||
|         models.get_apps() | ||||
|      | ||||
|     def __iter__(self): | ||||
|         return self | ||||
|      | ||||
|     def next(self): | ||||
|         """Iteration iterface -- return the next item in the stream""" | ||||
|         raise NotImplementedError | ||||
|          | ||||
| class DeserializedObject(object): | ||||
|     """ | ||||
|     A deserialzed model. | ||||
|      | ||||
|     Basically a container for holding the pre-saved deserialized data along | ||||
|     with the many-to-many data saved with the object. | ||||
|      | ||||
|     Call ``save()`` to save the object (with the many-to-many data) to the | ||||
|     database; call ``save(save_m2m=False)`` to save just the object fields | ||||
|     (and not touch the many-to-many stuff.) | ||||
|     """ | ||||
|      | ||||
|     def __init__(self, obj, m2m_data=None): | ||||
|         self.object = obj | ||||
|         self.m2m_data = m2m_data | ||||
|          | ||||
|     def __repr__(self): | ||||
|         return "<DeserializedObject: %s>" % str(self.object) | ||||
|          | ||||
|     def save(self, save_m2m=True): | ||||
|         self.object.save() | ||||
|         if self.m2m_data and save_m2m: | ||||
|             for accessor_name, object_list in self.m2m_data.items(): | ||||
|                 setattr(self.object, accessor_name, object_list) | ||||
|          | ||||
|         # prevent a second (possibly accidental) call to save() from saving  | ||||
|         # the m2m data twice. | ||||
|         self.m2m_data = None | ||||
							
								
								
									
										218
									
								
								django/core/serializers/xml_serializer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								django/core/serializers/xml_serializer.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,218 @@ | ||||
| """ | ||||
| XML serializer. | ||||
| """ | ||||
|  | ||||
| from xml.dom import pulldom | ||||
| from django.utils.xmlutils import SimplerXMLGenerator | ||||
| from django.core.serializers import base | ||||
| from django.db import models | ||||
|  | ||||
| class Serializer(base.Serializer): | ||||
|     """ | ||||
|     Serializes a QuerySet to XML. | ||||
|     """ | ||||
|      | ||||
|     def start_serialization(self): | ||||
|         """ | ||||
|         Start serialization -- open the XML document and the root element. | ||||
|         """ | ||||
|         self.xml = SimplerXMLGenerator(self.stream, self.options.get("encoding", "utf-8")) | ||||
|         self.xml.startDocument() | ||||
|         self.xml.startElement("django-objects", {"version" : "1.0"}) | ||||
|          | ||||
|     def end_serialization(self): | ||||
|         """ | ||||
|         End serialization -- end the document. | ||||
|         """ | ||||
|         self.xml.endElement("django-objects") | ||||
|         self.xml.endDocument() | ||||
|          | ||||
|     def start_object(self, obj): | ||||
|         """ | ||||
|         Called as each object is handled. | ||||
|         """ | ||||
|         if not hasattr(obj, "_meta"): | ||||
|             raise base.SerializationError("Non-model object (%s) encountered during serialization" % type(obj)) | ||||
|              | ||||
|         self.xml.startElement("object", { | ||||
|             "pk"    : str(obj._get_pk_val()), | ||||
|             "model" : str(obj._meta), | ||||
|         }) | ||||
|          | ||||
|     def end_object(self, obj): | ||||
|         """ | ||||
|         Called after handling all fields for an object. | ||||
|         """ | ||||
|         self.xml.endElement("object") | ||||
|          | ||||
|     def handle_field(self, obj, field): | ||||
|         """ | ||||
|         Called to handle each field on an object (except for ForeignKeys and | ||||
|         ManyToManyFields) | ||||
|         """ | ||||
|         self.xml.startElement("field", { | ||||
|             "name" : field.name, | ||||
|             "type" : field.get_internal_type() | ||||
|         }) | ||||
|          | ||||
|         # Get a "string version" of the object's data (this is handled by the | ||||
|         # serializer base class).  None is handled specially. | ||||
|         value = self.get_string_value(obj, field) | ||||
|         if value is None: | ||||
|             self.xml.addQuickElement("None") | ||||
|         else: | ||||
|             self.xml.characters(str(value)) | ||||
|  | ||||
|         self.xml.endElement("field") | ||||
|          | ||||
|     def handle_fk_field(self, obj, field): | ||||
|         """ | ||||
|         Called to handle a ForeignKey (we need to treat them slightly | ||||
|         differently from regular fields). | ||||
|         """ | ||||
|         self._start_relational_field(field) | ||||
|         related = getattr(obj, field.name) | ||||
|         if related is not None: | ||||
|             self.xml.characters(str(related._get_pk_val())) | ||||
|         else: | ||||
|             self.xml.addQuickElement("None") | ||||
|         self.xml.endElement("field") | ||||
|      | ||||
|     def handle_m2m_field(self, obj, field): | ||||
|         """ | ||||
|         Called to handle a ManyToManyField. Related objects are only | ||||
|         serialized as references to the object's PK (i.e. the related *data* | ||||
|         is not dumped, just the relation). | ||||
|         """ | ||||
|         self._start_relational_field(field) | ||||
|         for relobj in getattr(obj, field.name).iterator(): | ||||
|             self.xml.addQuickElement("object", attrs={"pk" : str(relobj._get_pk_val())}) | ||||
|         self.xml.endElement("field") | ||||
|          | ||||
|     def _start_relational_field(self, field): | ||||
|         """ | ||||
|         Helper to output the <field> element for relational fields | ||||
|         """ | ||||
|         self.xml.startElement("field", { | ||||
|             "name" : field.name, | ||||
|             "rel"  : field.rel.__class__.__name__, | ||||
|             "to"   : str(field.rel.to._meta), | ||||
|         }) | ||||
|          | ||||
| class Deserializer(base.Deserializer): | ||||
|     """ | ||||
|     Deserialize XML. | ||||
|     """ | ||||
|      | ||||
|     def __init__(self, stream_or_string, **options): | ||||
|         super(Deserializer, self).__init__(stream_or_string, **options) | ||||
|         self.encoding = self.options.get("encoding", "utf-8") | ||||
|         self.event_stream = pulldom.parse(self.stream)  | ||||
|      | ||||
|     def next(self): | ||||
|         for event, node in self.event_stream: | ||||
|             if event == "START_ELEMENT" and node.nodeName == "object": | ||||
|                 self.event_stream.expandNode(node) | ||||
|                 return self._handle_object(node) | ||||
|         raise StopIteration | ||||
|          | ||||
|     def _handle_object(self, node): | ||||
|         """ | ||||
|         Convert an <object> node to a DeserializedObject. | ||||
|         """ | ||||
|         # Look up the model using the model loading mechanism. If this fails, bail. | ||||
|         Model = self._get_model_from_node(node, "model") | ||||
|          | ||||
|         # Start building a data dictionary from the object.  If the node is | ||||
|         # missing the pk attribute, bail. | ||||
|         pk = node.getAttribute("pk") | ||||
|         if not pk: | ||||
|             raise base.DeserializationError("<object> node is missing the 'pk' attribute") | ||||
|         data = {Model._meta.pk.name : pk} | ||||
|          | ||||
|         # Also start building a dict of m2m data (this is saved as | ||||
|         # {m2m_accessor_attribute : [list_of_related_objects]}) | ||||
|         m2m_data = {} | ||||
|          | ||||
|         # Deseralize each field. | ||||
|         for field_node in node.getElementsByTagName("field"): | ||||
|             # If the field is missing the name attribute, bail (are you | ||||
|             # sensing a pattern here?) | ||||
|             field_name = field_node.getAttribute("name") | ||||
|             if not field_name: | ||||
|                 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 | ||||
|             # be propagated correctly. | ||||
|             field = Model._meta.get_field(field_name) | ||||
|              | ||||
|             # As is usually the case, relation fields get the special treatment. | ||||
|             if field.rel and isinstance(field.rel, models.ManyToManyRel): | ||||
|                 m2m_data[field.name] = self._handle_m2m_field_node(field_node) | ||||
|             elif field.rel and isinstance(field.rel, models.ManyToOneRel): | ||||
|                 data[field.name] = self._handle_fk_field_node(field_node) | ||||
|             else: | ||||
|                 value = field.to_python(getInnerText(field_node).strip().encode(self.encoding)) | ||||
|                 data[field.name] = value | ||||
|          | ||||
|         # Return a DeserializedObject so that the m2m data has a place to live. | ||||
|         return base.DeserializedObject(Model(**data), m2m_data) | ||||
|          | ||||
|     def _handle_fk_field_node(self, node): | ||||
|         """ | ||||
|         Handle a <field> node for a ForeignKey | ||||
|         """ | ||||
|         # Try to set the foreign key by looking up the foreign related object. | ||||
|         # If it doesn't exist, set the field to None (which might trigger  | ||||
|         # validation error, but that's expected). | ||||
|         RelatedModel = self._get_model_from_node(node, "to") | ||||
|         return RelatedModel.objects.get(pk=getInnerText(node).strip().encode(self.encoding)) | ||||
|          | ||||
|     def _handle_m2m_field_node(self, node): | ||||
|         """ | ||||
|         Handle a <field> node for a ManyToManyField | ||||
|         """ | ||||
|         # Load the related model | ||||
|         RelatedModel = self._get_model_from_node(node, "to") | ||||
|          | ||||
|         # Look up all the related objects. Using the in_bulk() lookup ensures | ||||
|         # that missing related objects don't cause an exception | ||||
|         related_ids = [c.getAttribute("pk").encode(self.encoding) for c in node.getElementsByTagName("object")] | ||||
|         return RelatedModel._default_manager.in_bulk(related_ids).values() | ||||
|      | ||||
|     def _get_model_from_node(self, node, attr): | ||||
|         """ | ||||
|         Helper to look up a model from a <object model=...> or a <field | ||||
|         rel=... to=...> node. | ||||
|         """ | ||||
|         model_identifier = node.getAttribute(attr) | ||||
|         if not model_identifier: | ||||
|             raise base.DeserializationError( | ||||
|                 "<%s> node is missing the required '%s' attribute" \ | ||||
|                     % (node.nodeName, attr)) | ||||
|         try: | ||||
|             Model = models.get_model(*model_identifier.split(".")) | ||||
|         except TypeError: | ||||
|             Model = None | ||||
|         if Model is None: | ||||
|             raise base.DeserializationError( | ||||
|                 "<%s> node has invalid model identifier: '%s'" % \ | ||||
|                     (node.nodeName, model_identifier)) | ||||
|         return Model | ||||
|          | ||||
|      | ||||
| def getInnerText(node): | ||||
|     """ | ||||
|     Get all the inner text of a DOM node (recursively). | ||||
|     """ | ||||
|     # inspired by http://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: | ||||
|             inner_text.append(child.data) | ||||
|         elif child.nodeType == child.ELEMENT_NODE: | ||||
|             inner_text.extend(getInnerText(child)) | ||||
|         else: | ||||
|            pass | ||||
|     return "".join(inner_text) | ||||
							
								
								
									
										85
									
								
								docs/serialization.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								docs/serialization.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | ||||
| ========================== | ||||
| Serializing Django objects | ||||
| ========================== | ||||
|  | ||||
| .. note:: | ||||
|      | ||||
|     This API is currently under heavy development and may change -- | ||||
|     perhaps drastically -- in the future. | ||||
|      | ||||
|     You have been warned. | ||||
|      | ||||
| Django's serialization framework provides a mechanism for "translating" Django | ||||
| objects into other formats. Usually these other formats will be text-based and | ||||
| used for sending Django objects over a wire, but it's possible for a | ||||
| serializer to handle any format (text-based or not). | ||||
|  | ||||
| Serializing data | ||||
| ---------------- | ||||
|  | ||||
| At the highest level, serializing data is a very simple operation:: | ||||
|  | ||||
|     from django.core import serializers | ||||
|     data = serializers.serialize("xml", SomeModel.objects.all()) | ||||
|      | ||||
| The arguments to the ``serialize`` function are the format to serialize the | ||||
| data to (see `Serialization formats`_) and a QuerySet_ to serialize. | ||||
| (Actually, the second argument can be any iterator that yields Django objects, | ||||
| but it'll almost always be a QuerySet). | ||||
|  | ||||
| .. _QuerySet: ../db_api/#retrieving-objects | ||||
|  | ||||
| You can also use a serializer object directly:: | ||||
|  | ||||
|     xml_serializer = serializers.get_serializer("xml") | ||||
|     xml_serializer.serialize(queryset) | ||||
|     data = xml_serializer.getvalue() | ||||
|      | ||||
| This is useful if you want to serialize data directly to a file-like object | ||||
| (which includes a HTTPResponse_):: | ||||
|  | ||||
|     out = open("file.xml", "w") | ||||
|     xml_serializer.serialize(SomeModel.objects.all(), stream=out) | ||||
|  | ||||
| .. _HTTPResponse: ../request_response/#httpresponse-objects | ||||
|  | ||||
| Deserializing data | ||||
| ------------------ | ||||
|  | ||||
| Deserializing data is also a fairly simple operation:: | ||||
|  | ||||
|     for obj in serializers.deserialize("xml", data): | ||||
|         do_something_with(obj) | ||||
|      | ||||
| As you can see, the ``deserialize`` function takes the same format argument as | ||||
| ``serialize``, a string or stream of data, and returns an iterator. | ||||
|  | ||||
| However, here it gets slightly complicated. The objects returned by the | ||||
| ``deserialize`` iterator *aren't* simple Django objects. Instead, they are | ||||
| special ``DeserializedObject`` instances that wrap a created -- but unsaved -- | ||||
| object and any associated relationship data. | ||||
|  | ||||
| Calling ``DeserializedObject.save()`` saves the object to the database. | ||||
|  | ||||
| This ensures that deserializing is a non-destructive operation even if the | ||||
| data in your serialized representation doesn't match what's currently in the | ||||
| database. Usually, working with these ``DeserializedObject`` instances looks | ||||
| something like:: | ||||
|  | ||||
|     for deserialized_object in serializers.deserialize("xml", data): | ||||
|         if object_should_be_saved(deserialized_object): | ||||
|             obj.save() | ||||
|              | ||||
| In other words, the usual use is to examine the deserialized objects to make | ||||
| sure that they are "appropriate" for saving before doing so.  Of course, if you trust your data source you could just save the object and move on. | ||||
|  | ||||
| The Django object itself can be inspected as ``deserialized_object.object``. | ||||
|  | ||||
| Serialization formats | ||||
| --------------------- | ||||
|  | ||||
| Django "ships" with a few included serializers, and there's a simple API for creating and registering your own... | ||||
|  | ||||
| .. note:: | ||||
|  | ||||
|     ... which will be documented once the API is stable :) | ||||
							
								
								
									
										0
									
								
								tests/modeltests/serializers/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/modeltests/serializers/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										94
									
								
								tests/modeltests/serializers/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								tests/modeltests/serializers/models.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| """ | ||||
| XXX. Serialization | ||||
|  | ||||
| ``django.core.serializers`` provides interfaces to converting Django querysets | ||||
| to and from "flat" data (i.e. strings). | ||||
| """ | ||||
|  | ||||
| from django.db import models | ||||
|  | ||||
| class Category(models.Model): | ||||
|     name = models.CharField(maxlength=20) | ||||
|  | ||||
|     class Meta: | ||||
|        ordering = ('name',) | ||||
|  | ||||
|     def __str__(self): | ||||
|         return self.name | ||||
|  | ||||
| class Author(models.Model): | ||||
|     name = models.CharField(maxlength=20) | ||||
|  | ||||
|     class Meta: | ||||
|         ordering = ('name',) | ||||
|      | ||||
|     def __str__(self): | ||||
|         return self.name | ||||
|  | ||||
| class Article(models.Model): | ||||
|     author = models.ForeignKey(Author) | ||||
|     headline = models.CharField(maxlength=50) | ||||
|     pub_date = models.DateTimeField() | ||||
|     categories = models.ManyToManyField(Category) | ||||
|  | ||||
|     class Meta: | ||||
|        ordering = ('pub_date',) | ||||
|  | ||||
|     def __str__(self): | ||||
|         return self.headline | ||||
|  | ||||
| API_TESTS = """ | ||||
| # Create some data: | ||||
| >>> from datetime import datetime | ||||
| >>> sports = Category(name="Sports") | ||||
| >>> music = Category(name="Music") | ||||
| >>> op_ed = Category(name="Op-Ed") | ||||
| >>> sports.save(); music.save(); op_ed.save() | ||||
|  | ||||
| >>> joe = Author(name="Joe") | ||||
| >>> jane = Author(name="Jane") | ||||
| >>> joe.save(); jane.save() | ||||
|  | ||||
| >>> a1 = Article( | ||||
| ...     author = jane, | ||||
| ...     headline = "Poker has no place on ESPN", | ||||
| ...     pub_date = datetime(2006, 6, 16, 11, 00)) | ||||
| >>> a2 = Article( | ||||
| ...     author = joe, | ||||
| ...     headline = "Time to reform copyright", | ||||
| ...     pub_date = datetime(2006, 6, 16, 13, 00)) | ||||
| >>> a1.save(); a2.save() | ||||
| >>> a1.categories = [sports, op_ed] | ||||
| >>> a2.categories = [music, op_ed] | ||||
|  | ||||
| # Serialize a queryset to XML | ||||
| >>> from django.core import serializers | ||||
| >>> xml = serializers.serialize("xml", Article.objects.all()) | ||||
|  | ||||
| # The output is valid XML | ||||
| >>> from xml.dom import minidom | ||||
| >>> dom = minidom.parseString(xml) | ||||
|  | ||||
| # Deserializing has a similar interface, except that special DeserializedObject | ||||
| # instances are returned.  This is because data might have changed in the  | ||||
| # database since the data was serialized (we'll simulate that below). | ||||
| >>> for obj in serializers.deserialize("xml", xml): | ||||
| ...     print obj | ||||
| <DeserializedObject: Poker has no place on ESPN> | ||||
| <DeserializedObject: Time to reform copyright> | ||||
|  | ||||
| # Deserializing data with different field values doesn't change anything in the | ||||
| # database until we call save(): | ||||
| >>> xml = xml.replace("Poker has no place on ESPN", "Poker has no place on television") | ||||
| >>> objs = list(serializers.deserialize("xml", xml)) | ||||
|  | ||||
| # Even those I deserialized, the database hasn't been touched | ||||
| >>> Article.objects.all() | ||||
| [<Article: Poker has no place on ESPN>, <Article: Time to reform copyright>] | ||||
|  | ||||
| # But when I save, the data changes as you might except. | ||||
| >>> objs[0].save() | ||||
| >>> Article.objects.all() | ||||
| [<Article: Poker has no place on television>, <Article: Time to reform copyright>] | ||||
|  | ||||
| """ | ||||
		Reference in New Issue
	
	Block a user