Django

Code

Ticket #2650: base.py

File base.py, 4.7 kB (added by ben.khoo@calytrix.com, 2 years ago)

A potential patch

Line 
1 """
2 Module for abstract serializer/unserializer base classes.
3 """
4
5 try:
6     from cStringIO import StringIO
7 except ImportError:
8     from StringIO import StringIO
9 from django.db import models
10
11 class SerializationError(Exception):
12     """Something bad happened during serialization."""
13     pass
14
15 class DeserializationError(Exception):
16     """Something bad happened during deserialization."""
17     pass
18
19 class Serializer(object):
20     """
21     Abstract serializer base class.
22     """
23
24     def serialize(self, queryset, **options):
25         """
26         Serialize a queryset.
27         """
28         self.options = options
29
30         self.stream = options.get("stream", StringIO())
31
32         self.start_serialization()
33         for obj in queryset:
34             self.start_object(obj)
35             for field in obj._meta.fields:
36                 if field is obj._meta.pk:
37                     continue
38                 elif field.rel is None:
39                     self.handle_field(obj, field)
40                 else:
41                     self.handle_fk_field(obj, field)
42             for field in obj._meta.many_to_many:
43                 self.handle_m2m_field(obj, field)
44             self.end_object(obj)
45         self.end_serialization()
46         return self.getvalue()
47
48     def get_string_value(self, obj, field):
49         """
50         Convert a field's value to a string.
51         """
52         if isinstance(field, models.DateTimeField):
53             value = getattr(obj, field.name)
54             if value is None:
55                 value = ''
56             else:
57                 value = value.strftime("%Y-%m-%d %H:%M:%S")
58         elif isinstance(field, models.FileField):
59             value = getattr(obj, "get_%s_url" % field.name, lambda: None)()
60         else:
61             value = field.flatten_data(follow=None, obj=obj).get(field.name, "")
62
63         if value is not None:
64             return str(value)
65         else:
66             return None
67
68     def start_serialization(self):
69         """
70         Called when serializing of the queryset starts.
71         """
72         raise NotImplementedError
73
74     def end_serialization(self):
75         """
76         Called when serializing of the queryset ends.
77         """
78         pass
79
80     def start_object(self, obj):
81         """
82         Called when serializing of an object starts.
83         """
84         raise NotImplementedError
85
86     def end_object(self, obj):
87         """
88         Called when serializing of an object ends.
89         """
90         pass
91
92     def handle_field(self, obj, field):
93         """
94         Called to handle each individual (non-relational) field on an object.
95         """
96         raise NotImplementedError
97
98     def handle_fk_field(self, obj, field):
99         """
100         Called to handle a ForeignKey field.
101         """
102         raise NotImplementedError
103
104     def handle_m2m_field(self, obj, field):
105         """
106         Called to handle a ManyToManyField.
107         """
108         raise NotImplementedError
109
110     def getvalue(self):
111         """
112         Return the fully serialized queryset.
113         """
114         return self.stream.getvalue()
115
116 class Deserializer(object):
117     """
118     Abstract base deserializer class.
119     """
120
121     def __init__(self, stream_or_string, **options):
122         """
123         Init this serializer given a stream or a string
124         """
125         self.options = options
126         if isinstance(stream_or_string, basestring):
127             self.stream = StringIO(stream_or_string)
128         else:
129             self.stream = stream_or_string
130         # hack to make sure that the models have all been loaded before
131         # deserialization starts (otherwise subclass calls to get_model()
132         # and friends might fail...)
133         models.get_apps()
134
135     def __iter__(self):
136         return self
137
138     def next(self):
139         """Iteration iterface -- return the next item in the stream"""
140         raise NotImplementedError
141
142 class DeserializedObject(object):
143     """
144     A deserialzed model.
145
146     Basically a container for holding the pre-saved deserialized data along
147     with the many-to-many data saved with the object.
148
149     Call ``save()`` to save the object (with the many-to-many data) to the
150     database; call ``save(save_m2m=False)`` to save just the object fields
151     (and not touch the many-to-many stuff.)
152     """
153
154     def __init__(self, obj, m2m_data=None):
155         self.object = obj
156         self.m2m_data = m2m_data
157
158     def __repr__(self):
159         return "<DeserializedObject: %s>" % str(self.object)
160
161     def save(self, save_m2m=True):
162         self.object.save()
163         if self.m2m_data and save_m2m:
164             for accessor_name, object_list in self.m2m_data.items():
165                 setattr(self.object, accessor_name, object_list)
166
167         # prevent a second (possibly accidental) call to save() from saving
168         # the m2m data twice.
169         self.m2m_data = None