by Ismail Elshareef
If you're using MongoDB with Django, you will need to manage ListField and EmbeddedModelField type fields in Django's admin module. Unfortunately, that's not so easy to do out of the box. You need to do some hacking to get it done. Here's a good link on how to use ListField in admin https://gist.github.com/1200165.
Now let's find out how we can use EmbeddedModelField with admin. Let's start hacking :)
Let's say you have an application that defines a collection (i.e. table) of users. Your models.py and admin.py will look something like this:
from django.db import models
from djangotoolbox.fields import ListField, EmbeddedModelField
class User(models.Model):
name = EmbeddedModelField('Name')
email = models.EmailField(max_length=255, db_index=True)
def __unicode__(self):
return '%s %s (%s)' % (self.name.fn, self.name.ln, self.email)
class Name(models.Model):
fn = models.CharField(max_length=50)
ln = models.CharField(max_length=50)
def __unicode__(self):
return '%s %s' % (self.fn, self.ln)
from django.contrib.admin import site
from user_profile.models import User
site.register(User)
That's great, except now you won't be able to administer users in Django's admin because Django will treat "name" as a unicode string and not an EmbeddedModelField field. We need to change that.
First thing you need to do is to create a forms.py file in the same directory as models.py and admin.py. In the forms.py file, you need to define a class that handles EmbeddedModelField objects to make it work with Django's admin.
The class has two methods: prepare_value and to_python.
prepare_value transforms a MongoDB "Name" object (modeled after Name model) to a string in the format "key=value, key=value, ...etc" so it's editable in Django's admin.
to_python takes a string that looks like "key=value, key=value, ...etc" and transforms it into a dictionary representation of the Name model.
from django import forms
class ObjectListField(forms.CharField):
def prepare_value(self, value):
if not value:
return ''
newvalue = {}
for key, val in value.__dict__.items():
if type(val) is unicode:
newvalue[key] = val
return ", ".join(["%s=%s" % (k, v) for k, v in newvalue.items()])
def to_python(self, value):
if not value:
return {}
obj = {}
lst = [item.strip() for item in value.split(',')]
for item in lst:
val = item.split('=');
obj[val[0]] = val[1]
return obj
Now that you have forms.py set up, you need to update models.py. You will need to define a class that overrides EmbeddedModelField to take advantage of the new form we just defined in forms.py.
from django.db import models
from djangotoolbox.fields import EmbeddedModelField
from .forms import ObjectListField
class EmbedOverrideField(EmbeddedModelField):
def formfield(self, **kwargs):
return models.Field.formfield(self, ObjectListField, **kwargs)
class User(models.Model):
name = EmbedOverrideField('Name')
email = models.EmailField(max_length=255, db_index=True)
def __unicode__(self):
return '%s %s (%s)' % (self.name.fn, self.name.ln, self.email)
class Name(models.Model):
fn = models.CharField(max_length=50)
ln = models.CharField(max_length=50)
def __unicode__(self):
return '%s %s' % (self.fn, self.ln)
Now sync your db and test it out. You should be good to go!
Thanks a lot!!
But I got this problem and I don´t understand why.
Exception Value: list index out of range
Exception Location: C:\Users\DARIO\Desktop\Escritorio\nanomag\askwer\forms.py in to_python, line 24
Sorry for my poor english.
I hope that you can help me. Thanks