0

I am looking to add serializing-deserializing methods to a Pydantic class to communicate class attributes to a restAPI or update object with API response. I arrived up to this point, program don't crash anymore and I can call my new methods but so far the class attributes can't be modified from my method fro son Could you give me some help please ?

    import uuid
    from typing import Optional
    from pydantic import BaseModel, Field, Extra
    import json


    class Panel(BaseModel):
        id: str = Field(default_factory=uuid.uuid4, alias="_id")
        manufacturer: str = Field(...)
        model: str = Field(...)
        url: str = Field(...)
        documents: Optional[object]

        #Electrical characteristics
        power_max_stc: object = Field(...)
        voltage_openCircuit: object = Field(...)
        current_shunt: object = Field(...)
        voltage_maxPow: object = Field(...)
        current_maxPow: object = Field(...)

        power_ptc: Optional[object]
        power_ratioPtcStc: Optional[object]
        efficiency: Optional[object]
        fill_factor: Optional[object]
        power_tolerance: Optional[object]
        integrity_maxVoltage: Optional[object]
        integrity_maxFuseCurrent: Optional[object]

        #Temperature coefficients
        coef_temp_shuntCurrent: Optional[object]
        coef_temp_openCircuitVoltage: Optional[object]
        coef_temp_maxPow: Optional[object]

        #Mechanical characteristics
        cells: object = Field(...)
        dimensions: object = Field(...)

        weight: Optional[object]
        wiring: Optional[object]
        bom: Optional[object]

        #Operating conditions
        temp_nom_cell: Optional[object]
        temp_operating: Optional[object]
        load_max: Optional[object]
        rating_hailStorm: Optional[object]
        rating_fireSafety: Optional[object]

        #Waranty and certifications
        certificates: Optional[object]
        waranties: Optional[object]

        __fields = ['manufacturer', 'model', 'url', 'documents', 'power_max_stc',
                'voltage_openCircuit', 'current_shunt', 'voltage_maxPow', 'current_maxPow', 'power_ptc', 
                'power_ratioPtcStc', 'efficiency', 'fill_factor', 'power_tolerance', 'integrity_maxVoltage', 
                'integrity_maxFuseCurrent', 'coef_temp_shuntCurrent', 'coef_temp_openCircuitVoltage', 'coef_temp_maxPow', 'cells', 
                'dimensions', 'weight', 'wiring', 'bom', 'temp_nom_cell', 
                'temp_operating', 'load_max', 'rating_hailStorm', 'rating_fireSafety', 'certificates', 
                'waranties',
                ]
        __fields_required = ['manufacturer', 'model', 'url', 'power_max_stc',
                'voltage_openCircuit', 'current_shunt', 'voltage_maxPow', 'current_maxPow', 'cells', 
                'dimensions',
                ]
        __fields_optional = ['documents', 'power_ptc', 
                'power_ratioPtcStc', 'efficiency', 'fill_factor', 'power_tolerance', 'integrity_maxVoltage', 
                'integrity_maxFuseCurrent', 'coef_temp_shuntCurrent', 'coef_temp_openCircuitVoltage', 'coef_temp_maxPow',
                'weight', 'wiring', 'bom', 'temp_nom_cell', 
                'temp_operating', 'load_max', 'rating_hailStorm', 'rating_fireSafety', 'certificates', 
                'waranties',
                ]

        @classmethod
        def fromJsonInit(self, panelJson = None):
            #super().__init__()
            if panelJson is not None:
                if self.isJsonString(panelJson):
                    panel = json.loads(panelJson)
                    hasRequiredData = True
                    for field in self.__fields_required:
                        if field not in panel: hasRequiredData = False
                    if hasRequiredData: 
                        self.fromJson(jsonObject=panelJson)
                    else:
                        raise(ValueError("Insufficient Data to create object!"))

        @classmethod
        def fromJson(cls, jsonObject):
            if cls.isJsonString(jsonObject):
                panel = json.loads(jsonObject)
                hasUnknownField = False
                for field in panel:
                    if field not in cls.__fields: hasUnknownField = True

                print(f"hasUnkonwnFields : ({hasUnknownField})")
                if not hasUnknownField:
                    for field in panel: 
                        print(f"field: {field}\nvalue: {panel[field]}")
                        setattr(cls,field, panel[field])
                        print("bam")
                else:
                    raise(ValueError(f"Passed Invalid Data to the object!\nReminder, appropriate ifelds are:\n{self.fields}"))
        
        @classmethod
        def toJson(self):
            outputJson = {}
            for attr in self.__fields:
                if self.isSet(attr):
                    outputJson[attr] = getattr(self,attr)
            return outputJson

        @classmethod
        def isJsonString(self, jsonStr):
            try:
                json.loads(jsonStr)
                isJson = True
            except:
                isJson = False
            finally:
                print(f"isJsonString: ({isJson})")
                return isJson
        
        @classmethod
        def isSet(self, variableName):
            try:
                getattr(self, variableName)
                #exec(f"{variableName}")
                #print("sure, it was defined.")
                isSetb = True
            except NameError:
                #print("well, it WASN'T defined after all!")
                isSetb = False
            finally:
                return isSetb
        
        class Config:
            #extra=Extra.allow
            arbitrary_types_allowed = True
            allow_mutation = True
            allow_population_by_field_name = True
            schema_extra = {
                "example": {
                    "_id": "066de609-b04a-4b30-b46c-32537c7f1f6e",
                    "manufacturer": "JA Solar",
                    "model": "JAM54S31/MR",
                    "url": "http://www.posharp.com/yl250p-32b-solar-panel-from-yingli-green-energy_p389707207d.aspx",
                    "documents": {"datasheet": {
                        "english": "http://www.posharp.com/Businesses/ca46966d-c99d-456b-9e49-5b6bc7233e14/Panel/YL255P-32b_en.pdf",
                        "french": "http://www.posharp.com/Businesses/ca46966d-c99d-456b-9e49-5b6bc7233e14/Panel/YL255P-32b_fr.pdf"
                    }},

                    "power_max_stc": {"value": 250.0,"unit": "W"},
                    "voltage_openCircuit": {"value": 40.9,"unit": "V"},
                    "current_shunt": {"value": 8.33,"unit": "A"},
                    "voltage_maxPow": {"value": 32.3,"unit": "V"},
                    "current_maxPow": {"value": 7.74,"unit": "A"},

                    "power_ptc": {"value": 220.6,"unit": "W"},
                    "power_ratioPtcStc": {"value": 88.2,"unit": "%"},
                    "efficiency": {"value": 14.0,"unit": "%"},
                    "fill_factor": {"value": 73.4,"unit": "%"},
                    "power_tolerance": {
                        "nominal": 0.0,
                        "min": -3.0,
                        "max": 3.0,
                        "interval": 6.0,
                        "unit": "%"
                        },
                    "integrity_maxVoltage": {"value": 1000.0,"unit": "V"},
                    "integrity_maxFuseCurrent": {"value": 15.0,"unit": "A"},

                    "coef_temp_shuntCurrent": {"value": 0.006,"unit": "%/ºC"},
                    "coef_temp_openCircuitVoltage": {"value": -0.37,"unit": "%/ºC"},
                    "coef_temp_maxPow": {"value": -0.45,"unit": "%/ºC"},

                    "cells": {
                        "type": "Monocrystalline Cell",
                        "size": "80x156",
                        "size_width": 80,
                        "size_height": 156,
                        "count_format": "8x10",
                        "count_format_axis1": 8,
                        "count_format_axis2": 10
                    },
                    "dimensions": {
                        "axis1": {"value": 1652.0,"unit": "mm"},
                        "axis2": {"value": 1306.0,"unit": "mm"},
                        "axis3": {"value": 55.0,"unit": "mm"},
                    },

                    "weight": {"value": 22.5,"unit": "Kg"},
                    "wiring": {
                        "junction_box": {
                            "rating_safety": "IP67",
                            "diodes": 4
                        },
                        "positive_cable": "80mm, 16mm2",
                        "negative_cable": "110mm, 16mm2",
                        "connector": "MC4",
                    },
                    "bom": {
                        "front_cover": "glass, 3mm",
                        "backsheet_cover": "PET Black 4mm",
                        "encapsulation": "PBT",
                        "frame": "aluminum",
                    },

                    "temp_nom_cell": {"value": 50.0,"unit": "ºC"},
                    "temp_operating": {
                        "nominal": 25.0,
                        "min": -40.0,
                        "max": 90.0,
                        "interval": 130.0,
                        "unit": "ºC"
                        },
                    "load_max": {"value": 50.0,"unit": "Mpa"},
                    "rating_hailStorm": {"value": "Grade B"},
                    "rating_fireSafety": {"value": "Grade Z"},
                    "certificates": [
                        {"name": "UL 1703","url": "https://www.intertek.com/building/standards/ul-1703/"},
                        {"name": "CEC California","url": ""},
                        ],
                    "waranties": {
                        "defectAndWorksmanship": {"value": 5.0,"unit": "Year"},
                        "power_90Percent": {"value": 10.0,"unit": "Year"},
                        "power_80Percent": {"value": 25.0,"unit": "Year"},
                        "insuredByThirdParty": False
                        },

                }
            }
        


    if __name__ == "__main__":

        try:
            testPanel_valid = Panel(**{
                'manufacturer':"testMan",
                'model':"testModel",
                'url': "testUrl", 
                'power_max_stc': {},
                'voltage_openCircuit': {}, 
                'current_shunt': {}, 
                'voltage_maxPow': {}, 
                'current_maxPow': {}, 
                'cells': {}, 
                'dimensions': {},
            })
            
            testPanel_invalid = Panel(**{
                'manufacturer':"testMan",
                'model':"testModel",
                'url': "testUrl", 
                'power_max_stc': {},
                'voltage_openCircuit': {}, 
                'current_shunt': {}, 
                'voltage_maxPow': {}, 
                'current_maxPow': {}, 
                'cells': {}, 
                'dimensions': {},
                "Dmensiooooooooooooon":{}
            })

        except Exception as e:
            print(f"Panel class error : {e}")

        print(f"testPanel_valid: {testPanel_valid}\n")

        print(f"testPanel_invalid: {testPanel_invalid}\n")

        testPanel_valid.fromJson(json.dumps({
            "manufacturer":"mySatisfactory"
        }))

        print(f"testPanel_valid: {testPanel_valid}\n")

