mirror of
https://github.com/Hopiu/django.git
synced 2026-05-24 14:53:52 +00:00
Since the original ones in django.db.models.loading were kept only for backwards compatibility, there's no need to recreate them. However, many internals of Django still relied on them. They were also imported in django.db.models. They never appear in the documentation, except a quick mention of get_models and get_app in the 1.2 release notes to document an edge case in GIS. I don't think that makes them a public API. This commit doesn't change the overall amount of global state but clarifies that it's tied to the app_cache object instead of hiding it behind half a dozen functions.
162 lines
6.2 KiB
Python
162 lines
6.2 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 __future__ import unicode_literals
|
|
|
|
from django.apps import app_cache
|
|
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_text, is_protected_type
|
|
from django.utils import six
|
|
|
|
|
|
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):
|
|
data = {
|
|
"model": smart_text(obj._meta),
|
|
"fields": self._current,
|
|
}
|
|
if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'):
|
|
data["pk"] = smart_text(obj._get_pk_val(), strings_only=True)
|
|
|
|
return data
|
|
|
|
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_foreign_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_foreign_keys and hasattr(field.rel.to, 'natural_key'):
|
|
m2m_value = lambda value: value.natural_key()
|
|
else:
|
|
m2m_value = lambda value: smart_text(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)
|
|
ignore = options.pop('ignorenonexistent', False)
|
|
|
|
app_cache.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 = {}
|
|
if 'pk' in d:
|
|
data[Model._meta.pk.attname] = Model._meta.pk.to_python(d.get("pk", None))
|
|
m2m_data = {}
|
|
model_fields = Model._meta.get_all_field_names()
|
|
|
|
# Handle each field
|
|
for (field_name, field_value) in six.iteritems(d["fields"]):
|
|
|
|
if ignore and field_name not in model_fields:
|
|
# skip fields no longer on model
|
|
continue
|
|
|
|
if isinstance(field_value, str):
|
|
field_value = smart_text(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__') and not isinstance(value, six.text_type):
|
|
return field.rel.to._default_manager.db_manager(db).get_by_natural_key(*value).pk
|
|
else:
|
|
return smart_text(field.rel.to._meta.pk.to_python(value))
|
|
else:
|
|
m2m_convert = lambda v: smart_text(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__') and not isinstance(field_value, six.text_type):
|
|
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)
|
|
|
|
obj = base.build_instance(Model, data, db)
|
|
yield base.DeserializedObject(obj, m2m_data)
|
|
|
|
|
|
def _get_model(model_identifier):
|
|
"""
|
|
Helper to look up a model from an "app_label.model_name" string.
|
|
"""
|
|
try:
|
|
Model = app_cache.get_model(*model_identifier.split("."))
|
|
except TypeError:
|
|
Model = None
|
|
if Model is None:
|
|
raise base.DeserializationError("Invalid model identifier: '%s'" % model_identifier)
|
|
return Model
|