Login

Convert an instance to a dictionary for use in newforms

Author:
SmileyChris
Posted:
April 23, 2007
Language:
Python
Version:
.96
Tags:
newforms
Score:
3 (after 3 ratings)

Useful for when you want to use an instance's values as the initial values of a form which you didn't use form_for_instance to create.

Handles foreign keys and many-to-many fields just fine.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
def instance_dict(instance, key_format=None):
    "Returns a dictionary containing field names and values for the given instance"
    from django.db.models.fields.related import ForeignKey
    if key_format:
        assert '%s' in key_format, 'key_format must contain a %s'
    key = lambda key: key_format and key_format % key or key

    d = {}
    for field in instance._meta.fields:
        attr = field.name
        value = getattr(instance, attr)
        if value is not None and isinstance(field, ForeignKey):
            value = value._get_pk_val()
        d[key(attr)] = value
    for field in instance._meta.many_to_many:
        d[key(field.name)] = [obj._get_pk_val() for obj in getattr(instance, field.attname).all()]
    return d

More like this

Comments

akaihola (on September 25, 2007):
<p>The function didn't handle dates correctly, at least when feeding instance data to a form with a SelectDateWidget.</p> <p>I also wanted to use it for unsaved objects, but they failed on many-to-many fields.</p> <p>Here's a version which fixes both these problems:</p> <pre>def instance_dict(instance, key_format=None): """ Returns a dictionary containing field names and values for the given instance """ from django.db.models.fields import DateField from django.db.models.fields.related import ForeignKey if key_format: assert '%s' in key_format, 'key_format must contain a %s' key = lambda key: key_format and key_format % key or key pk = instance._get_pk_val() d = {} for field in instance._meta.fields: attr = field.name value = getattr(instance, attr) if value is not None: if isinstance(field, ForeignKey): value = value._get_pk_val() elif isinstance(field, DateField): value = value.strftime('%Y-%m-%d') d[key(attr)] = value for field in instance._meta.many_to_many: if pk: d[key(field.name)] = [ obj._get_pk_val() for obj in getattr(instance, field.attname).all()] else: d[key(field.name)] = [] return d </pre>

#

stuaxo (on July 27, 2012):
<p>I've wanted one that could traverse foreign keys.</p> <p>This adds on the date improvement version above and is tested on django 1.4.</p> <p>Foreign keys come back as foreignkeyname.foreignkeyvalue in the dictionary, it's recursive so will pull back all the relations (only tested with one level of foreignkey).</p> <pre>def instance_dict(instance, key_format=None): """ Returns a dictionary containing field names and values for the given instance """ from django.db.models.fields import DateField from django.db.models.fields.related import ForeignKey if key_format: assert '%s' in key_format, 'key_format must contain a %s' key = lambda key: key_format and key_format % key or key d = {} for field in instance._meta.fields: attr = field.name value = getattr(instance, attr) if value is not None: if isinstance(field, ForeignKey): fkey_values = instance_dict(value) for k, v in fkey_values.items(): d['%s.%s' % (key(attr), k)] = v continue elif isinstance(field, DateField): value = value.strftime('%Y-%m-%d') d[key(attr)] = value for field in instance._meta.many_to_many: if pk: d[key(field.name)] = [ obj._get_pk_val() for obj in getattr(instance, field.attname).all()] else: d[key(field.name)] = [] return d </pre>

#

stuaxo (on October 24, 2012):
<p>This turned out to be incompatible with django-filer so I've added a hasattr test.</p> <p>Now tested + working on django 1.4.1</p> <pre>def instance_dict(instance, key_format=None): """ Returns a dictionary containing field names and values for the given instance """ from django.db.models.fields import DateField from django.db.models.fields.related import ForeignKey if key_format: assert '%s' in key_format, 'key_format must contain a %s' key = lambda key: key_format and key_format % key or key d = {} for field in instance._meta.fields: attr = field.name if hasattr(instance, attr): # django filer broke without this check value = getattr(instance, attr) if value is not None: if isinstance(field, ForeignKey): fkey_values = instance_dict(value) for k, v in fkey_values.items(): d['%s.%s' % (key(attr), k)] = v continue elif isinstance(field, DateField): value = value.strftime('%Y-%m-%d') d[key(attr)] = value for field in instance._meta.many_to_many: if pk: d[key(field.name)] = [ obj._get_pk_val() for obj in getattr(instance, field.attname).all()] else: d[key(field.name)] = [] return d </pre>

#

vandorjw (on October 13, 2014):
<p>Previous versions did not properly handle inherited models, or ImageFields</p> <pre># method to convert a model to a flat dictionary def instance_to_dict(instance, fields=None, exclude=None): """ Returns a dict containing the data in ``instance`` suitable for converting to JSON. ``fields`` is an optional list of field names. If provided, only the named fields will be included in the returned dict. ``exclude`` is an optional list of field names. If provided, the named fields will be excluded from the returned dict, even if they are listed in the ``fields`` argument. """ # avoid a circular imports from django.db.models.fields import DateField, TimeField from django.db.models.fields.files import ImageField from django.db.models.fields.related import ForeignKey, OneToOneField data = {} for field in instance._meta.fields: if fields and field.name not in fields: continue if exclude and field.name in exclude: continue attr = field.name if hasattr(instance, attr): value = getattr(instance, attr) if value is not None: if isinstance(field, OneToOneField): # knock out duplicate inherited data. # must come before Foreignkey check! continue elif isinstance(field, ForeignKey): fkey_values = instance_to_dict(value) for k, v in fkey_values.items(): data['%s.%s' % (attr, k)] = v continue elif isinstance(field, DateField): value = value.strftime('%Y-%m-%d') elif isinstance(field, TimeField): value = value.strftime('%H-%M-%S') elif isinstance(field, ImageField): value = value.url data[field.name] = value for field in instance._meta.many_to_many: data[field.name] = [obj._get_pk_val() for obj in getattr(instance, field.attname).all()] return data </pre>

#

Please login first before commenting.