How to post to a model which contains a GenericForeignKey Content Type field?

1,760 views
Skip to first unread message

kharis

unread,
Jul 3, 2018, 10:28:08 PM7/3/18
to Django REST framework
Hi, I have a model in my application that has a Generic Relation, it uses django content type framework and model view set
the official documentation I didn't understand how to do it after reading http://www.django-rest-framework.org/api-guide/relations/#generic-relationships , I find no section explaining how to POST the data. 

class Bar1(models.Model):
    id
= models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    data
= models.CharField(max_length=20)

class Bar2(models.Model):
    id
= models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    data
= models.IntegerField()

class Foo(models.Model):
    id
= models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    content_type
= models.ForeignKey(ContentType, on_delete=models.PROTECT)
    object_id
= models.UUIDField()
    contentObject
= GenericForeignKey()

class FooViewSet(viewsets.ModelViewSet):
    serializer_class
= FooSerializer
    queryset
= Foo.objects.all()

class FooSerializer(serializers.ModelSerializer):
   
class Meta:
        model
= Foo
        fields
= ('__all__')

How can I post data to it? 
I tried to post another object uuid (Bar1/Bar2) to it,

{
 
 "content_type": "<<uuid>>",
   
"object_id" : "<<uuid>>
}

it doesn't work. it returns "Incorrect type. Expected pk value, received str."

then I tried to get the content type object in serializer

def create(self, validated_data):
        content_type =  validated_data.get("object_id")
        taggedObject = Bar1.objects.get(id=content_type) if Bar1.objects.filter(id=content_type).count() > 0 else None
        taggedObject = Bar2.objects.get(id=content_type) if Bar2.objects.filter(id=content_type).count() > 0 and taggedObject == None else None

        if taggedObject is not None:
            contentType = ContentType.objects.get_for_model(taggedObject)
            if contentType is not None:
                validated_data["content_type"] = contentType
                validated_data["object_id"] = taggedObject
        return super(FooSerializer, self).create(validated_data)

class Meta:
    model = Foo
    fields = ('__all__')
    extra_kwargs = {
        'content_type': {'required': False},
    }

but it returns attribute error: UUID object has no attribute int.
If anyone knows how to do it, kindly share here :) thanks for checking out my post

How to post to a model which contains a GenericForeignKey Content Type field?


Jason

unread,
Jul 4, 2018, 7:26:45 AM7/4/18
to Django REST framework
that documentation link shows how to do it, you just need to scroll down to the TaggedObjectRelatedField example.  

But I also think you have an issue with your model:

contentObject = GenericForeignKey()

should have the fields you want acting as FKs as parameters for GFK

there's also a package that could handle this for you: https://github.com/Ian-Foote/rest-framework-generic-relations

kharis

unread,
Jul 4, 2018, 9:33:09 PM7/4/18
to Django REST framework
I thought when using GenericForeignKey with no parameter, it searches for the default field 'content_type' and 'object_id'
and the TaggedObjectRelatedField example is for converting object to primitive dictionary. Isn't it used when returning GET request? 

Thanks for the package reference, I might try that :)

kharis

unread,
Jul 5, 2018, 5:08:03 AM7/5/18
to Django REST framework
So apparently I just miss assigned the validated_data["object_id"]. It should be assigned with an id not the instance. After I updated the create serializer method to: 

 def create(self, validated_data):
        content_type
=  validated_data.get("object_id")
        taggedObject
= Bar1.objects.get(id=content_type) if Bar1.objects.filter(id=content_type).count() > 0 else None
        taggedObject
= Bar2.objects.get(id=content_type) if Bar2.objects.filter(id=content_type).count() > 0 and taggedObject == None else None


       
if taggedObject is not None:
            contentType
= ContentType.objects.get_for_model(taggedObject)
           
if contentType is not None:
                validated_data
["content_type"] =
contentType
                validated_data
["object_id"] = taggedObject.id
       
return super(FooSerializer, self).create(validated_data)

It now works! Thanks! 

Jason

unread,
Jul 5, 2018, 8:22:34 AM7/5/18
to Django REST framework
nice find, and thanks for posting the solution :-)
Reply all
Reply to author
Forward
0 new messages