So I've got a UserProfile in Django that has certain fields that are required by the entire project - birthday, residence, etc. - and it also contains a lot of information that doesn't actually have any importance as far as logic goes - hometown, about me, etc. I'm trying to make my project a bit more flexible and applicable to more situations than my own, and I'd like to make it so that administrators of a project instance can add any fields they like to a UserProfile without having to directly modify the model. That is, I'd like an administrator of a new instance to be able to create new attributes of a user on the fly based on their specific needs. Due to the nature of the ORM, is this possible?
-
I would rather have another table with `key:value` pairs where `key` describes what kind of attribute is it (website, address) and `value` is something like "google.com", "Mountain View" etc. and relate those pairs with `UserProfile`. – Davor Lucic Jun 09 '11 at 15:58
2 Answers
Well a simple solution is to create a new model called UserAttribute that has a key and a value, and link it to the UserProfile. Then you can use it as an inline in the django-admin. This would allow you to add as many new attributes to a UserProfile as you like, all through the admin:
models.py
class UserAttribute(models.Model):
key = models.CharField(max_length=100, help_text="i.e. Age, Name etc")
value = models.TextField(max_length=1000)
profile = models.ForeignKey(UserProfile)
admin.py
class UserAttributeInline(admin.StackedInline):
model = UserAttribute
class UserProfile(admin.ModelAdmin):
inlines = [UserAttibuteInline,]
This would allow an administrator to add a long list of attributes. The limitations are that you cant's do any validation on the input(outside of making sure that it's valid text), you are also limited to attributes that can be described in plain english (i.e. you won't be able to perform much login on them) and you won't really be able to compare attributes between UserProfiles (without a lot of Database hits anyway)

- 53,000
- 18
- 155
- 177
-
1+1 Agreed, this is [database normalization](http://en.wikipedia.org/wiki/Database_normalization) issue not Django ORM. – Davor Lucic Jun 09 '11 at 16:00
-
Yeah, I'd seen this suggestion elsewhere on stackoverflow, and while it works very well if all I want to do is store string values, it sort of falls apart if you want to, say, have an email field with proper validation, or a datetime field at all. – NSU Jun 09 '11 at 18:07
-
then you are talking about dynamic form fields. I actually just asked a question about this on convore as I was wondering about it myself: https://convore.com/django-community/dynamic-formsform-building/ – Timmy O'Mahony Jun 09 '11 at 18:34
You can store additional data in serialized state. This can save you some DB hits and simplify your database structure a bit. May be the best option if you plan to use the data just for display purposes.
Example implementation (not tested)::
import yaml
from django.db import models
class UserProfile(models.Model):
user = models.OneToOneField('auth.User', related_name='profile')
_additional_info = models.TextField(default="", blank=True)
@property
def additional_info(self):
return yaml.load(self._additional_info)
@additional_info.setter
def additional_info(self, user_info_dict):
self._additional_info = yaml.dump(user_info_dict)
When you assign to profile.additional_info
, say, a dictionary, it gets serialized and stored in _additional_info
instead (don't forget to save the instance later). And then, when you access additional_info
, you get that python dictionary.
I guess, you can also write a custom field to deal with this.
UPDATE (based on your comment):
So it appears that the actual problem here is how to automatically create and validate forms for user profiles. (It remains regardless on whether you go with serialized options or complex data structure.)
And since you can create dynamic forms without much trouble[1], then the main question is how to validate them.
Thinking about it... Administrator will have to specify validators (or field type) for each custom field anyway, right? So you'll need some kind of a configuration option—say,
CUSTOM_PROFILE_FIELDS = (
{
'name': 'user_ip',
'validators': ['django.core.validators.validate_ipv4_address'],
},
)
And then, when you're initializing the form, you define fields with their validators according to this setting.
[1] See also this post by Jacob Kaplan-Moss on dynamic form generation. It doesn't deal with validation, though.

- 32,294
- 8
- 53
- 61
-
I actually already wrote this system using JSON and that's a huge pain if you want to create forms automatically along with the data in your models, particularly if you want correct validators. It's definitely possible to create forms that dynamically grow based on what's in the additional_info, but getting validated form fields is a lot harder. I'm not going to say impossible, but I just don't know of a way. – NSU Jun 09 '11 at 17:48
-
1Thanks a bunch for the update, I'm going to give that a shot when I get out of this meeting. – NSU Jun 09 '11 at 18:11