1
\$\begingroup\$

I have the following code which works as expected i.e saves data to the db as I would like it to, however, there is a lot of code repetition and I am unable to find a way to shorten the code

I have about 30 serializers (pasting 3 to shorten the code)

class FirstOne(serializers.ModelSerializer):
    period = PeriodSerializer(many=False)

    class Meta:
        model = FirstOne
        fields = ['decimals', 'unitRef', 'value', 'period']


class SecondOne(serializers.ModelSerializer):
    period = PeriodSerializer(many=False)

    class Meta:
        model = SecondOne
        fields = ['decimals', 'unitRef', 'value', 'period']


class ThirdOne(serializers.ModelSerializer):
    period = PeriodSerializer(many=False)

    class Meta:
        model = ThirdOne
        fields = ['decimals', 'unitRef', 'value', 'period'] 

class CashFlowSerializer(serializers.ModelSerializer):
    FirstItemInJson = FirstOne(many=True)
    SecondItemInJson = SecondOne(many=True)
    ThirdItemInJson = ThirdOne(many=True)

class Meta:
    model = Basetable
    fields = "__all__"

def create(self, validated_data):
    itemOneData = validated_data.pop('FirstItemInJson')
    itemTwoData = validated_data.pop('SecondItemInJson')
    itemThreeData = validated_data.pop('ThirdItemInJson')
cashflow = Basetable.objects.create(**validated_data)
for data in itemOneData:
    period_data = data.pop("period")
    dataObj = FirstItemModelClass.objects.create(
        basetable_id=cashflow, **data)
    period_object = Period.objects.create(
        firstItem_id=dataObj, **period_data)
for data in itemTwoData:
    period_data = data.pop("period")
    dataObj = SecondItemModelClass.objects.create(
        basetable_id=cashflow, **data)
    period_object = Period.objects.create(
        secondItem_id=dataObj, **period_data)
for data in itemThreeData:
    period_data = data.pop("period")
    dataObj = ThirdItemModelClass.objects.create(
        basetable_id=cashflow, **data)
    period_object = Period.objects.create(
        thirdItem_id=dataObj, **period_data)

JSON: (trimmed to keep it shorter)

jsonToUse = {
    "CompanyId": "320193",
    "CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalents": [
        {
            "decimals": "-6",
            "unitRef": "usd",
            "period": {
                "instant": "2020-09-26"
            },
            "value": "39789000000"
        },
        {
            "decimals": "-6",
            "unitRef": "usd",
            "period": {
                "instant": "2019-09-28"
            },
            "value": "50224000000"
        },
        {
            "decimals": "-6",
            "unitRef": "usd",
            "period": {
                "instant": "2018-09-29"
            },
            "value": "25913000000"
        },
        {
            "decimals": "-6",
            "unitRef": "usd",
            "period": {
                "instant": "2021-09-25"
            },
            "value": "35929000000"
        }
    ],
    "NetIncomeLoss": [
        {
            "decimals": "-6",
            "unitRef": "usd",
            "period": {
                "startDate": "2020-09-27",
                "endDate": "2021-09-25"
            },
            "value": "94680000000"
        },
        {
            "decimals": "-6",
            "unitRef": "usd",
            "period": {
                "startDate": "2019-09-29",
                "endDate": "2020-09-26"
            },
            "value": "57411000000"
        },
        {
            "decimals": "-6",
            "unitRef": "usd",
            "period": {
                "startDate": "2018-09-30",
                "endDate": "2019-09-28"
            },
            "value": "55256000000"
        },
        {
            "decimals": "-6",
            "unitRef": "usd",
            "period": {
                "startDate": "2020-09-27",
                "endDate": "2021-09-25"
            },
            "segment": {
                "dimension": "us-gaap:StatementEquityComponentsAxis",
                "value": "us-gaap:RetainedEarningsMember"
            },
            "value": "94680000000"
        },
        {
            "decimals": "-6",
            "unitRef": "usd",
            "period": {
                "startDate": "2019-09-29",
                "endDate": "2020-09-26"
            },
            "segment": {
                "dimension": "us-gaap:StatementEquityComponentsAxis",
                "value": "us-gaap:RetainedEarningsMember"
            },
            "value": "57411000000"
        },
        {
            "decimals": "-6",
            "unitRef": "usd",
            "period": {
                "startDate": "2018-09-30",
                "endDate": "2019-09-28"
            },
            "segment": {
                "dimension": "us-gaap:StatementEquityComponentsAxis",
                "value": "us-gaap:RetainedEarningsMember"
            },
            "value": "55256000000"
        }
    ],
    "DepreciationDepletionAndAmortization": [
        {
            "decimals": "-6",
            "unitRef": "usd",
            "period": {
                "startDate": "2020-09-27",
                "endDate": "2021-09-25"
            },
            "value": "11284000000"
        },
        {
            "decimals": "-6",
            "unitRef": "usd",
            "period": {
                "startDate": "2019-09-29",
                "endDate": "2020-09-26"
            },
            "value": "11056000000"
        },
        {
            "decimals": "-6",
            "unitRef": "usd",
            "period": {
                "startDate": "2018-09-30",
                "endDate": "2019-09-28"
            },
            "value": "12547000000"
        }
    ]}

