Currently I have a lot of python objects in my code similar to the following:
class MyClass(): def __init__(self, name, friends): self.myName = name self.myFriends = [str(x) for x in friends]
Now I want to turn this into a Django model, where self.myName is a string field, and self.myFriends is a list of strings.
from django.db import models class myDjangoModelClass(): myName = models.CharField(max_length=64) myFriends = ??? # what goes here?
Since the list is such a common data structure in python, I sort of expected there to be a Django model field for it. I know I can use a ManyToMany or OneToMany relationship, but I was hoping to avoid that extra indirection in the code.
I added this related question, which people may find useful.
Would this relationship not be better expressed as a one-to-many foreign key relationship to a
Friends table? I understand that
myFriends are just strings but I would think that a better design would be to create a
Friend model and have
MyClass contain a foreign key realtionship to the resulting table.
With that firmly in mind, let's do this! Once your apps hit a certain point, denormalizing data is very common. Done correctly, it can save numerous expensive database lookups at the cost of a little more housekeeping.
To return a
list of friend names we'll need to create a custom Django Field class that will return a list when accessed.
David Cramer posted a guide to creating a SeperatedValueField on his blog. Here is the code:
from django.db import models class SeparatedValuesField(models.TextField): __metaclass__ = models.SubfieldBase def __init__(self, *args, **kwargs): self.token = kwargs.pop('token', ',') super(SeparatedValuesField, self).__init__(*args, **kwargs) def to_python(self, value): if not value: return if isinstance(value, list): return value return value.split(self.token) def get_db_prep_value(self, value): if not value: return assert(isinstance(value, list) or isinstance(value, tuple)) return self.token.join([unicode(s) for s in value]) def value_to_string(self, obj): value = self._get_val_from_obj(obj) return self.get_db_prep_value(value)
The logic of this code deals with serializing and deserializing values from the database to Python and vice versa. Now you can easily import and use our custom field in the model class:
from django.db import models from custom.fields import SeparatedValuesField class Person(models.Model): name = models.CharField(max_length=64) friends = SeparatedValuesField()