Search code examples
pythonmagic-methodspython-classpython-object

How to make a Python class properties accesible by index?


I have a Django model like this:

class Competitor(models.Model):
  """
  Competitor model object
  """
  name = models.CharField(max_length=20)
  easy = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Easy Mode')
  hard = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Hard Mode')
  tematicas = ArrayField(models.PositiveSmallIntegerField(), size=7, null=True, blank=True, verbose_name='Tematicas')
  random_score = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Random Mode')
  min1 = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='minuto 1')
  min2 = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='minuto 2')
  deluxe = ArrayField(models.PositiveSmallIntegerField(), size=14, null=True, blank=True, verbose_name='Deluxe')
  replica = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Replica')

And I want to be able to access the properties with index, therefore, if I write competitor[0], it should return the value of name.

I have looked, and according to this question, I have to "Implement both __iter__() and __getitem__() et alia methods." But I have no idea what to do inside this methods.

Anyone knows how to do this?


Solution

  • First of all, I want to thank @julianofischer for his answer. It taught me how to implement the methods I will implement in this answer.

    First add a list of strings with the names of the fields you want to be able to access with [] as a propperty of the class, like this:

    class Competitor(Model):
      """
      Competitor model object
      """
      name = CharField(max_length=20)
      easy = ArrayField(PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Easy Mode')
      hard = ArrayField(PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Hard Mode')
      tematicas = ArrayField(PositiveSmallIntegerField(), size=7, null=True, blank=True, verbose_name='Tematicas')
      random_score = ArrayField(PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Random Mode')
      min1 = ArrayField(PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='minuto 1')
      min2 = ArrayField(PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='minuto 2')
      deluxe = ArrayField(PositiveSmallIntegerField(), size=14, null=True, blank=True, verbose_name='Deluxe')
      replica = ArrayField(PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Replica')
      _list = [ 'easy', 'hard', 'tematicas', 'random_score', 'min1', 'min2', 'deluxe', 'replica' ]
    

    Then, if you want both to get and set the values, you have to implement __getitem__ and __setitem__, like this:

    def __getitem__(self, index: int):
      return self.__dict__[self._list[index]]
    
    def __setitem__(self, index: int, value: list):
      self.__dict__[self._list[index]] = value
      self.save(update_fields=[self._list[index]])
    

    This way, you are accesing the values of the fields with __getitem__, using .__dict__[index], and returning those, and with __setitem__, you are modifying those directly.