Reputation: 8539
I'm trying to define the fields of a model dynamically depending on the key, value pairs contained in a dictionary.
I've attempted two ways:
the dictionary is:
NOTIFICATION_TYPES = {
'friend_request_received': 0,
'friend_request_accepted': 1,
# eccetera
}
the very wrong (generate an exception because self is not defined):
class EmailNotification(models.Model):
"""
User Email Notification Model
Takes care of tracking the user's email notification preferences
"""
user = models.OneToOneField(User, verbose_name=_('user'))
for key, value in NOTIFICATION_TYPES.items():
setattr(self, key, models.BooleanField(_('notify new matches'), default=True))
class Meta:
db_table = 'profile_email_notification'
the apparently less wrong but does not create the model fields:
class EmailNotification(models.Model):
"""
User Email Notification Model
Takes care of tracking the user's email notification preferences
"""
user = models.OneToOneField(User, verbose_name=_('user'))
def __init__(self, *args, **kwargs):
for key, value in NOTIFICATION_TYPES.items():
setattr(self.__class__, key, models.BooleanField(_(key), default=True))
super(EmailNotification, self).__init__(*args, **kwargs)
class Meta:
db_table = 'profile_email_notification'
Is it possible to do what i'm trying to do? I'm sure it is!
Upvotes: 0
Views: 225
Reputation: 8539
Thanks to the suggestions in the answers, but there was a key missing concept.
Two things must be done:
setattr(myclass, key, field)
myclass.add_to_class(key, field)
A working solution here:
Upvotes: 0
Reputation: 1125078
You'll need to set those extra attributes after defining the class:
class EmailNotification(models.Model):
"""
User Email Notification Model
Takes care of tracking the user's email notification preferences
"""
user = models.OneToOneField(User, verbose_name=_('user'))
class Meta:
db_table = 'profile_email_notification'
for key, value in NOTIFICATION_TYPES.items():
setattr(EmailNotification, key, models.BooleanField(_('notify new matches'), default=True))
You could use a class decorator to wrap the for loop into a function applied to the class:
def add_notification(cls):
for key, value in NOTIFICATION_TYPES.items():
setattr(cls, key, models.BooleanField(_('notify new matches'), default=True))
return cls
@add_notification
class EmailNotification:
# ...
I am somewhat worried that the Django metaclass handling wants to process these fields though, and you may need to add additional calls to make the EmailNotification._meta
structure aware of the additional fields that you have added.
Upvotes: 1
Reputation: 880927
You could use a class decorator:
def add_notification(cls):
for key in NOTIFICATION_TYPES:
setattr(cls, key, models.BooleanField(_('notify new matches'), default=True))
return cls
@add_notification
class EmailNotification(models.Model):
"""
User Email Notification Model
Takes care of tracking the user's email notification preferences
"""
user = models.OneToOneField(User, verbose_name=_('user'))
class Meta:
db_table = 'profile_email_notification'
Upvotes: 1