I am quite new to Python and Django, and totally new on Stack Overflow, so I hope I won't break any rules here and I respect the question format.
I am facing a problem trying to implement a custom model field with Django (Python 3.3.0, Django 1.5a1), and I didn't find any similar topics, I am actually quite stuck on this one...
So there is a Player, he has got a Hand (of Card). The Hand inherits from CardContainer, which is basically a list of cards with some (hidden here) helper functions. Here is the corresponding code:
from django.db import models
class Card:
def __init__(self, id):
self.id = id
class CardContainer:
def __init__(self, cards=None):
if cards is None:
cards = []
self.cards = cards
class Hand(CardContainer):
def __init__(self, cards=None):
super(Hand, self).__init__(cards)
class CardContainerField(models.CommaSeparatedIntegerField):
__metaclass__ = models.SubfieldBase
def __init__(self, cls, *args, **kwargs):
if not issubclass(cls, CardContainer):
raise TypeError('{} is not a subclass of CardContainer'.format(cls))
self.cls = cls
kwargs['max_length'] = 10
super(CardContainerField, self).__init__(*args, **kwargs)
def to_python(self, value):
if not value:
return self.cls()
if isinstance(value, self.cls):
return value
if isinstance(value, list):
return self.cls([i if isinstance(i, Card) else Card(i) for i in value])
# String: '1,2,3,...'
return self.cls([Card(int(i)) for i in value.split(',')])
def get_prep_value(self, value):
if value is None:
return ''
return ','.join([str(card.id) for card in value.cards])
class Player(models.Model):
hand = CardContainerField(Hand)
But when I get a player, lets say, like this: Player.objects.get(id=3).hand
, instead of getting a Hand
instance (or even a CardContainer
instance at all!), I am just getting a comma-separated string of integers like "1,2,3", which is fine in the database (it is the format I'd like to see IN the database)...
It seems to me that to_python doesn't get called, so the returned data is the raw value, hence the string. When I searched for this type of problems, people missed the __metaclass__ = models.SubfieldBase
... I hoped I could have missed that too but, hey, it would have been too simple!
Did I miss something trivial, or am I wrong for the whole thing? :D
Thanks a lot!!
In python 3 the module-global __metaclass__
variable is no longer supported. You must use:
class CardContainerField(models.CommaSeparatedIntegerField, metaclass=models.SubfieldBase):
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With