Search code examples
pythonvalidationinheritanceattributespython-dataclasses

How to set an attribute of type class in a dataclass and maintain inheritance - PYTHON 3


I am creating a dataclass named TransDate to validate a date field to be imported from a CSV file. I am adding some properties to return the year, week, month, day, weekday and quarters and i am also calling a function to validate the date according to 6 format types.

This works fine.

I then create a second class named Transactions of which one of its attributes is the insDate. When I instatiate a TransDate object the properties are working fine. When I instatiate a Transaction object and I want to access the TransDate properties (tr1.insDate.week or tr1.insDate.qrt or any of its properties) I get the error:

AttributeError: 'str' object has no attribute 'week'.

What am I missing?
I am guessing that the return type of the TransDate class is of a type datetime and I am struggling finding a way to over-pass this. I would much appreciate your help.

from dataclasses import dataclass
from datetime import datetime


@dataclass
class TransDate:
    insDate: str

    #  special method that is called after the object is initialized to perform date validation
    def __post_init__(self):
        self.validate_date()

    # date validation according to 6 date format patterns
    def validate_date(self) -> datetime:
        patterns = ["%d/%m/%Y", "%d-%m-%Y", "%d.%m.%Y", "%Y/%m/%d", "%Y-%m-%d", "%Y.%m.%d"]
        for pattern in patterns:
            try: 
                if valid_date := datetime.strptime(str(self.insDate), pattern):
                    self.insDate = valid_date.date()
                raise ValueError
            except ValueError:
                pass
            
   
    def __repr__(self) -> str:
        return f"{self.day:02}/{self.month:02}/{self.year}"


    @property
    def year(self) -> str:
        return self.insDate.strftime('%Y')

    @property
    def month(self) -> str:
        return self.insDate.strftime('%m')

    @property
    def day(self) -> str:
        return self.insDate.strftime('%d')

    @property
    def qrt(self) -> str:
        match int(self.month):
            case int() as sm if sm in range(1, 4):
                return "01"
            case int() as sm if sm in range(4, 7):
                return "02"
            case int() as sm if sm in range(7, 10):
                return "03"
            case int() as sm if sm in range(10, 13):
                return "04"

    @property
    def weekday(self):
        return self.insDate.strftime('%w')
    
    @property
    def week(self):
        return self.insDate.strftime('%W')
    

@dataclass
class Transaction:
    store: int
    insDate: TransDate
    weekly_sales: float
    

d1 = TransDate("10-05-2015")
print(d1)
print(d1.qrt)
print(d1.week)

tr1 = Transaction(store=1, insDate="10-11-2005", weekly_sales=45100.45)
print(type(tr1.insDate))   # output <class 'str'>
print(tr1.insDate.week)    # output AttributeError: 'str' object has no attribute 'week'

Solution

  • The problem is solved by changing the Transaction class as follows:

    @dataclass
    class Transaction:
        store: int
        insDate: str
        weekly_sales: float
    
    def __post_init__(self):
        if self.insDate:
            self.insDate = TransDate(self.insDate)