web/lib/django/core/serializers/python.py
author ymh <ymh.work@gmail.com>
Wed, 02 Jun 2010 18:57:35 +0200
changeset 38 77b6da96e6f1
permissions -rw-r--r--
update django
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
38
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     1
"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     2
A Python "serializer". Doesn't do much serializing per se -- just converts to
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     3
and from basic Python data types (lists, dicts, strings, etc.). Useful as a basis for
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     4
other serializers.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     5
"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     6
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     7
from django.conf import settings
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     8
from django.core.serializers import base
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     9
from django.db import models, DEFAULT_DB_ALIAS
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
from django.utils.encoding import smart_unicode, is_protected_type
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    11
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    12
class Serializer(base.Serializer):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    13
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    14
    Serializes a QuerySet to basic Python objects.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    15
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    16
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    17
    internal_use_only = True
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    18
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    19
    def start_serialization(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    20
        self._current = None
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    21
        self.objects = []
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    22
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    23
    def end_serialization(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    24
        pass
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    25
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    26
    def start_object(self, obj):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    27
        self._current = {}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    28
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    29
    def end_object(self, obj):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    30
        self.objects.append({
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
            "model"  : smart_unicode(obj._meta),
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    32
            "pk"     : smart_unicode(obj._get_pk_val(), strings_only=True),
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    33
            "fields" : self._current
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    34
        })
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    35
        self._current = None
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    36
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    37
    def handle_field(self, obj, field):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    38
        value = field._get_val_from_obj(obj)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    39
        # Protected types (i.e., primitives like None, numbers, dates,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    40
        # and Decimals) are passed through as is. All other values are
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    41
        # converted to string first.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    42
        if is_protected_type(value):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    43
            self._current[field.name] = value
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    44
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    45
            self._current[field.name] = field.value_to_string(obj)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    46
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    47
    def handle_fk_field(self, obj, field):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    48
        related = getattr(obj, field.name)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    49
        if related is not None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    50
            if self.use_natural_keys and hasattr(related, 'natural_key'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    51
                related = related.natural_key()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    52
            else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    53
                if field.rel.field_name == related._meta.pk.name:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    54
                    # Related to remote object via primary key
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    55
                    related = related._get_pk_val()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    56
                else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    57
                    # Related to remote object via other field
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    58
                    related = smart_unicode(getattr(related, field.rel.field_name), strings_only=True)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    59
        self._current[field.name] = related
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    60
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    61
    def handle_m2m_field(self, obj, field):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    62
        if field.rel.through._meta.auto_created:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    63
            if self.use_natural_keys and hasattr(field.rel.to, 'natural_key'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    64
                m2m_value = lambda value: value.natural_key()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    65
            else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
                m2m_value = lambda value: smart_unicode(value._get_pk_val(), strings_only=True)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
            self._current[field.name] = [m2m_value(related)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
                               for related in getattr(obj, field.name).iterator()]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    69
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    70
    def getvalue(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    71
        return self.objects
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    72
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    73
def Deserializer(object_list, **options):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    74
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    75
    Deserialize simple Python objects back into Django ORM instances.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    76
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    77
    It's expected that you pass the Python objects themselves (instead of a
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    78
    stream or a string) to the constructor
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    79
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    80
    db = options.pop('using', DEFAULT_DB_ALIAS)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    81
    models.get_apps()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    82
    for d in object_list:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    83
        # Look up the model and starting build a dict of data for it.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    84
        Model = _get_model(d["model"])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    85
        data = {Model._meta.pk.attname : Model._meta.pk.to_python(d["pk"])}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    86
        m2m_data = {}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    87
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    88
        # Handle each field
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    89
        for (field_name, field_value) in d["fields"].iteritems():
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
            if isinstance(field_value, str):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    91
                field_value = smart_unicode(field_value, options.get("encoding", settings.DEFAULT_CHARSET), strings_only=True)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    92
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    93
            field = Model._meta.get_field(field_name)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    94
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    95
            # Handle M2M relations
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    96
            if field.rel and isinstance(field.rel, models.ManyToManyRel):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    97
                if hasattr(field.rel.to._default_manager, 'get_by_natural_key'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    98
                    def m2m_convert(value):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    99
                        if hasattr(value, '__iter__'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   100
                            return field.rel.to._default_manager.db_manager(db).get_by_natural_key(*value).pk
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   101
                        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   102
                            return smart_unicode(field.rel.to._meta.pk.to_python(value))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   103
                else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   104
                    m2m_convert = lambda v: smart_unicode(field.rel.to._meta.pk.to_python(v))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   105
                m2m_data[field.name] = [m2m_convert(pk) for pk in field_value]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   106
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   107
            # Handle FK fields
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   108
            elif field.rel and isinstance(field.rel, models.ManyToOneRel):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   109
                if field_value is not None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   110
                    if hasattr(field.rel.to._default_manager, 'get_by_natural_key'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   111
                        if hasattr(field_value, '__iter__'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   112
                            obj = field.rel.to._default_manager.db_manager(db).get_by_natural_key(*field_value)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   113
                            value = getattr(obj, field.rel.field_name)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   114
                            # If this is a natural foreign key to an object that
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   115
                            # has a FK/O2O as the foreign key, use the FK value
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   116
                            if field.rel.to._meta.pk.rel:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   117
                                value = value.pk
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   118
                        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   119
                            value = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   120
                        data[field.attname] = value
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   121
                    else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   122
                        data[field.attname] = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   123
                else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   124
                    data[field.attname] = None
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   125
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   126
            # Handle all other fields
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   127
            else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   128
                data[field.name] = field.to_python(field_value)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   129
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   130
        yield base.DeserializedObject(Model(**data), m2m_data)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   131
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   132
def _get_model(model_identifier):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   133
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   134
    Helper to look up a model from an "app_label.module_name" string.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   135
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   136
    try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   137
        Model = models.get_model(*model_identifier.split("."))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   138
    except TypeError:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   139
        Model = None
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   140
    if Model is None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   141
        raise base.DeserializationError(u"Invalid model identifier: '%s'" % model_identifier)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   142
    return Model