Take the 2-minute tour ×

I have a model lets say MyModel which has a foreign key to another model say Tag.

class MyModel(models.Model):
    tag = models.ManyToMany(Tag)

I have approximately 50,000 instances of MyModel and each MyModel can have 100 Tags.

If I use the above model I will get 5,000,000 entries in a table, mymodel_tag but I can use all the features of ORM.

However If I write custom methods and treat above field as an array of integers and write custom code to retrieve/save ids of tags associated with MyModel, I will have just 50K entries but I will have to write custom code for retrieval etc.

a) I want to know the pros and cons of both the approaches!

b) If I have to have to use the custom array approach how can i do it efficiently.

share|improve this question
add comment

2 Answers


tag = models.ManyToManyField(Tag)


With a foreign key, MyModel can only be associated with one and only one Tag. I'm honestly not even sure how you were able to give each one 100 Tags without have to duplicate each MyModel 100 times. If you were doing that, no wonder you're not liking the results.

ManyToManyField creates a join table that will consist only of an id (integer) reference to MyModel and an id (integer) reference to Tag. That is the most compact you'll ever get with this type of relationship, and it's the best practice, anyways.

share|improve this answer
You are absolutely right, I meant ManyToMany(that is why mentioned the join table also) but ended up writing as foreign key. But the question is is it better to have a join table with 5million rows or shall I save it as an array of ids in another column? If I use another column then what is the best way to do that. Thanks! –  Ajay Yadav Jun 6 '11 at 16:35
The join table will be indexed, so I don't think the amount of rows truly matters. In general, it's always best practice to follow best practices, and ManyToManyField behaves as it does in Django because software developers have pretty much universally agreed that join tables are the best way to handle many-to-many relationships. –  Chris Pratt Jun 6 '11 at 22:03
add comment

Although I totally agree with what chrisdpratt says but unfortunately, I have been forced to do it otherwise. Here is one way I found of doing that at http://djangosnippets.org/snippets/1200/:

from django.db import models
from django import forms

class MultiSelectFormField(forms.MultipleChoiceField):
    widget = forms.CheckboxSelectMultiple

        self.max_choices = kwargs.pop('max_choices', 0)
        super(MultiSelectFormField, self).__init__(*args, **kwargs)

    def clean(self, value):
        if not value and self.required:
            raise forms.ValidationError(self.error_messages['required'])
        if value and self.max_choices and len(value) > self.max_choices:
            raise forms.ValidationError('You must select a maximum of %s choice%s.'
                    % (apnumber(self.max_choices), pluralize(self.max_choices)))
        return value

class MultiSelectField(models.Field):
    __metaclass__ = models.SubfieldBase

    def get_internal_type(self):
        return "CharField"

    def get_choices_default(self):
        return self.get_choices(include_blank=False)

    def _get_FIELD_display(self, field):
        value = getattr(self, field.attname)
        choicedict = dict(field.choices)

    def formfield(self, **kwargs):
        # don't call super, as that overrides default widget if it has choices
        defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 
                    'help_text': self.help_text, 'choices':self.choices}
        if self.has_default():
            defaults['initial'] = self.get_default()
        return MultiSelectFormField(**defaults)

    def get_db_prep_value(self, value):
        if isinstance(value, basestring):
            return value
        elif isinstance(value, list):
            return ",".join(value)

    def to_python(self, value):
        if isinstance(value, list):
            return value
        return value.split(",")

    def contribute_to_class(self, cls, name):
        super(MultiSelectField, self).contribute_to_class(cls, name)
        if self.choices:
            func = lambda self, fieldname = name, choicedict = dict(self.choices):",".join([choicedict.get(value,value) for value in getattr(self,fieldname)])
            setattr(cls, 'get_%s_display' % self.name, func)
share|improve this answer
add comment

Your Answer