I tried with the options extra = Extra.allow, arbitrary_types_allowed=True, allow_mutation=True

which I found here: text

It still does not work. If I place the extra option in the class definition line like in the url, like : cla s Model Base Model, extra Extra.forbid I get an error ambiguous configuration

Yaakov Bressler
  • 9,056
  • 2
  • 45
  • 69
  • 2
    this si confusing, because the whole point of pydantic is that you shouldn't have to write custom serialization/deserialization logic, and instead, just delcaratively describe the structure of your model. why did you think it was necessary to write this logic? What is insufficient about the capabilities of the pydantic models for your use-case? – juanpa.arrivillaga Jun 07 '23 at 18:11
  • Welcome to Stack Overflow. I suggest you read the articles on [how to ask a good question](https://stackoverflow.com/help/how-to-ask) and [how to create a MRE](https://stackoverflow.com/help/mcve), then use the _Edit_ function to modify your question accordingly. The practice of boiling the code down to the bare minimum needed to capture the essence of the problem not only motivates others to actually try and help you but more often than not gives you a deeper understanding to solve the problem yourself. – Daniil Fajnberg Jun 07 '23 at 19:55
  • To transmit my object to a server through a restAPI I need to serialize / describe my object in a map and on the server I need to deserialize to build back the same object on the remote system don't I ? – GrosseKartoffel Jun 07 '23 at 20:26
  • Yes, that is what the [`json`](https://docs.pydantic.dev/latest/usage/exporting_models/#modeljson) instance method and the [`parse_raw`](https://docs.pydantic.dev/latest/usage/models/#helper-functions) class method of `BaseModel` are for. – Daniil Fajnberg Jun 07 '23 at 21:31

0 Answers0