Django Converting Queryset with Foreign Keys to JSON - django

I'm trying to pass JSON objects to my template. I have some complicated relationships that I can't modify. Based on OS
posts I've tried different things. The ones that I think brought me close are:
I created a .values() queryset that looks like this
def my_queryset():
results=TodaysResults.objects.values('id','foreignModel1__name',
'foreignModel1__ForeignModel2__title')
print (myquery_set)
my_queryset gives me all the values I need. So I tried to convert it using the methods below.
1)
def make_querydict():
results=TodaysResults.objects.values('id','foreignModel1__name',
'foreignModel1__ForeignModel2__title')
json_results=json.dumps(list(results,cls=DjangoJSONEncoder))
print (json_results)
I get the following error:
"TypeError: list() takes no keyword arguments"
I tried this as well:
def serialize():
fields = ['id','foreignModel1__name','foreignModel1__ForeignModel2__title']
qs = TodaysResults.objects.all()
json_data = serializers.serialize('json',qs,fields=fields)
print(json_data)
But when I print the json_data in only shows id and not the foreign values.
Based on a few answers like this(from 2012) that were along the same lines And I tried:
def for_JSON_response():
response=JsonResponse(dict(results_info=list(TodaysResultslts.objects.values('id','foreignModel1__name',
'foreignModel1__ForeignModel2__title')
print(response)
I don't get any errors but it does not print anything.So, I'm assuming nothing happened.
Based on this I tried:
def my_queryset():
results=TodaysResults.objects.values('id','foreignModel1__name',
'foreignModel1__ForeignModel2__title')
print (JsonResponse(results, safe=False))
I get:
TypeError: Object of type QuerySet is not JSON serializable
And I tried:
def my_queryset():
results=TodaysResults.objects.values('id','foreignModel1__name',
'foreignModel1__ForeignModel2__title')
results_json = serializers.serialize('json',results)
And I got:
AttributeError: 'dict' object has no attribute '_meta'
I've been looking around a lot and some of the responses look outdated. My attempts above are the ones that I believe came the closest to converting the
valuesqueryset to json or getting the values I need into JSON. Is there a way of making a chained query like the one I have in my_queryset and convert it to JSON?
Models.Py
Simplified for this example
class TodaysResults(models.Model):
place = models.CharField(max_length=255)
ForeignModel1 = models.ForeignKey(ForeignModel,related_name='m1')
class ForeignModel(models.Model):
name = Models.CharField(max_length=255)
ForeignModel2 = models.ManyToManyField(M2M, related_name='m2')
class M2M(models.Model):
title = Models.CharField(max_length=255)

Here we have three model TodaysResults, ForeignModel and MModel and I am guessing MModel is a manyTomany relationship with ForeignModel. I am proposing two possible way how we can get all information from TodaysResults serializer.
Possible Solution One
from rest_framework import serializers
class MModelSerializer(serializers.ModelSerializer):
class Meta:
model = MModel
fields = ('title',)
class ForeignModelSerializer(serializers.ModelSerializer):
foreignModel2 = MModelSerializer(many=True) # as it is many to many field
class Meta:
model = ForeignModel
fields = ('name', 'foreignModel2',)
class TodaysResultsSerializer(serializers.ModelSerializer):
foreignModel1 = ForeignModelSerializer()
class Meta:
model = TodaysResults
fields = ('place', 'foreignModel1')
Now pass your TodaysResults queryset TodaysResultsSerializer and from serializer.data you will get your serialized data.
Possible Solution Two
Even we can do this with one serialize as there is not all fields is required.
class TodaysResultsSerializer(serializers.ModelSerializer):
class Meta:
model = TodaysResults
fields = ('place', 'foreignModel1__name', 'foreignModel1__foreignModel2__title')
Though i am not fully sure but this should also work.

Related

Django Rest Framework – Custom Hyperlink field in serializer

How can I add a custom hyperlink field in a serializer? I would like to have a hyperlink field in my serializer that has query params in it. Since there is no way to pass query params from HyperlinkedRelatedField or HyperlinkedIdentityField as far as I know, I've tried using a SerializerMethodField. However, this only serializes to a string, and is not a clickable URL when I visit the API through my browser. My code looks something like this:
class MySerializer(serializers.HyperlinkedModelSerializer):
custom_field = serializers.SerializerMethodField()
class Meta:
model = MyModel
fields = ('url', 'custom_field')
def get_custom_field(self, obj):
result = '{}?{}'.format(
reverse('my-view'),
urllib.urlencode({'param': 'foo'})
)
return result
Also, I am having trouble understanding the difference between a HyperlinkedRelatedField and a HyperlinkedIdentityField, so a brief explanation would be appreciated.
This should do the trick:
from rest_framework.reverse import reverse
class MySerializer(serializers.HyperlinkedModelSerializer):
custom_field = serializers.SerializerMethodField()
class Meta:
model = MyModel
fields = ('url', 'custom_field')
def get_custom_field(self, obj):
result = '{}?{}'.format(
reverse('my-view', args=[obj.id], request=self.context['request']),
'param=foo'
)
return result
The reverse function in rest_framework takes a view name (whatever view you'd like to link to), either an args list (the object id, in this case) or kwargs, and a request object (which can be accessed inside the serializer at self.context['request']). It can additionally take a format parameter and any extra parameters (as a dictionary) that you want to pass to it.
The reverse function then builds a nice, fully-formed URL for you. You can add query params to it by simply adding as many ?{}&{}&{} to your result variable and then filling in the series of query params beneath the 'param=foo' inside your format function with whatever other params you want.
The HyperlinkedIdentityField is used on the object itself that is being serialized. So a HyperlinkedIdentifyField is being used in place of your primary key field on MyModel because you are using a HyperlinkedModelSerializer which creates a HyperlinkedIdentityField for the pk of the object itself being serialized.
The HyperlinkedRelatedField is used to define hyperlinked relationships to RELATED objects. So if there were a MySecondModel with a foreign key relationship to MyModel and you wanted to have a hyperlink on your MyModel serializer to all the related MySecondModel objects you would use a HyperlinkedRelatedField like so (remember to add the new field to your fields attribute in Meta):
class MySerializer(serializers.HyperlinkedModelSerializer):
custom_field = serializers.SerializerMethodField()
mysecondmodels = serializers.HyperlinkedRelatedField(
many=True
read_only=True,
view_name='mysecondmodel-detail'
)
class Meta:
model = MyModel
fields = ('url', 'custom_field', 'mysecondmodels')
def get_custom_field(self, obj):
result = '{}?{}'.format(
reverse('my-view', args=[obj.id], request=self.context['request']),
'param=foo'
)
return result
If it were a OneToOneField rather than ForeignKey field on MySecondModel then you would set many=False.
Hope this helps!

Django custom serializer how to input ID/PK

I am trying to make custom serializer work. Can't use model serializer since I will be saving data into multiple models. Here is my code:
class RushSerializer(serializers.Serializer):
keyword = serializers.IntegerField(read_only=True)
url = serializers.URLField()
def save(self):
keyword = self.validated_data['keyword']
url = self.validated_data['url']
Url1.objects.create(keyword=keyword, url=url)
And I input data like this:
nov = {'url': 'https://domain.com/'}
nov['keyword'] = id
serializer = RushSerializer(data=nov)
It returns me this error:
KeyError at /admin/keywords/kw/add/
'keyword'
Here is the model itself:
class Url1(models.Model):
keyword = models.ForeignKey(KW)
url = models.URLField()
What is wrong with my code? I tried pretty much anything from here but can't seem to make it work.
EDIT: To clarify, it works if I do it with model serializer and this:
class Meta:
model = Url1
fields = ('keyword', 'url')
So problem isn't in input 100% but in serializer itself.
If I do keyword without read_only then I get this error:
ValueError at /admin/keywords/kw/add/ Cannot assign "104":
"Url1.keyword" must be a "KW" instance.

Django Rest Framework Many=False producing errors

I am stuck on trying to solve an issue with the Serializers and related fields using the django-rest-framework. Currently I have a model that looks like this:
class DataSetModel(models.Model):
title = models.CharField(max_length=200)
description = models.TextField()
class DataPointModel(models.Model):
dataSet = models.ForeignKey(DataSetModel, related_name='dataPoints')
label = models.CharField(max_length=200)
My serializers look like this:
class DataPointSerializer(serializers.ModelSerializer):
class Meta:
model = DataPointModel
fields = ('pk','label')
class DataSetSerializer(serializers.ModelSerializer):
dataPoints = DataPointSerializer(many=True, read_only=True)
class Meta:
model = DataSetModel
fields = ('pk','title')
The problem I am having is when I try to change the "many=False" in the serializer produces this error:
Got AttributeError when attempting to get a value for field label on
serializer DataPointSerializer. The serializer field might be named
incorrectly and not match any attribute or key on the RelatedManager
instance. Original exception text was: 'RelatedManager' object has no
attribute 'label'.
Since this is only ever one model object (one-to-many relationship), I want to get the result as a single object vs a list of one object.
Am I doing this the right way? I thought that turning the "many=False" it would fetch the first record in an nested query.
Any insight would be greatly appreciated.
You can't set many=False, because dataPoints is a related field that returns a queryset containing a list of instances, not just an instance.
When you do DataPointModel.dataPoints that returns a queryset, it can't returns just an instance. So setting many=False, it wouldn't get the first element of the list.
So the solve I came to was refactoring my models with where the Foreign Keys are attached.
Here is the fix I implemented:
class DataSetModel(models.Model):
title = models.CharField(max_length=200)
description = models.TextField()
dataPoint = models.ForeignKey(DataPointModel)
class DataPointModel(models.Model):
label = models.CharField(max_length=200)
Moving it to the DataSetModel automatically joins the data model if you set 'many=False' in the Serializer object.
Write your DataPointModel as below. It should work then
class DataPointModel(models.Model):
dataSet = models.OneToOneField(DataSetModel, related_name='dataPoints')
label = models.CharField(max_length=200)

Django get all, with related models

Problem:
I'm using Django Rest Framework and i want to fetch all models with the relationships included, like this:
TestModel.objects.all()
My model looks like this:
class TestModel(models.Model):
name = models.CharField(max_length=32)
related_model = models.ForeignKey(TestRelation)
Problem is, i only get the Primary Keys for related_model but i need the whole related_model!
I'm using the ListCreateAPIView, with the above queryset (TestModel.objects.all()) and the most basic form of the ModelSerializer.
I tried the PrimaryKeyRelatedField but i get the same result..
Thanks!
Just create serializer for your related model:
class TestRelationSerializer(serializers.ModelSerializer):
class Meta:
meta = TestRelation
and use is as field in TestModelSerializer:
class TestModelSerializer(serializers.ModelSerializer):
related_model = TestRelationSerializer()
You can also do it other way around, by using TestModelSerializer as field in TestRelationSerializer with many set to true:
class TestRelationSerializer(serializers.ModelSerializer):
testmodel_set = TestModelSerializer(many=True)
just remember, you can't do both at once due to infinite recursion it makes.

Django ORM access User table through multiple models

views.py
I'm creating a queryset that I want to serialize and return as JSON. The queryset looks like this:
all_objects = Program.objects.all()
test_data = serializers.serialize("json", all_objects, use_natural_keys=True)
This pulls back everything except for the 'User' model (which is linked across two models).
models.py
from django.db import models
from django.contrib.auth.models import User
class Time(models.Model):
user = models.ForeignKey(User)
...
class CostCode(models.Model):
program_name = models.TextField()
...
class Program(models.Model):
time = models.ForeignKey(Time)
program_select = models.ForeignKey(CostCode)
...
Question
My returned data has Time, Program, and CostCode information, but I'm unable to query back the 'User' table. How can I get back say the 'username' (from User Table) in the same queryset?
Note: I've changed my queryset to all_objects = Time.objects.all() and this gets User info, but then it doesn't pull in 'CostCode'. My models also have ModelManagers that return the get_by_natural_key so the relevant fields appear in my JSON.
Ultimately, I want data from all four models to appear in my serialized JSON fields, I'm just missing 'username'.
Here's a picture of how the JSON object currently appears in Firebug:
Thanks for any help!
It seems a bit heavyweight at first glance but you could look at using Django REST Framework:
http://www.django-rest-framework.org/api-guide/serializers#modelserializer
You can define and use the serializer classes without having to do anything else with the framework. The serializer returns a python dict which can then be easily dumped to JSON.
To get all fields from each related model as nested dicts you could do:
class ProgramSerializer(serializers.ModelSerializer):
class Meta:
model = Program
depth = 2
all_objects = Program.objects.all()
serializer = ProgramSerializer(all_objects, many=True)
json_str = json.dumps(serializer.data)
To customise which fields are included for each model you will need to define a ModelSerializer class for each of your models, for example to output only the username for the time.user:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', )
class TimeSerializer(serializers.ModelSerializer):
"""
specifying the field here rather than relying on `depth` to automatically
render nested relations allows us to specify a custom serializer class
"""
user = UserSerializer()
class Meta:
model = Time
class ProgramSerializer(serializers.ModelSerializer):
time = TimeSerializer()
class Meta:
model = Program
depth = 1 # render nested CostCode with default output
all_objects = Program.objects.all()
serializer = ProgramSerializer(all_objects, many=True)
json_str = json.dumps(serializer.data)
What you really want is a "deep" serialization of objects which Django does not natively support. This is a common problem, and it is discussed in detail here: Serializing Foreign Key objects in Django. See that question for some alternatives.
Normally Django expects you to serialize the Time, CostCode, Program, and User objects separately (i.e. a separate JSON array for each) and to refer to them by IDs. The IDs can either be the numeric primary keys (PKs) or a "natural" key defined with natural_key.
You could use natural_key to return any fields you want, including user.username. Alternatively, you could define a custom serializer output whatever you want there. Either of these approaches will probably make it impossible to load the data back into a Django database, which may not be a problem for you.