Convert Django Model object to dict with all of the fields intact
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 and getting the ids of foreign keys for many to many fields results in the following code which behaves as desired:
from itertools import chain
def to_dict(instance):
opts = instance._meta
data = {}
for f in chain(opts.concrete_fields, opts.private_fields):
data[f.name] = f.value_from_object(instance)
for f in opts.many_to_many:
data[f.name] = [i.id for i in 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's ModelSerializer 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 itertools import chain
class PrintableModel(models.Model):
def __repr__(self):
return str(self.to_dict())
def to_dict(instance):
opts = instance._meta
data = {}
for f in chain(opts.concrete_fields, opts.private_fields):
data[f.name] = f.value_from_object(instance)
for f in opts.many_to_many:
data[f.name] = [i.id for i in f.value_from_object(instance)]
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}
Django: Converting an entire set of a Model's objects into a single dictionary
Does this need to create an actual dict? could you get by with only something that looked like a dict?
class DictModelAdaptor():
def __init__(self, model):
self.model = model
def __getitem__(self, key):
return self.model.objects.get(key=key)
def __setitem__(self, key, item):
pair = self.model()
pair.key = key
pair.value = item
pair.save()
def __contains__(self, key):
...
You could then wrap a model in this way:
modelDict = DictModelAdaptor(DictModel)
modelDict["name"] = "Bob Jones"
etc...
django convert model instance to dict
This actually already exists in Django, but its not widely documented.
from django.forms import model_to_dict
my_obj = User.objects.first()
model_to_dict(my_obj,
fields = [...], # fields to include
exclude = [...], # fields to exclude
)
Django convert model objects to dictionary in large volumes results in server timeout
At first glance, your main problem is most likely you're hitting the database twice for each EntryState instance.
convertToDict
method makes use of FK entry
and for each entry, you also fetch the M2M tags
. Solution is to optimize the query.
First, let's identify the problem.
When you put this code before the end of the view, you'll see how many times you're hitting the database in the console.
def myView(request):
....
from django.db import connection; qq=connection.queries; print(f'QUERY: {len(qq)}')
return render(request, 'my_template.html', context)
Now, try to improve the query in the view.
query = EntryState.objects.filter(field1 < 10) # Assuming this is a valid query that uses something like `field1__lt=10`
You can use select_related
for the Entry FK so that it'll be retrieved within that single query (Saves N hits to the DB)
query = EntryState.objects.filter(field1__lt=10).select_related('entry')
Also you can add prefetch related to retrieve the tags
for ALL entries in one hit.
query = EntryState.objects.filter(field1__lt=10).select_related('entry').prefetch_related('entry__tags_set')
After each change, you can see how many times you hit to the database so that you can see the problem getting fixed. You can check the number of DB hits again to ensure it's optimized.
For more info, please read about query optimization, select_related
and prefetch_related
.
You can also use some apps to monitor the performance of your views: django-silk, django-debug-toolbar
Source: https://docs.djangoproject.com/en/4.0/ref/models/querysets/#select-related
expanding dict model field in django
Option 1, solution with JSONField:
class AJsonModel(models.Model):
name = models.CharField(null=True, max_length=255)
data = models.JSONField(null=True)
def __str__(self):
return f"A list of {[n for n in self.data]}"
data = [10, 20, 30, 40, 50]
dict_data = {k:v for (k, v) in enumerate(data, 1)}
m = AJsonModel.objects.create(name="One", data=dict_data)
m.save()
Unfortunately for this method, you will have to do some work everytime you want to add another value to the list:
c = AJsonModel.objects.get(id=1)
values = list(c.data.values())
values.append(60)
c.data = {k:v for (k, v) in enumerate(values, 1)}
c.save()
Option 2, use related table:
class AJsonModelWithChild(models.Model):
name = models.CharField(null=True, max_length=255)
def __str__(self):
return f"{self.name}"
class GrowingChild(models.Model):
value = models.IntegerField(default=0)
parent = models.ForeignKey(AJsonModelWithChild, on_delete=models.CASCADE)
def __str__(self):
return f"{self.id}: {self.value}"
The good thing for this method is that you do not have to think about the other, previous values stored before - just add a new child for this parent:
parent = AJsonModelWithChild.objects.create(name="Two")
parent.save()
child = GrowingChild.objects.create(value=100, parent=parent)
child.save()
How can I convert django model object and related object in to list of dictionary?
q_customer = Customer.objects.all()
L = []
for customer in q_custmer:
Dcustomer = {'name':customer.name,
'Payment':[{'amount',x.amount} for x in customer.payment_set]
L.append[D]
# L now holds the list
How do you add one object to a Django Model that has a ManyToManyField?
The *
in the arguments converts a list to individual args. For example-
lst = [1,2,3,4,5]
function(*lst)
can be just read as
function(1,2,3,4,5)
You have used follower.followers.add(*user)
. Hence, user
must be an iterable to be unpacked and passed as a list or arguments. But user
is a single User
object.
You should just use follower.followers.add(user)
in this case.
Django Project : 'dict' object has no attribute 'code'
Classic issue I'm also having when switching over eg from Javascript :-)
There is no dot notation in python to access dicts. There are basically 2 way to access a dict in python:
product["code"]
or
product.get("code")
The latter has the advantage that you can pass in a default value in case the key "code" is not available:
product.get("code", "123421")
Related Topics
Pylint "Unresolved Import" Error in Visual Studio Code
Check If Something Is (Not) in a List in Python
Attributeerror: 'Module' Object Has No Attribute
Convert Numpy Array to Python List
Getting Syntaxerror for Print with Keyword Argument End=' '
Pandas Long to Wide Reshape, by Two Variables
How to Find Median and Quantiles Using Spark
Python Nameerror: Name Is Not Defined
Check for Presence of a Sliced List in Python
Pandas Dataframe Line Plot Display Date on Xaxis
Differences Between Distribute, Distutils, Setuptools and Distutils2
Convert Django Model Object to Dict with All of the Fields Intact
Error After Upgrading Pip: Cannot Import Name 'Main'
How to Test That a Python Function Throws an Exception
What Are the 'Levels', 'Keys', and Names Arguments for in Pandas' Concat Function
How to Detach Matplotlib Plots So That the Computation Can Continue