Go – Django: Structure Django Model to allow Arbitrary Fieldtypes


I'd like to make a user profile app in Django (I know there are some that exist, thanks) and I'm wondering how you would structure the models to allow for an arbitrary combination fields in each sub-section.

As an example, the section 'education' may have a sub-section called 'Programming Experience', and the section 'personal info' may have a sub-section called 'favourites'.

Thinking in terms of a typical side bar navigation setup each section would be a header, and each sub-section would be a link to a form where the information can be manipulated.

- Schooling
- Programming Experience

Personal Info
- Location
- Favourites
- Look a-likes

What I'd like to do is be able to add items to the sub-sections on an Arbitrary basis. Whatever the feel of the site calls for.

Maybe one site would benefit from photos of the school a user attended, while another might only need a description.

I'd like to use the admin interface to add these field types to the sub-section. So adding an item would present the choice of what type of information it is (image, video, text, etc) and what sub-section it's to be applied to.

I'd like to know how you would accomplish this; and more importantly, by jumping through as few hoops as possible.



To hopefully clarify the question I'll provide a sample models.py file. This is just a quick moch-up to demonstrate the problem more accurately. I have two solutions in mind, and I think solution two will work better than solution one; but I'd also like to here what the SO community thinks and if they have any other solutions of their own.


class Section(models.Model):
    The root of categorization. Acts much like a header
    name = models.CharField(max_length=30)
    description = models.CharField(max_length=255)

class SubSection(models.Model):
    The contents of each section. Contains many items of varying types as needed
    by the site developer.
    name = models.CharField(max_length=30)
    description = models.CharField(max_length=255)
    section = models.ForeignKey(Section)

class Item(models.Model):
    I would like this to store the information here and have a foreign key to the
    'SubSection' table. The problem is that there are a lot of different information
    types that can be stored and I'd need a row for each type. Thus for each
    entry most of the columns will be blank.

    I'm thinking that it may be better to use this table as a pointer to another
    table that actually contains the information. This will result in a lot of
    tables but will eliminate waste.

    name = models.CharField(max_length=30)
    description = models.CharField(max_length=255)
    sub_section = models.ForeignKey(SubSection)

    ### Solution One
    # Storing the info here results in a lot of wasted space and may not be all
    # that flexible
    image = models.ImageField()
    text = models.CharField(max_length=255)
    numeric = models.IntegerField()
    time = models.TimeField()
    # etc ...

    ### Solution Two
    # Storing the field info results in more tables but allows for a better match
    # of information and field type.
    field_type = models.CharField(max_length=255)
    field_properties = models.CommaSeparatedIntegerField(max_length=None)

### Solution Two Tables
# Solution two would require a table for each field type supported here, which
# is quite a few different types.

class ImageStorage(models.Model):
    item = models.ForeignKey(Item)
    information = models.ImageField()

class IntegerStorage(models.Model):
    item = models.ForeignKey(Item)
    information = models.IntegerField()

### etc ...

Just keep in mind it's targeted at user profiles. So a weight loss site may want the users current weight in the profile (numeric information) while a travel site may want a list of places visited (text information, could even use the IPAddressField I suppose). I just have no idea what will pop up so I'm trying to make it as generic as possible.

Best Solution

If I'm understanding you properly, the simplest way to do this would likely be a many-to-one relationship. I'm not sure if you wanted these on a per-user or per-site basis so I'll assume you want to customize it per-site (switching that is easy).

Create a table that looks something like this:

class Section(models.Model):
    section = models.CharField()
    sub-section = model.CharField()
    site = models.ForeignKey(Site)

For multiple subsections that belong to the same section, simply give them the same primary section name so that you can query the DB for the accompanying subsections using that name.

Another way would be to use two tables to accomplish the same thing, but given the application I think this might be more appropriate.

Related Question