Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How would you inherit from and override the django model classes to create a listOfStringsField?

I want to create a new type of field for django models that is basically a ListOfStrings. So in your model code you would have the following:

models.py:

from django.db import models

class ListOfStringsField(???):
    ???

class myDjangoModelClass():
    myName = models.CharField(max_length=64)
    myFriends = ListOfStringsField() # 

other.py:

myclass = myDjangoModelClass()
myclass.myName = "bob"
myclass.myFriends = ["me", "myself", "and I"]

myclass.save()

id = myclass.id

loadedmyclass = myDjangoModelClass.objects.filter(id__exact=id)

myFriendsList = loadedclass.myFriends
# myFriendsList is a list and should equal ["me", "myself", "and I"]

How would you go about writing this field type, with the following stipulations?

  • We don't want to do create a field which just crams all the strings together and separates them with a token in one field like this. It is a good solution in some cases, but we want to keep the string data normalized so tools other than django can query the data.
  • The field should automatically create any secondary tables needed to store the string data.
  • The secondary table should ideally have only one copy of each unique string. This is optional, but would be nice to have.

Looking in the Django code it looks like I would want to do something similar to what ForeignKey is doing, but the documentation is sparse.

This leads to the following questions:

  • Can this be done?
  • Has it been done (and if so where)?
  • Is there any documentation on Django about how to extend and override their model classes, specifically their relationship classes? I have not seen a lot of documentation on that aspect of their code, but there is this.

This is comes from this question.

like image 924
grieve Avatar asked Jul 14 '09 16:07

grieve


3 Answers

There's some very good documentation on creating custom fields here.

However, I think you're overthinking this. It sounds like you actually just want a standard foreign key, but with the additional ability to retrieve all the elements as a single list. So the easiest thing would be to just use a ForeignKey, and define a get_myfield_as_list method on the model:

class Friends(model.Model):
    name = models.CharField(max_length=100)
    my_items = models.ForeignKey(MyModel)

class MyModel(models.Model):
    ...

    def get_my_friends_as_list(self):
        return ', '.join(self.friends_set.values_list('name', flat=True))

Now calling get_my_friends_as_list() on an instance of MyModel will return you a list of strings, as required.

like image 151
Daniel Roseman Avatar answered Oct 03 '22 00:10

Daniel Roseman


What you have described sounds to me really similar to the tags.
So, why not using django tagging?
It works like a charm, you can install it independently from your application and its API is quite easy to use.

like image 29
rob Avatar answered Oct 02 '22 23:10

rob


I also think you're going about this the wrong way. Trying to make a Django field create an ancillary database table is almost certainly the wrong approach. It would be very difficult to do, and would likely confuse third party developers if you are trying to make your solution generally useful.

If you're trying to store a denormalized blob of data in a single column, I'd take an approach similar to the one you linked to, serializing the Python data structure and storing it in a TextField. If you want tools other than Django to be able to operate on the data then you can serialize to JSON (or some other format that has wide language support):

from django.db import models
from django.utils import simplejson

class JSONDataField(models.TextField):
    __metaclass__ = models.SubfieldBase

    def to_python(self, value):
        if value is None: 
            return None
        if not isinstance(value, basestring): 
            return value
        return simplejson.loads(value)

    def get_db_prep_save(self, value):
        if value is None: 
            return None
        return simplejson.dumps(value)

If you just want a django Manager-like descriptor that lets you operate on a list of strings associated with a model then you can manually create a join table and use a descriptor to manage the relationship. It's not exactly what you need, but this code should get you started.

like image 33
mmalone Avatar answered Oct 03 '22 00:10

mmalone