module for abstract serializerunserializer base classes the requested serializer was not found pass class serializationerrorexception something bad happened during deserialization classmethod def withdatacls originalexc model fk fieldvalue return cls s s pks fieldvalue was s originalexc model fk fieldvalue class m2mdeserializationerrorexception abstract serializer base class indicates if the implemented serializer is only available for internal django use serialize a queryset use the concrete parent class meta instead of the object s meta this is to avoid localfields problems for proxy models refs 17717 when using natural primary keys retrieve the pk field of the parent for multitable inheritance child models that field must be serialized otherwise deserialization isn t possible called when serializing of the queryset starts called when serializing of the queryset ends called when serializing of an object starts called when serializing of an object ends called to handle each individual nonrelational field on an object called to handle a foreignkey field called to handle a manytomanyfield return the fully serialized queryset or none if the output stream is not seekable abstract base deserializer class init this serializer given a stream or a string iteration interface return the next item in the stream raise notimplementederror subclasses of deserializer must provide a next method class deserializedobject def initself obj m2mdatanone deferredfieldsnone self object obj self m2mdata m2mdata self deferredfields deferredfields def reprself return s spks self class name self object meta label self object pk def saveself savem2mtrue usingnone kwargs call save on the model baseclass directly this bypasses any modeldefined save the save is also forced to be raw rawtrue is passed to any prepostsave signals models model savebaseself object usingusing rawtrue kwargs if self m2mdata and savem2m for accessorname objectlist in self m2mdata items getattrself object accessorname setobjectlist prevent a second possibly accidental call to save from saving the m2m data twice self m2mdata none def savedeferredfieldsself usingnone self m2mdata for field fieldvalue in self deferredfields items opts self object meta label opts applabel opts modelname if isinstancefield remotefield models manytomanyrel try values deserializem2mvalues field fieldvalue using handleforwardreferencesfalse except m2mdeserializationerror as e raise deserializationerror withdata e originalexc label self object pk e pk self m2mdatafield name values elif isinstancefield remotefield models manytoonerel try value deserializefkvalue field fieldvalue using handleforwardreferencesfalse except exception as e raise deserializationerror withdata e label self object pk fieldvalue setattrself object field attname value self save def buildinstancemodel data db defaultmanager model meta defaultmanager pk data getmodel meta pk attname if pk is none and hasattrdefaultmanager getbynaturalkey and hasattrmodel naturalkey obj modeldata obj state db db naturalkey obj naturalkey try datamodel meta pk attname model meta pk topython defaultmanager dbmanagerdb getbynaturalkeynaturalkey pk except model doesnotexist pass return modeldata def deserializem2mvaluesfield fieldvalue using handleforwardreferences model field remotefield model if hasattrmodel defaultmanager getbynaturalkey def m2mconvertvalue if hasattrvalue iter and not isinstancevalue str return model defaultmanager dbmanagerusing getbynaturalkeyvalue pk else return model meta pk topythonvalue else def m2mconvertv return model meta pk topythonv try pksiter iterfieldvalue except typeerror as e raise m2mdeserializationerrore fieldvalue try values for pk in pksiter values appendm2mconvertpk return values except exception as e if isinstancee objectdoesnotexist and handleforwardreferences return deferfield else raise m2mdeserializationerrore pk def deserializefkvaluefield fieldvalue using handleforwardreferences if fieldvalue is none return none model field remotefield model defaultmanager model defaultmanager fieldname field remotefield fieldname if hasattrdefaultmanager getbynaturalkey and hasattrfieldvalue iter and not isinstancefieldvalue str try obj defaultmanager dbmanagerusing getbynaturalkeyfieldvalue except objectdoesnotexist if handleforwardreferences return deferfield else raise value getattrobj fieldname if this is a natural foreign key to an object that has a fko2o as the foreign key use the fk value if model meta pk remotefield value value pk return value return model meta getfieldfieldname topythonfieldvalue the requested serializer was not found something bad happened during serialization something bad happened during deserialization factory method for creating a deserialization error which has a more explanatory message something bad happened during deserialization of a manytomanyfield abstract serializer base class indicates if the implemented serializer is only available for internal django use serialize a queryset use the concrete parent class _meta instead of the object s _meta this is to avoid local_fields problems for proxy models refs 17717 when using natural primary keys retrieve the pk field of the parent for multi table inheritance child models that field must be serialized otherwise deserialization isn t possible called when serializing of the queryset starts called when serializing of the queryset ends called when serializing of an object starts called when serializing of an object ends called to handle each individual non relational field on an object called to handle a foreignkey field called to handle a manytomanyfield return the fully serialized queryset or none if the output stream is not seekable abstract base deserializer class init this serializer given a stream or a string iteration interface return the next item in the stream a deserialized model basically a container for holding the pre saved deserialized data along with the many to many data saved with the object call save to save the object with the many to many data to the database call save save_m2m false to save just the object fields and not touch the many to many stuff call save on the model baseclass directly this bypasses any model defined save the save is also forced to be raw raw true is passed to any pre post_save signals prevent a second possibly accidental call to save from saving the m2m data twice build a model instance if the model instance doesn t have a primary key and the model supports natural keys try to retrieve it from the database if this is a natural foreign key to an object that has a fk o2o as the foreign key use the fk value
from io import StringIO

