I'm working in an API and I have to validate the data that users write in a post/put endpoint. But my model class has 10 attributes, so I have to validate them with 10 ifs, so it makes my code look terrible.
Example:
class MyClass():
atr1: str
atr2: str
atr3: str
atr4: str
atr5: str
atr6: str
atr7: str
atr8: str
atr9: str
atr10: str
And then I have my put endpoint:
@api.put("/update/{id}")
def update(id: int, exemple: MyClass):
if exemple not in exemples:
return {"Error": "exemple not found!"}
if exemple.atr1 != None:
exemples[exemple].atr1 = exemple.atr1
if exemple.atr2 != None:
exemples[exemple].atr2 = exemple.atr2
if exemple.atr3 != None:
exemples[exemple].atr3 = exemple.atr3
if exemple.atr4 != None:
exemples[exemple].atr4 = exemple.atr4
if exemple.atr5 != None:
exemples[exemple].atr5 = exemple.atr5
if exemple.atr6 != None:
exemples[exemple].atr6 = exemple.atr6
if exemple.atr7 != None:
exemples[exemple].atr7 = exemple.atr7
if exemple.atr8 != None:
exemples[exemple].atr8 = exemple.atr8
if exemple.atr9 != None:
exemples[exemple].atr9 = exemple.atr9
if exemple.atr10 != None:
exemples[exemple].atr10 = exemple.atr10
return exemples[exemple]
I know that I can do this validation in another place, but I would still need to do the 10 ifs to validate each field. I'm looking for a way to just get the name of the each attribute and then loop with a for and validade in an if-statment inside the for-loop. I search for ways to do this. I found a way that uses __dict__
, vars(MyClass) but I couldn't make it work. Can anyone help me to do this in a simple way?
CodePudding user response:
I can suggest two ways to do this and avoid these if else block.
First: You can use properties. For each attribute in your class, add a property (getter and setter). In this way every time new value is assigned to each attribute of your class from anywhere, you have your validation. (One important note you should keep in mind is that properties are slow in I/O tasks).
class MyClass:
_atr1: float
@property
def atr1(self) -> float:
"""
This is getter method and just return the
_atr1 attribute.
"""
return self._atr1
@atr1.setter
def atr1(self, value) -> None:
"""
This is setter method for _atr1 and has a
little validation. If validation will not pass
It will raise an ValueError.
"""
if value > 0:
self._atr1 = value
return
raise ValueError("The value is not enough!")
Seconds: You can use descriptors. In this way you can add many descriptors with out side of your class, and assign this descriptors to your attributes. One important advantage of descriptors over properties is that you can use them in many classes and they have better speed.
class NumberDescriptor(object):
"""
This class is a descriptor which validate the value
to be greater than 0
"""
def __init__(self, number=None):
self.number = number
def __get__(self, obj, objtype):
return self.number
def __set__(self, obj, number):
if number > 0:
self.number = number
else:
raise ValueError("The value is not enough!")
class MyClass:
atr1 = NumberDescriptor()
atr2 = NumberDescriptor()
atr3 = NumberDescriptor()
c = MyClass()
c.atr1 = 0
ValueError: The value is not enough!