I'm creating an API and I need to return data in a dictionnary format sothat it can be serialized (by the API mechanism).
The code that currently works is something as simple as:
def mymethod(self):
queryset1 = MyClass.objects.get(...) # Ccontains 1 object, easy to deal with
queryset2 = OtherClass.objects.filter(...) # Contains N objects, hard to deal with !
return {
'qs1_result': queryset1.some_attribute # This works well
}
Returning data from queryset1 is easy because there is 1 object. I just pick the attribute I need and it works. Now let's say that in addition, I want to return data from queryset2, where there are many objects, and that I don't need every attributes of the object.
How would you do that?
I repeat that I do NOT need to make the serialization myself. I just need to return structured data sothat the serialization can be made.
Thanks a lot.
From the Django docs: https://docs.djangoproject.com/en/dev/topics/serialization/#subset-of-fields
Subset of fields
If you only want a subset of fields to be serialized, you can specify a fields argument to the serializer:
from django.core import serializers
data = serializers.serialize('json', SomeModel.objects.all(), fields=('name','size'))
In this example, only the name and size attributes of each model will be serialized.
Related
I want to serialize a QuerySet into a JSON object instead of JSON array.
For model Day, the serialized QuerySet should be an object with Day.date keys and serialized Days as values.
class DaySerializer(serializers.ModelSerializer):
class Meta:
model = Day
exclude = []
This returns an array of serialized objects:
DaySerializer(Day.objects.all(),many=True).data
{'15.02.2005':{...},
'16.02.2005':{...},
...
}
I'm curious if there is some DRF way to do that.
AFAIK there is not an out-of-the-box way of doing that, but you can override .to_representation() and .to_internal_value() methods of the serializer to achieve that.
These methods enable you to alter how both serialization and de-serialization is done.
See here for details.
I'm trying to create a couple django models with a one to one relation. However I'm trying to get it so the related one to one model is automatically created. If I have something simple like this:
class MyObject(models.Model):
data = models.OneToOneField('MyData', related_name='my_object')
class MyData(models.Model):
info = models.TextField(null=True)
If I create a MyObject and access MyObject.data it will return None. I was hoping there was a way I can have it return a MyData object (just default reference).
I'd like MyObject to automatically have a related MyData object. Is there a way for me to do this or do I need to check every time to see if there's a related MyData object?
Have you seen the official doc?
d = MyData(info='whatever')
o = MyObject(data=d)
How can it be automatic if info text field has to be filled in?
after seeing your edit:
you can probably set my data to be null
o = MyObject(data=Mydata(info=None))
of course, your Mydata should now be able to accept None as their type.
This question already has answers here:
Convert Django Model object to dict with all of the fields intact
(17 answers)
Closed 6 years ago.
In my Django app, how can I turn objects from Models into a dictionary that includes the foreign-key references of the Model object?
When I try this:
from django.forms.models import model_to_dict
model_to_dict(instance, fields=[], exclude=[])
The resulting dictionary only has the direct fields. I would like to also get the foreign keys related to the Model object. How can I do this?
obj = get_object_or_404(CustomModel,id=some_id)
my_dict = obj.__dict__
How about:
model_to_dict(instance, fields=[field.name for field in instance._meta.fields])
By explicitly naming all of the fields, it should give you the foreign key ids at least (although I haven't verified).
Note that this function does not return fields with editable=False (since it is intended for forms).
I think I had the same need as you -- I wanted a plain-and-simple dict representation of an object. the other answers posted (at the time I write) wouldn't give me that, and the serializers, while useful for their purpose, produce output with extra info I don't want, and an inconvenient structure.
Though admittedly a hack, this is giving me good mileage:
from django.core import serializers
def obj_to_dict(model_instance):
serial_obj = serializers.serialize('json', [model_instance])
obj_as_dict = json.loads(serial_obj)[0]['fields']
obj_as_dict['pk'] = model_instance.pk
return obj_as_dict
wrapping the django_model_object in a list, then accessing item 0 after parsing the json is needed because, for some reason, serializers can only serialize iterables of model objects (weird).
You'd need some extra gears to handle any kind of foreign key fields, I may post back if I end up needing to write that (unless someone else edits it in first!).
HTH for now.
You may want to have look at Django serialization if you want to work with related models.
None of the other answers quite worked for me. This, however, seemed to do the trick.
def model_to_dict(instance, include=None, exclude=None):
fields = instance._meta.concrete_fields
if include is not None:
return {f.attname: getattr(instance, f.attname) for f in fields if f.name in include}
if exclude is not None:
return {f.attname: getattr(instance, f.attname) for f in fields if f.name not in exclude}
return {f.attname: getattr(instance, f.attname) for f in fields}
I imagine that you've already solved this problem for your case, but I found some info as I was searching for similar solutions. I wanted to be able to access objects and their fields/attributes in a template. I am just learning django.
What I finally read-up on was the https://docs.djangoproject.com/en/3.0/ref/class-based-views/generic-display/, which seem to be able to send an object instance or a queryset of objects along to a template. Then you can access the objects like the model.field, or even if it's a foreignkey field, model.foreignmodel.foreignmodelfield.
So it's been pretty useful for me. This was after I built a bunch of custom 'to-dict' methods in different class managers. This helped me a lot. But you'll have to read up on the Generic Views and how to customize them to really get the details you want.
-Matt
I have a very complicated form and I choose to not use ModelForm since I needed flexibility and control over the fields. Since I am not using ModelForm, I can't simply do something like instance=order, where order = Order.objects.get(pk=1).
Currently I am pre-populating every field with initial in the forms.py as oppose to the views.py like this
self.fields['work_type'] = forms.ChoiceField(choices=Order.WORK_TYPE_CHOICES, initial=order.work_type)
But I was wondering if I could pass the entire order object to a form or do I have to declare initial to every field?
Is there a way to do something like
order_form = OrderEditForm(data=request.POST, initial=order)
in views.py?
I have a very complicated form and I choose to not use ModelForm since
I needed flexibility and control over the fields
Everything that you can do using a Form, you can do in a ModelForm such as adding new fields or over-riding attributes on the fields etc.
But I was wondering if I could pass the entire order object to a form
or do I have to declare initial to every field?
You can pass the order object into the form but you will still have to populate each field individually either in the forms or in the view function.
So in your view you would do something like this:
intial = {'order_number': order.number, 'order_id': order.id}
form = OrderForm(initial=initial)
The easiest way to prepopulate data to a form is passing a dictionary as first argument to de form constructor.
order_form = OrderEditForm(order.__dict__())
where __dict__() is a method that passes "order" object attributes to a dictionary with each attribute's name as a key and their content as value.
An example of how to "invent" such a method could be something like:
order_initial = Order.objects.filter(pk=order.pk).values()[0]
and then construct the form with:
order_form = OrderEditForm(order_initial)
Look at this example (how they populate values at "post" time):
For future reference to other people:
I have since found out after reading SO's comments and answers that it's better to use ModelForm even if you end up explicitly defining every field manually (using something like self.fields['foo'] = forms.CharField()).
In any case, if you are trying to pass a dictionary of current values in a form then the best (built-in) way to convert a model to a dictionary is actually using model_to_dict:
from django.forms.models import model_to_dict
order = Order.objects.get(pk=1)
dictionary = model_to_dict(order)
form = OrderEditForm(dictionary)
I got the solution from this blog. I hope this will be helpful for someone.
I have this manytomany field in my model that I have overridden with a CharField that receives a csv list of the second models name attribute.
class PostForm(ModelForm):
tests = CharField(label="tests")
class Meta:
model = Post
fields = ('title','body')
def clean_tests(self):
# Here I clean, create or retrieve and return a list of Test objects.
Now, saving and validating is alright with this code, everything works, my problem comes when I create the PostForm with an existing instance, like PostForm(instance=current_post).
The CharField should contain a csv list but it contains nothing, obviously this happens because there is no conversion happening from Test object list to test name list, the problem is I do not know where to put that code, I see no method I could override to get this done, i've looked into initial data and default properties of fields.
I'm not sure if there's a method you could override to do this -- from a look at the BaseModelForm constructor though, it looks perfectly okay to specify both the instance and initial keyword arguments together -- the instance is converted into a dict (subject to the fields and exclude options in Meta), and that dict's update method is called with initial. Something like this should work:
# build your csv list somehow (just speculation here)
tests = ','.join(test.name for test in current_post.tests.all())
form = PostForm(instance=current_post, initial={'tests': tests})