Ticket #2650: base.py

File base.py, 4.7 KB (added by ben.khoo@…, 13 years ago)

A potential patch

Line 
1"""
2Module for abstract serializer/unserializer base classes.
3"""
4
5try:
6    from cStringIO import StringIO
7except ImportError:
8    from StringIO import StringIO
9from django.db import models
10
11class SerializationError(Exception):
12    """Something bad happened during serialization."""
13    pass
14
15class DeserializationError(Exception):
16    """Something bad happened during deserialization."""
17    pass
18
19class 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
116class 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
142class 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
Back to Top