Search code examples
pythonpydantic

Possibility of Incrementing a Field in Pydantic


I'm trying to use pydantic to scrape an api endpoint. These endpoints are a little tricky to scrape in one go, so I need some identifier to merge them together. I decided to use ab_number (at-bat number) to be that unique identifier. The issue I'm having is incrementing it through the list of each at-bats. Here's my code:

import requests
from pydantic import BaseModel, Field
from typing import List

url = "https://statsapi.mlb.com/api/v1.1/game/745707/feed/live/diffPatch"
response = requests.get(url).json()

class AtBat(BaseModel):
   ab_number: int = Field(None) # Ideally I don't want this here, but it's there to bypass the missing error
   _counter: int = 1
   def __init__(self, **data):
       super().__init__(**data)
       self.ab_number = AtBat._counter
       AtBat._counter += 1
       print(self.ab_number) # Can use this as a debugging check

class Plays(BaseModel):
   allPlays: List[AtBat]

data = Plays(**response['liveData']['plays'])

The error I run into with this code is:

TypeError: unsupported operand type(s) for +=: 'ModelPrivateAttr' and 'int'

What I want it to return:

allPlays = [AtBat(ab_number=1), AtBat(ab_number=2), AtBat(ab_number=3), ..., ]

I tried looking into the docs on if you can access the value of private attribute, but there doesn't appear to be so I'm not sure if there are other ways to do this. I just want something that increments as simple as how a primary key would auto increment in mysql.


Solution

  • I used the ClassVar type cast then created a reset class method to reset the counter if there are multiple games being scraped so it doesn't keep incrementing to n plays.

    import requests
    from pydantic import BaseModel
    from typing import List, ClassVar
    
    url = "https://statsapi.mlb.com/api/v1.1/game/745707/feed/live/diffPatch"
    response = requests.get(url).json()
    
    class AtBat(BaseModel):
       ab_number: int = None 
       _counter: ClassVar[int] = 1
       
       def __init__(self, **data):
           super().__init__(**data)
           self.ab_number = AtBat._counter
           AtBat._counter += 1
        
       @classmethod 
       def reset_count(cls):
            cls._counter = 1
    
    class Plays(BaseModel):
       allPlays: List[AtBat]
    
       def __init__(self, **data):
           super().__init__(**data)
           AtBat.reset_count()
    
    data = Plays(**response['liveData']['plays'])
    print(data)
    

    Appreciate the help.