from django.core.exceptions import ObjectDoesNotExist
from django.db import models

DEFER_FIELD = object()


class SerializerDoesNotExist(KeyError):
    

    pass


class SerializationError(Exception):
    

    pass


class DeserializationError(Exception):
    

    @classmethod
    def WithData(cls, original_exc, model, fk, field_value):
        
        return cls(
            "%s: (%s:pk=%s) field_value was '%s'"
            % (original_exc, model, fk, field_value)
        )


class M2MDeserializationError(Exception):
    

    def __init__(self, original_exc, pk):
        self.original_exc = original_exc
        self.pk = pk


class ProgressBar:
    progress_width = 75

    def __init__(self, output, total_count):
        self.output = output
        self.total_count = total_count
        self.prev_done = 0

    def update(self, count):
        if not self.output:
            return
        perc = count * 100 // self.total_count
        done = perc * self.progress_width // 100
        if self.prev_done >= done:
            return
        self.prev_done = done
        cr = "" if self.total_count == 1 else "\r"
        self.output.write(
            cr + "[" + "." * done + " " * (self.progress_width - done) + "]"
        )
        if done == self.progress_width:
            self.output.write("\n")
        self.output.flush()


class Serializer:
    

    
    
    internal_use_only = False
    progress_class = ProgressBar
    stream_class = StringIO

    def serialize(
        self,
        queryset,
        *,
        stream=None,
        fields=None,
        use_natural_foreign_keys=False,
        use_natural_primary_keys=False,
        progress_output=None,
        object_count=0,
        **options,
    ):
        
        self.options = options

        self.stream = stream if stream is not None else self.stream_class()
        self.selected_fields = fields
        self.use_natural_foreign_keys = use_natural_foreign_keys
        self.use_natural_primary_keys = use_natural_primary_keys
        progress_bar = self.progress_class(progress_output, object_count)

        self.start_serialization()
        self.first = True
        for count, obj in enumerate(queryset, start=1):
            self.start_object(obj)
            
            
            concrete_model = obj._meta.concrete_model
            
            
            
            if self.use_natural_primary_keys:
                pk = concrete_model._meta.pk
                pk_parent = (
                    pk if pk.remote_field and pk.remote_field.parent_link else None
                )
            else:
                pk_parent = None
            for field in concrete_model._meta.local_fields:
                if field.serialize or field is pk_parent:
                    if field.remote_field is None:
                        if (
                            self.selected_fields is None
                            or field.attname in self.selected_fields
                        ):
                            self.handle_field(obj, field)
                    else:
                        if (
                            self.selected_fields is None
                            or field.attname[:-3] in self.selected_fields
                        ):
                            self.handle_fk_field(obj, field)
            for field in concrete_model._meta.local_many_to_many:
                if field.serialize:
                    if (
                        self.selected_fields is None
                        or field.attname in self.selected_fields
                    ):
                        self.handle_m2m_field(obj, field)
            self.end_object(obj)
            progress_bar.update(count)
            self.first = self.first and False
        self.end_serialization()
        return self.getvalue()

    def start_serialization(self):
        
        raise NotImplementedError(
            "subclasses of Serializer must provide a start_serialization() method"
        )

    def end_serialization(self):
        
        pass

    def start_object(self, obj):
        
        raise NotImplementedError(
            "subclasses of Serializer must provide a start_object() method"
        )

    def end_object(self, obj):
        
        pass

    def handle_field(self, obj, field):
        
        raise NotImplementedError(
            "subclasses of Serializer must provide a handle_field() method"
        )

    def handle_fk_field(self, obj, field):
        
        raise NotImplementedError(
            "subclasses of Serializer must provide a handle_fk_field() method"
        )

    def handle_m2m_field(self, obj, field):
        
        raise NotImplementedError(
            "subclasses of Serializer must provide a handle_m2m_field() method"
        )

    def getvalue(self):
        
        if callable(getattr(self.stream, "getvalue", None)):
            return self.stream.getvalue()


