mirror of
https://github.com/django/django.git
synced 2025-06-04 11:09:13 +00:00
This should prevent storing all rows in memory when big sets of data are dumped. See ticket for heroic contributors.
143 lines
5.6 KiB
Python
143 lines
5.6 KiB
Python
"""
|
|
A Python "serializer". Doesn't do much serializing per se -- just converts to
|
|
and from basic Python data types (lists, dicts, strings, etc.). Useful as a basis for
|
|
other serializers.
|
|
"""
|
|
|
|
from django.conf import settings
|
|
from django.core.serializers import base
|
|
from django.db import models, DEFAULT_DB_ALIAS
|
|
from django.utils.encoding import smart_unicode, is_protected_type
|
|
|
|
class Serializer(base.Serializer):
|
|
"""
|
|
Serializes a QuerySet to basic Python objects.
|
|
"""
|
|
|
|
internal_use_only = True
|
|
|
|
def start_serialization(self):
|
|
self._current = None
|
|
self.objects = []
|
|
|
|
def end_serialization(self):
|
|
pass
|
|
|
|
def start_object(self, obj):
|
|
self._current = {}
|
|
|
|
def end_object(self, obj):
|
|
self.objects.append(self.get_dump_object(obj))
|
|
self._current = None
|
|
|
|
def get_dump_object(self, obj):
|
|
return {
|
|
"pk": smart_unicode(obj._get_pk_val(), strings_only=True),
|
|
"model": smart_unicode(obj._meta),
|
|
"fields": self._current
|
|
}
|
|
|
|
def handle_field(self, obj, field):
|
|
value = field._get_val_from_obj(obj)
|
|
# Protected types (i.e., primitives like None, numbers, dates,
|
|
# and Decimals) are passed through as is. All other values are
|
|
# converted to string first.
|
|
if is_protected_type(value):
|
|
self._current[field.name] = value
|
|
else:
|
|
self._current[field.name] = field.value_to_string(obj)
|
|
|
|
def handle_fk_field(self, obj, field):
|
|
if self.use_natural_keys and hasattr(field.rel.to, 'natural_key'):
|
|
related = getattr(obj, field.name)
|
|
if related:
|
|
value = related.natural_key()
|
|
else:
|
|
value = None
|
|
else:
|
|
value = getattr(obj, field.get_attname())
|
|
self._current[field.name] = value
|
|
|
|
def handle_m2m_field(self, obj, field):
|
|
if field.rel.through._meta.auto_created:
|
|
if self.use_natural_keys and hasattr(field.rel.to, 'natural_key'):
|
|
m2m_value = lambda value: value.natural_key()
|
|
else:
|
|
m2m_value = lambda value: smart_unicode(value._get_pk_val(), strings_only=True)
|
|
self._current[field.name] = [m2m_value(related)
|
|
for related in getattr(obj, field.name).iterator()]
|
|
|
|
def getvalue(self):
|
|
return self.objects
|
|
|
|
def Deserializer(object_list, **options):
|
|
"""
|
|
Deserialize simple Python objects back into Django ORM instances.
|
|
|
|
It's expected that you pass the Python objects themselves (instead of a
|
|
stream or a string) to the constructor
|
|
"""
|
|
db = options.pop('using', DEFAULT_DB_ALIAS)
|
|
models.get_apps()
|
|
for d in object_list:
|
|
# Look up the model and starting build a dict of data for it.
|
|
Model = _get_model(d["model"])
|
|
data = {Model._meta.pk.attname : Model._meta.pk.to_python(d["pk"])}
|
|
m2m_data = {}
|
|
|
|
# Handle each field
|
|
for (field_name, field_value) in d["fields"].iteritems():
|
|
if isinstance(field_value, str):
|
|
field_value = smart_unicode(field_value, options.get("encoding", settings.DEFAULT_CHARSET), strings_only=True)
|
|
|
|
field = Model._meta.get_field(field_name)
|
|
|
|
# Handle M2M relations
|
|
if field.rel and isinstance(field.rel, models.ManyToManyRel):
|
|
if hasattr(field.rel.to._default_manager, 'get_by_natural_key'):
|
|
def m2m_convert(value):
|
|
if hasattr(value, '__iter__'):
|
|
return field.rel.to._default_manager.db_manager(db).get_by_natural_key(*value).pk
|
|
else:
|
|
return smart_unicode(field.rel.to._meta.pk.to_python(value))
|
|
else:
|
|
m2m_convert = lambda v: smart_unicode(field.rel.to._meta.pk.to_python(v))
|
|
m2m_data[field.name] = [m2m_convert(pk) for pk in field_value]
|
|
|
|
# Handle FK fields
|
|
elif field.rel and isinstance(field.rel, models.ManyToOneRel):
|
|
if field_value is not None:
|
|
if hasattr(field.rel.to._default_manager, 'get_by_natural_key'):
|
|
if hasattr(field_value, '__iter__'):
|
|
obj = field.rel.to._default_manager.db_manager(db).get_by_natural_key(*field_value)
|
|
value = getattr(obj, field.rel.field_name)
|
|
# If this is a natural foreign key to an object that
|
|
# has a FK/O2O as the foreign key, use the FK value
|
|
if field.rel.to._meta.pk.rel:
|
|
value = value.pk
|
|
else:
|
|
value = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)
|
|
data[field.attname] = value
|
|
else:
|
|
data[field.attname] = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)
|
|
else:
|
|
data[field.attname] = None
|
|
|
|
# Handle all other fields
|
|
else:
|
|
data[field.name] = field.to_python(field_value)
|
|
|
|
yield base.DeserializedObject(Model(**data), m2m_data)
|
|
|
|
def _get_model(model_identifier):
|
|
"""
|
|
Helper to look up a model from an "app_label.module_name" string.
|
|
"""
|
|
try:
|
|
Model = models.get_model(*model_identifier.split("."))
|
|
except TypeError:
|
|
Model = None
|
|
if Model is None:
|
|
raise base.DeserializationError(u"Invalid model identifier: '%s'" % model_identifier)
|
|
return Model
|