Given I have 30 items in my JSON, currently I am going through each item using a for loop so that I can store the Period data in that. How can I shorten my code?

Thank you for your time and help.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ can you explain why you have separation by three? how does jsonToUse fit in to the provided code? \$\endgroup\$
    – depperm
    Jan 27 at 14:02
  • 1
    \$\begingroup\$ @depperm I am pretty fresh to Django and going through the documentation for writable nested serializers and following it, I created separate serializer for each item in the JSON object, but I am extracting same fields from all the objects. I can add more if need be. \$\endgroup\$
    – Maddy
    Jan 27 at 14:11

1 Answer 1

1
\$\begingroup\$

I'm not well-versed in Django, considering all the magic it does behind the scenes using metaproramming, and I don't have an environment where I could test the code I'll present, but I'll try to give a couple advices:


Firstly, you may try creating serializers.ModelSerializer-inheriting classes using a factory.

Write a function that creates the classes and call it like this:

def model_serializer_factory(meta_model, meta_fields):

    class T(serializers.ModelSerializer):
        period = PeriodSerializer(many=False)

        class Meta:
            model = meta_model
            fields = meta_fields

    return T


FirstOneSerializer, SecondOneSerializer, ThirdOneSerializer = (
    model_serializer_factory(model, ['decimals', 'unitRef', 'value', 'period'])
    for model in (FirstOne, SecondOne, ThirdOne)
)

This is a (very) simplified example that's specific to the code you've provided. If your classes have more complex structures, you could create them on the fly using type like this:

def create_serializer_class(meta_model, meta_attrs, serializer_name, serializer_attrs):
    meta = type(
        'Meta',
        (),
        {'model': meta_model, **meta_attrs}
    )

    return type(
        serializer_name,
        (serializers.ModelSerializer,),
        {**serializer_attrs, 'Meta': meta}
    )


FirstOneSerializer, SecondOneSerializer, ThirdOneSerializer = (
    model_serializer_factory(
        model,
        {'fields': ['decimals', 'unitRef', 'value', 'period']},
        serializer_name,
        {'period': PeriodSerializer(many=False)}
    )
    for model, serializer_name in (
        (FirstOne, 'FirstOneSerializer'),
        (SecondOne, 'SecondOneSerializer'),
        (ThirdOne, 'ThirdOneSerializer')
    )
)

I'm simply creating serializers in a loop since they're (almost) identical in your example, but you can create arbitrary classes using this improved model_serializer_factory function.

Refer to this question which is about the exact same thing as I'm explaining here.


Secondly, your copy-pasted loops at the end can be parameterized as well. Turning item_X_Data and X_ItemModelClass into loop variables is trivial. For passing the X_Item_id as a kwarg to Period.objects.create, you'll have to convert it into a single-item dictionary and use kwargs-spreading, though.

parameterized_list = [
    (itemOneData, FirstItemModelClass, 'firstItem_id'),
    (itemTwoData, SecondItemModelClass, 'secondItem_id'),
    (itemThreeData, ThirdItemModelClass, 'thirdItem_id')
]

for data_list, model, item_id in parameterized_list:
    for data in data_list:
        period_data = data.pop("period")
        dataObj = model.objects.create(basetable_id=cashflow, **data)
        period_object = Period.objects.create(**{item_id: dataObj}, **period_data)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.