Django
Python
Model Serialization
Django ORM
Data Conversion

Convert Django Model object to dict with all of the fields intact

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Converting a Django model instance to a dictionary is needed for JSON API responses, logging, testing, and data export. Django provides several built-in approaches: model_to_dict() from django.forms.models, the __dict__ attribute, and values() on QuerySets. Each has trade-offs regarding which fields are included (foreign keys, non-editable fields, many-to-many relations). This article covers all methods and their differences.

Using model_to_dict()

python
1from django.forms.models import model_to_dict
2from myapp.models import User
3
4user = User.objects.get(id=1)
5data = model_to_dict(user)
6print(data)
7# {'id': 1, 'name': 'Alice', 'email': '[email protected]', 'role': 'admin'}

model_to_dict() returns a dictionary of all editable fields. By default, it excludes non-editable fields (like auto_now DateTimeFields) and includes foreign key IDs and many-to-many relations as lists of IDs.

Controlling Which Fields Are Included

python
1from django.forms.models import model_to_dict
2
3user = User.objects.get(id=1)
4
5# Only specific fields
6data = model_to_dict(user, fields=['name', 'email'])
7# {'name': 'Alice', 'email': '[email protected]'}
8
9# Exclude specific fields
10data = model_to_dict(user, exclude=['password', 'last_login'])
11# {'id': 1, 'name': 'Alice', 'email': '[email protected]', 'role': 'admin'}

Using dict

python
1user = User.objects.get(id=1)
2data = user.__dict__
3
4print(data)
5# {'_state': <django.db.models.base.ModelState>,
6#  'id': 1,
7#  'name': 'Alice',
8#  'email': '[email protected]',
9#  'password': 'hashed...',
10#  'created_at': datetime.datetime(2024, 1, 1, ...),
11#  'role_id': 3}
12
13# Clean up internal Django state
14data = {k: v for k, v in user.__dict__.items() if not k.startswith('_')}
15print(data)
16# {'id': 1, 'name': 'Alice', 'email': '[email protected]',
17#  'password': 'hashed...', 'created_at': ..., 'role_id': 3}

__dict__ includes all fields (including non-editable ones and the raw _id suffix for ForeignKey fields) but also includes Django's internal _state object. Filter keys starting with _ to clean it up.

Using values() on QuerySet

python
1# Returns a QuerySet of dictionaries
2users = User.objects.filter(role='admin').values()
3for user_dict in users:
4    print(user_dict)
5# {'id': 1, 'name': 'Alice', 'email': '[email protected]', ...}
6
7# Specific fields
8users = User.objects.filter(role='admin').values('name', 'email')
9# [{'name': 'Alice', 'email': '[email protected]'}, ...]
10
11# Single instance
12data = User.objects.filter(id=1).values().first()

values() works at the database level, generating SQL that returns dictionaries instead of model instances. It is the most efficient method when you do not need the model object itself.

Custom to_dict Method

python
1from django.db import models
2
3class User(models.Model):
4    name = models.CharField(max_length=100)
5    email = models.EmailField()
6    role = models.ForeignKey('Role', on_delete=models.CASCADE)
7    created_at = models.DateTimeField(auto_now_add=True)
8    tags = models.ManyToManyField('Tag')
9
10    def to_dict(self):
11        return {
12            'id': self.id,
13            'name': self.name,
14            'email': self.email,
15            'role': self.role.name,  # Resolved FK, not just ID
16            'created_at': self.created_at.isoformat(),
17            'tags': list(self.tags.values_list('name', flat=True)),
18        }
19
20# Usage
21user = User.objects.select_related('role').prefetch_related('tags').get(id=1)
22print(user.to_dict())
23# {'id': 1, 'name': 'Alice', 'email': '[email protected]',
24#  'role': 'Admin', 'created_at': '2024-01-01T00:00:00', 'tags': ['python', 'django']}

A custom to_dict() method gives full control over field inclusion, FK resolution, and serialization format.

Using Django REST Framework Serializers

python
1from rest_framework import serializers
2
3class UserSerializer(serializers.ModelSerializer):
4    role_name = serializers.CharField(source='role.name', read_only=True)
5
6    class Meta:
7        model = User
8        fields = ['id', 'name', 'email', 'role_name', 'created_at']
9
10# Usage
11user = User.objects.select_related('role').get(id=1)
12data = UserSerializer(user).data
13print(data)
14# {'id': 1, 'name': 'Alice', 'email': '[email protected]',
15#  'role_name': 'Admin', 'created_at': '2024-01-01T00:00:00Z'}

DRF serializers handle nested objects, validation, and JSON-compatible output. Use them for API responses.

Comparison Table

 
MethodNon-editableFK as IDFK resolvedM2MJSON-safe
model_to_dict()NoYesNoYesNo
dictYesYes (_id)NoNoNo
values()YesYes (_id)NoNoPartial
Custom to_dict()You chooseYou chooseYesYesYes
DRF SerializerYou chooseYou chooseYesYesYes

Common Pitfalls

  • model_to_dict excludes non-editable fields: Fields with editable=False (like auto_now and auto_now_add) are excluded by model_to_dict(). Use __dict__ or a custom method to include them.
  • dict includes _state: The _state attribute is Django internal metadata. Always filter it out: {k: v for k, v in obj.__dict__.items() if not k.startswith('_')}.
  • ForeignKey fields use _id suffix in dict: user.__dict__ shows role_id (the raw integer), not role (the related object). Use model_to_dict() or access user.role directly for the related object.
  • Many-to-many fields missing: __dict__ and values() do not include M2M relationships. Use model_to_dict() (returns IDs) or a custom method (returns resolved values).
  • Non-serializable types in output: DateTime objects, Decimal, and UUID are not JSON-serializable. Use DjangoJSONEncoder or convert them manually (.isoformat(), str()) before json.dumps().

Summary

  • model_to_dict(instance) is the quickest built-in but excludes non-editable fields
  • instance.__dict__ includes all fields but requires filtering _state and returns raw FK IDs
  • QuerySet.values() is most efficient for bulk conversion directly from the database
  • Custom to_dict() methods give full control over FK resolution, M2M, and serialization
  • DRF serializers are the standard choice for API responses with nested relationships

Course illustration
Course illustration

All Rights Reserved.