views:

522

answers:

6

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.

Edit:

I added this related question, which people may find useful.

+7  A: 

Would not this relationship 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.

Andrew Hare
This is probably what I will end up doing, but I was really hoping the underlying structure for this would have been built in. I guess I am to o lazy.
grieve
+2  A: 

Remember that this eventually has to end up in a relational database. So using relations really is the common way to solve this problem. If you absolutely insist on storing a list in the object itself, you could make it for example comma-separated, and store it in a string, and then provide accessor functions that split the string into a list. With that, you will be limited to a maximum number of strings, and you will lose efficient queries.

Martin v. Löwis
I am fine with the database storing it as a relation, I was hoping the Django models abstracted that portion out for me already. From the app side I am always going to want to treat it as a list of strings.
grieve
+1  A: 

You can store virtually any object using a Django Pickle Field, ala this snippet:

http://www.djangosnippets.org/snippets/513/

Harold
Which is database dependent.
drozzy
No, it isn't. Read the description of the snippet.
Harold
+3  A: 
class Course(models.Model):
   name = models.CharField(max_length=256)
   students = models.ManyToManyField(Student)

class Student(models.Model):
   first_name = models.CharField(max_length=256)
   student_number = models.CharField(max_length=128)
   # other fields, etc...

   friends = models.ManyToManyField('self')
drozzy
+4  A: 

"Premature optimization is the root of all evil."

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 an 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)

The logic of this code deals with serializing and deserializing values from the database to Python and visa-versa. Now can 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()
jb
+1 for a great answer, but we are already doing something like this. It is really squishing all the values into one string then splitting them out. I guess I was hoping for something more like a ListofStringsField, which actually builds the separate table and makes the foreign keys automatically. I am not sure if that is possible in Django. If it is, and I find an answer, I will post it on stackoverflow.
grieve
If that's the case then you're looking for initcrash's django-denorm. You'll find it on github: http://github.com/initcrash/django-denorm/tree/master
jb
A: 

Using one-to-many relation (FK from Friend to parent class) will make your app more scalable (as you can trivially extend the Friend object with additional attributes beyond the simple name). And thus this is the best way

Guard