Django Rest Framework Many=False producing errors - django

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)

Related

How to define mongodb ArrayField in Djongo's model

class question(models.Model):
question_id = models.IntegerField()
choice = models.CharField(max_length=100)
tags = models.ArrayField(models.CharField(max_length=100))
ERROR:
packages\djongo\models\fields.py", line 116, in _validate_container
for field in self.model_container._meta._get_fields(reverse=False):
AttributeError: 'CharField' object has no attribute '_meta'
I want to add a ArrayField in my models using djongo models. i hope 'tags' looks like ['tag1', 'tag2', 'tag3' ...]. But djongo requeirs model_container, but i only want an array contains strings, not a model ,please help~~
I was experiencing a similar situation: wanted to create an IntegerField array. I solved it with this workaround:
Create an intermediate model which stores your field.
For example, for my use case (with integers that represent quantity by each second) I created a model called Second which holds an attribute called data of type models.IntegerField():
class Second(models.Model):
data = models.IntegerField(null=False)
class Meta:
abstract = True
class ParentModel(models.Model):
_id = models.ObjectIdField()
by_second = models.ArrayField(
model_container=Second
)
objects = models.DjongoManager()
So I guess in your case you could use the same but changing models.IntegerField()by models.CharField(max_length=100)
I know it has been a long time since the question was asked, but I hope this helps someone having this issue :)

Django Converting Queryset with Foreign Keys to JSON

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.

Django Rest Framework: Just get certain values of a ManyToMany relationship

I'm using Django Rest Framework to write my API. I want to write different values than the the id (specifically the uuid) into my serializer.
Let me give you the basic setup first. I have a model called House which has amongst other values a pk and a uuid. And I have a second model called Citizen which also has a pk and a uuid. House and Citizen have a ManyToMany relationship with each other.
I would like to have a serializer that just gives back an array of it's citizen.
Here is the not working pseudo code that I tried (and failed):
class HouseSerializer(serializers.ModelSerializer):
address = AddressSerializer()
citizen = serializers.UUIDField(source="citizen.uuid")
class Meta:
model = Table
fields = [
"uuid",
"address",
"citizen",
...
]
This serializer throws the error:
AttributeError: Got AttributeError when attempting to get a value for field `citizen` on serializer `HouseListSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `House` instance.
Original exception text was: 'ManyRelatedManager' object has no attribute 'uuid'.
But on my model for the House I have explicitly citizen = models.ManyToManyField(Citizen).
If I just don't specify any serializer and just leave citizen in the fields array, I just get an array of the PKs which I can't use.
How can I get an array of the UUIDs here?
First you'll need a serializer class for your Citizen model.
class CitizenSerializer(serializers.ModelSerializer):
uuid = serializers.UUIDField(read_only=True)
class Meta:
model = Citizen
fields = ('uuid', )
We will then add the CitizenSerializer to your HouseSerializer. Note that we need the many=True argument for ManyToManyField relation.
class HouseSerializer(serializers.ModelSerializer):
address = AddressSerializer()
citizen = CitizenSerializer(read_only=True, many=True)
class Meta:
...
You can read more about this here
You can use SlugRelatedField like this:
class HouseSerializer(serializers.ModelSerializer):
address = AddressSerializer()
citizen = serializers.SlugRelatedField( many=True,
read_only=True,
slug_field='uuid')
read the drf doc for more information.

N duplicated queries nested model

I've got an Area model allowing sub areas (you might think of it as categories with subcategories). I reached this by nesting one field to self as foreign key.
class Area(models.Model):
area = models.CharField(max_length=120)
parent = models.ForeignKey('self', models.CASCADE, blank=True, null=True, related_name='subarea')
def __str__(self):
return self.area
With the django rest framwork I've manages to get the correct output. The problem is that when I analyze the request with django-toolbar multiple duplicated requests are made (N*Area(parent=None)). I've solved similar issues by using prefetch_related or select_related. But never done it with a nested model. Is there any way to solve this? Or is this design of the model bad?
I manage to serialize the correct output with the following view and
class ListArea(generics.ListCreateAPIView):
serializer_class = AreaSerializer
queryset = Area.objects.prefetch_related('parent').filter(parent=None)
and serializers
class SubAreaSerializer(serializers.ModelSerializer):
class Meta:
model = Area
fields = ('area','id')
class AreaSerializer(serializers.ModelSerializer):
subarea=SubAreaSerializer(many=True)
class Meta:
model = Area
fields = ('area','id','subarea')
Or might those extra calls be due to the browsable API?
Solution
I solved this with help of the following thread Django: Does prefetch_related() follow reverse relationship lookup?
Instead of
queryset = Area.objects.prefetch_related('parent').filter(parent=None)
I should use
queryset = Area.objects.prefetch_related('parent').prefetch_related('subarea')

django: how do I query based on GenericForeignKey's fields?

I'm new in using GenericForeignKey, and I couldn't make it to work in a query statement. The tables are roughly like the following:
class Ticket(models.Model):
issue_ct = models.ForeignKey(ContentType, related_name='issue_content_type')
issue_id = models.PositiveIntegerField(null=True, blank=True)
issue = generic.GenericForeignKey('issue_ct', 'issue_id')
class Issue(models.Model):
scan = models.ForeignKey(Scan)
A scan creates one issue, an issue generates some tickets, and I made Issue as a foreign key to Ticket table. Now I have a Scan object, and I want to query for all the tickets that related to this scan. I tried this first:
tickets = Tickets.objects.filter(issue__scan=scan_obj)
which doesn't work. Then I tried this:
issue = Issue.objects.get(scan=scan_obj)
content_type = ContentType.objects.get_for_model(Issue)
tickets = Tickets.objects.filter(content_type=content_type, issue=issue)
Still doesn't work. I need to know how to do these kind of queries in django? Thanks.
The Ticket.issue field you've defined will help you go from a Ticket instance to the Issue it's attached to, but it won't let you go backwards. You're close with your second example, but you need to use the issue_id field - you can't query on the GenericForeignKey (it just helps you retrieve the object when you have a Ticket instance). Try this:
from django.contrib.contenttypes.models import ContentType
issue = Issue.objects.get(scan=scan_obj)
tickets = Ticket.objects.filter(
issue_id=issue.id,
issue_ct=ContentType.objects.get_for_model(issue).id
)
Filtering across a GenericForeignKey can by creating a second model that shares the db_table with Ticket. First split up Ticket into an abstract model and concrete model.
class TicketBase(models.Model):
issue_ct = models.ForeignKey(ContentType, related_name='issue_content_type')
issue_id = models.PositiveIntegerField(null=True, blank=True)
class Meta:
abstract = True
class Ticket(TicketBase):
issue = generic.GenericForeignKey('issue_ct', 'issue_id')
Then create a model that also subclasses TicketBase. This subclass will have all the same fields except issue which is instead defined as a ForeignKey. Adding a custom Manager allows it to be filtered to just a single ContentType.
Since this subclass does not need to be synced or migrated it can be created dynamically using type().
def subclass_for_content_type(content_type):
class Meta:
db_table = Ticket._meta.db_table
class Manager(models.Manager):
""" constrain queries to a single content type """
def get_query_set(self):
return super(Manager, self).get_query_set().filter(issue_ct=content_type)
attrs = {
'related_to': models.ForeignKey(content_type.model_class()),
'__module__': 'myapp.models',
'Meta': Meta,
'objects': Manager()
}
return type("Ticket_%s" % content_type.name, (TicketBase,), attrs)