def __setattr__(self, name: str, value: Any) -> None: # noqa CCR001 """ Overwrites setattr in object to allow for special behaviour of certain params. Parameter "pk" is translated into actual primary key field name. Relations are expanded (child model constructed if needed) and registered on both ends of the relation. The related models are handled by RelationshipManager exposed at _orm param. Json fields converted if needed. Setting pk, foreign key value or any other field value sets Model save status to False. Setting a reverse relation or many to many relation does not as it does not modify the state of the model (but related model or through model). To short circuit all checks and expansions the set of attribute names present on each model is gathered into _quick_access_fields that is looked first and if field is in this set the object setattr is called directly. :param name: name of the attribute to set :type name: str :param value: value of the attribute to set :type value: Any :return: None :rtype: None """ if name in object.__getattribute__(self, "_quick_access_fields"): object.__setattr__(self, name, value) elif name == "pk": object.__setattr__(self, self.Meta.pkname, value) self.set_save_status(False) elif name in object.__getattribute__(self, "_orm"): model = (object.__getattribute__( self, "Meta").model_fields[name].expand_relationship(value=value, child=self)) if isinstance( object.__getattribute__(self, "__dict__").get(name), list): # virtual foreign key or many to many # TODO: Fix double items in dict, no effect on real action ugly repr # if model.pk not in [x.pk for x in related_list]: object.__getattribute__(self, "__dict__")[name].append(model) else: # foreign key relation object.__getattribute__(self, "__dict__")[name] = model self.set_save_status(False) else: if name in object.__getattribute__(self, "_choices_fields"): validate_choices(field=self.Meta.model_fields[name], value=value) super().__setattr__(name, value) self.set_save_status(False)
def validate_choices(cls, new_kwargs: Dict) -> Dict: """ Receives dictionary of model that is about to be saved and validates the fields with choices set to see if the value is allowed. :param new_kwargs: dictionary of model that is about to be saved :type new_kwargs: Dict :return: dictionary of model that is about to be saved :rtype: Dict """ if not cls._choices_fields: return new_kwargs for field_name, field in cls.Meta.model_fields.items(): if field_name in new_kwargs and field_name in cls._choices_fields: validate_choices(field=field, value=new_kwargs.get(field_name)) return new_kwargs