There are many ways to convert an instance to a dictionary, with varying degrees of corner case handling and closeness to the desired result.
1. instance.__dict__
instance.__dict__ which returns
{'_reference1_cache': <OtherModel: OtherModel object>, '_state': <django.db.models.base.ModelState at 0x1f63310>, 'created': datetime.datetime(2014, 2, 21, 4, 38, 51, 844795, tzinfo=<UTC>), 'id': 1L, 'reference1_id': 1L, 'value': 1, 'value2': 2} This is by far the simplest, but is missing reference2, reference1 is misnamed, and it has two unwanted extra things in it.
2. model_to_dict
from django.forms.models import model_to_dict model_to_dict(instance) which returns
{u'id': 1L, 'reference1': 1L, 'reference2': [1L], 'value': 1} This is the only one with reference2, but is missing the uneditable fields.
3. model_to_dict(..., fields=...)
from django.forms.models import model_to_dict model_to_dict(instance, fields=[field.name for field in instance._meta.fields]) which returns
{u'id': 1L, 'reference1': 1L, 'value': 1} This is strictly worse than the standard model_to_dict invocation.
4. query_set.values()
SomeModel.objects.filter(id=instance.id).values()[0] which returns
{'created': datetime.datetime(2014, 2, 21, 4, 38, 51, tzinfo=<UTC>), u'id': 1L, 'reference1_id': 1L, 'value': 1L, 'value2': 2L} This is the same output as instance.__dict__ but without the extra fields. reference1_id is still wrong and reference2 is still missing.
5. Custom Function
The code for django's model_to_dict had most of the answer. It explicitly removed non-editable fields, so removing that check results in the following code which behaves as desired:
from django.db.models.fields.related import ManyToManyField def to_dict(instance): opts = instance._meta data = {} for f in opts.concrete_fields + opts.many_to_many: if isinstance(f, ManyToManyField): if instance.pk is None: data[f.name] = [] else: data[f.name] = list(f.value_from_object(instance).values_list('pk', flat=True)) else: data[f.name] = f.value_from_object(instance) return data While this is the most complicated option, calling to_dict(instance) gives us exactly the desired result:
{'created': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>), u'id': 1, 'reference1': 1, 'reference2': [1], 'value': 1, 'value2': 2} 6. Use Serializers
Django Rest Framework's ModelSerialzer allows you to build a serializer automatically from a model.
from rest_framework import serializers class SomeModelSerializer(serializers.ModelSerializer): class Meta: model = SomeModel fields = "__all__" SomeModelSerializer(instance).data returns
{'created': '2018-12-20T21:27:07.714627Z', 'id': 1, 'reference1': 1, 'reference2': [1], 'value': 1, 'value2': 2} This is almost as good as the custom function, but created is a string instead of a datetime object.
Bonus Round: better model printing
If you want a django model that has a better python command-line display, have your models child-class the following:
from django.db import models from django.db.models.fields.related import ManyToManyField class PrintableModel(models.Model): def __repr__(self): return str(self.to_dict()) def to_dict(self): opts = self._meta data = {} for f in opts.concrete_fields + opts.many_to_many: if isinstance(f, ManyToManyField): if self.pk is None: data[f.name] = [] else: data[f.name] = list(f.value_from_object(self).values_list('pk', flat=True)) else: data[f.name] = f.value_from_object(self) return data class Meta: abstract = True So, for example, if we define our models as such:
class OtherModel(PrintableModel): pass class SomeModel(PrintableModel): value = models.IntegerField() value2 = models.IntegerField(editable=False) created = models.DateTimeField(auto_now_add=True) reference1 = models.ForeignKey(OtherModel, related_name="ref1") reference2 = models.ManyToManyField(OtherModel, related_name="ref2") Calling SomeModel.objects.first() now gives output like this:
{'created': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>), 'value': 1, 'value2': 2, 'reference1': 1, u'id': 1, 'reference2': [1]}