Issue
Example Json
{
"name": "asdfg",
"amount": {
"amount": 5000,
"forexAmount": 10,
"rateOfExchange": 500,
"currency": "$",
"isDebit": false
}
}
Want to create a custom field which accepts amount. and perform calculations like ModelName.objects.filter(amount.isDebit=True).aggregate(Sum('amount.amount'))
I am using following code. Issue with following field is its creating multiple fields amount_amount
Django Model
from django.db import models
class Ledger(models.Model):
name = models.CharField(max_length=100)
amount = AmountField()
Custom Django Model Field
from typing import Any, Type
from django.db import models
from django.db.models import Model
from django.db.models.query_utils import DeferredAttribute
from django.db.models.utils import AltersData
from django.utils.translation import gettext_lazy as _
class Amount():
amount:float
forexAmount:float|None
rateOfExchange:float|None
currency:str|None
isDebit:bool
def __init__(self,amount,isDebit,currency=None,forexAmount=None,rateOfExchange=None) -> None:
self.amount = amount
self.forexAmount = forexAmount
self.rateOfExchange = rateOfExchange
self.isDebit = isDebit
self.currency = currency
class FieldAmount(Amount,AltersData):
def __init__(self, instance, field, name):
super().__init__(None, name)
self.instance = instance
self.field = field
self.storage = field.storage
self._committed = True
class AmountDescriptor(DeferredAttribute):
def __get__(self,instance,cls=None):
if instance is None:
return self
data = instance.__dict__
amount = super().__get__(instance, cls)
# if(isinstance(amount,Amount)) or amount is None:
# attr = self.field.attr_class(instance, self.field, amount)
# data[self.field.attname] = attr
# pass
# else :
# val = Amount(5,False)
# data[self.field.attname] = val
return data[self.field.attname]
def __set__(self, instance, value):
if(isinstance(value,Amount)):
data = instance.__dict__
data[self.field.attname] = value
data[self.field.attname+"_amount"] = value.amount
data[self.field.attname+"_forexAmount"] = value.forexAmount
class AmountField(models.Field):
attr_class = FieldAmount
descriptor_class = AmountDescriptor
description = _("Tally Amount")
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
def deconstruct(self) -> Any:
return super().deconstruct()
def from_db_value(self,value):
if value is None:
return value
return value
def to_python(self, value: Any) -> Any:
return super().to_python(value)
def contribute_to_class(self, cls: type[Model], name: str, private_only: bool = ...) -> None:
if not hasattr(self, "_amount_field"):
amount_field = models.DecimalField(decimal_places=5,max_digits=15)
amount_field.contribute_to_class(cls,"amount_amount")
self._amount_field = amount_field
super().contribute_to_class(cls, name)
setattr(cls, name, self.descriptor_class(self))
Error:
django.db.utils.OperationalError: duplicate column name: amount_amount
References :
- https://docs.djangoproject.com/en/4.2/howto/custom-model-fields/
- How to store a complex number in Django model
Solution
below changes to contribute_to_class worked for me
def contribute_to_class(self, cls: type[Model], name: str, private_only: bool = ...) -> None:
_amount_field = models.DecimalField(decimal_places=5,max_digits=15)
#... other Fields
self.set_attributes_from_name(name)
self.concrete = False
cls._meta.add_field(self, True)
setattr(cls, name, self.descriptor_class(self))
cls.add_to_class(self.amount_field_name, _amount_field)
#... other Fields
Answered By - sai vineeth
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.