class Deserializer:
    

    def __init__(self, stream_or_string, **options):
        
        self.options = options
        if isinstance(stream_or_string, str):
            self.stream = StringIO(stream_or_string)
        else:
            self.stream = stream_or_string

    def __iter__(self):
        return self

    def __next__(self):
        
        raise NotImplementedError(
            "subclasses of Deserializer must provide a __next__() method"
        )


class DeserializedObject:
    

    def __init__(self, obj, m2m_data=None, deferred_fields=None):
        self.object = obj
        self.m2m_data = m2m_data
        self.deferred_fields = deferred_fields

    def __repr__(self):
        return "<%s: %s(pk=%s)>" % (
            self.__class__.__name__,
            self.object._meta.label,
            self.object.pk,
        )

    def save(self, save_m2m=True, using=None, **kwargs):
        
        
        
        models.Model.save_base(self.object, using=using, raw=True, **kwargs)
        if self.m2m_data and save_m2m:
            for accessor_name, object_list in self.m2m_data.items():
                getattr(self.object, accessor_name).set(object_list)

        
        
        self.m2m_data = None

    def save_deferred_fields(self, using=None):
        self.m2m_data = {}
        for field, field_value in self.deferred_fields.items():
            opts = self.object._meta
            label = opts.app_label + "." + opts.model_name
            if isinstance(field.remote_field, models.ManyToManyRel):
                try:
                    values = deserialize_m2m_values(
                        field, field_value, using, handle_forward_references=False
                    )
                except M2MDeserializationError as e:
                    raise DeserializationError.WithData(
                        e.original_exc, label, self.object.pk, e.pk
                    )
                self.m2m_data[field.name] = values
            elif isinstance(field.remote_field, models.ManyToOneRel):
                try:
                    value = deserialize_fk_value(
                        field, field_value, using, handle_forward_references=False
                    )
                except Exception as e:
                    raise DeserializationError.WithData(
                        e, label, self.object.pk, field_value
                    )
                setattr(self.object, field.attname, value)
        self.save()


def build_instance(Model, data, db):
    
    default_manager = Model._meta.default_manager
    pk = data.get(Model._meta.pk.attname)
    if (
        pk is None
        and hasattr(default_manager, "get_by_natural_key")
        and hasattr(Model, "natural_key")
    ):
        obj = Model(**data)
        obj._state.db = db
        natural_key = obj.natural_key()
        try:
            data[Model._meta.pk.attname] = Model._meta.pk.to_python(
                default_manager.db_manager(db).get_by_natural_key(*natural_key).pk
            )
        except Model.DoesNotExist:
            pass
    return Model(**data)


def deserialize_m2m_values(field, field_value, using, handle_forward_references):
    model = field.remote_field.model
    if hasattr(model._default_manager, "get_by_natural_key"):

        def m2m_convert(value):
            if hasattr(value, "__iter__") and not isinstance(value, str):
                return (
                    model._default_manager.db_manager(using)
                    .get_by_natural_key(*value)
                    .pk
                )
            else:
                return model._meta.pk.to_python(value)

    else:

        def m2m_convert(v):
            return model._meta.pk.to_python(v)

    try:
        pks_iter = iter(field_value)
    except TypeError as e:
        raise M2MDeserializationError(e, field_value)
    try:
        values = []
        for pk in pks_iter:
            values.append(m2m_convert(pk))
        return values
    except Exception as e:
        if isinstance(e, ObjectDoesNotExist) and handle_forward_references:
            return DEFER_FIELD
        else:
            raise M2MDeserializationError(e, pk)


def deserialize_fk_value(field, field_value, using, handle_forward_references):
    if field_value is None:
        return None
    model = field.remote_field.model
    default_manager = model._default_manager
    field_name = field.remote_field.field_name
    if (
        hasattr(default_manager, "get_by_natural_key")
        and hasattr(field_value, "__iter__")
        and not isinstance(field_value, str)
    ):
        try:
            obj = default_manager.db_manager(using).get_by_natural_key(*field_value)
        except ObjectDoesNotExist:
            if handle_forward_references:
                return DEFER_FIELD
            else:
                raise
        value = getattr(obj, field_name)
        
        
        if model._meta.pk.remote_field:
            value = value.pk
        return value
    return model._meta.get_field(field_name).to_python(field_value)