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

 {'_foreign_key_cache': <OtherModel: OtherModel object>,
 '_state': <django.db.models.base.ModelState at 0x7ff0993f6908>,
 'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=<UTC>),
 'foreign_key_id': 2,
 'id': 1,
 'normal_value': 1,
 'readonly_value': 2}

This is by far the simplest, but is missing `many_to_many`, `foreign_key` 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

 {'foreign_key': 2,
 'id': 1,
 'many_to_many': [<OtherModel: OtherModel object>],
 'normal_value': 1}

This is the only one with `many_to_many`, 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

 {'foreign_key': 2, 'id': 1, 'normal_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

 {'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=<UTC>),
 'foreign_key_id': 2,
 'id': 1,
 'normal_value': 1,
 'readonly_value': 2}

This is the same output as `instance.__dict__` but without the extra fields.
`foreign_key_id` is still wrong and `many_to_many` 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:

 {'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=<UTC>),
 'foreign_key': 2,
 'id': 1,
 'many_to_many': [2],
 'normal_value': 1,
 'readonly_value': 2}

----------

## 6. Use Serializers

[Django Rest Framework][1]'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

 {'auto_now_add': '2018-12-20T21:34:29.494827Z',
 'foreign_key': 2,
 'id': 1,
 'many_to_many': [2],
 'normal_value': 1,
 'readonly_value': 2}

This is almost as good as the custom function, but auto_now_add 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):
 normal_value = models.IntegerField()
 readonly_value = models.IntegerField(editable=False)
 auto_now_add = models.DateTimeField(auto_now_add=True)
 foreign_key = models.ForeignKey(OtherModel, related_name="ref1")
 many_to_many = models.ManyToManyField(OtherModel, related_name="ref2")

Calling `SomeModel.objects.first()` now gives output like this:


 {'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=<UTC>),
 'foreign_key': 2,
 'id': 1,
 'many_to_many': [2],
 'normal_value': 1,
 'readonly_value': 2}

 [1]: https://www.django-rest-framework.org/