Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Model Method from rest_framework modelSerializer

this is a simple question but I'm very new to django-rest-framework.

I was wondering if there is any way to access a method defined on the model from the serializer.?

Say I have a model

class Listing(models.Model):
    listingid           = models.BigIntegerField(primary_key=True)
    mappingid           = models.BigIntegerField()
    projectlevelid      = models.IntegerField()
    subsellerid         = models.IntegerField()
    iscreatedbyadmin    = models.BooleanField(default=None, null=True)
    createdon           = models.DateTimeField(auto_now_add=True, editable=False)
    validationstatus    = models.SmallIntegerField(default=0)

    def is_project(self):
        """ Returns True if listing is of Project Type (projectlevelid=6) else False""" 
        if self.projectlevelid == 6:
            return True
        else:
            return False

    def get_project_info(self):
        """Returns False if listing is not mapped to a project, else returns the project info"""
        if self.is_project() == False:
            return False
        return models.Project.objects.get(projectid=self.mappingid)

Is it possible for the serializer

class ListingSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.MasterListing

to have access to Listing.is_project i.e. for an object of the Listing model, can the serializer call its is_project method?

If so, can I set a field in the serializer such that if is_project returns true, the field is populated? I am trying for something like this,

class ListingSerializer(serializers.ModelSerializer):
    project = serializers.SomeRELATEDFieldTYPE()    # this field if populated if the `is_project` is true

    class Meta:
        model = models.MasterListing

I understand I can do this using some combination of required=False and SerializerMethodField, but maybe there is a simpler way?.

Note: It is not possible for me to set a foreign key to the mappingid, since it depends on the projectlevelid. I also can't affect this relationship so no further normalization is possible. I know that there might be some way using content-types, but we are trying to avoid that if it is possible..


EDIT: I solved the problem, but not as the question specified. I used this:

class ListingSerializer(serializers.ModelSerializer):
    project = serializers.SerializerMethodField()

    def get_project(self, obj):
        """Returns False if listing is not mapped to a project, else returns the project info"""
        if str(obj.projectlevelid) == str(6):
            projectObj = models.Project(projectid=obj.mappingid)
            projectObjSerialized = ProjectSerializer(projectObj)
            return projectObjSerialized.data
        return False

    class Meta:
        model = models.MasterListing

So, the original question still stands: "Is it possible for the modelSerializer to access its models methods?"

Also, another problem that now appears is, can I make the serializer exclude fields on demand, i.e. can it exclude mappingid and projectlevelid if it is indeed a project?

like image 641
kunl Avatar asked Jan 23 '15 07:01

kunl


2 Answers

For your first question source attribute is the answer, citing:

May be a method that only takes a self argument, such as URLField('get_absolute_url')

For your second answer, yes it is also possible. Check the example it provides in their docs: http://www.django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields

PS: I really love drf for its very complete documentation =).

EDIT

To use the source attribute you can just declare a new explicit field like so:

is_project = serializers.BooleanField(source='is_project')

With this, is_project field has the value of the is_project method of your instance. Having this, when creating the dynamic serializer (by modifying its init method) you can add the 'project' field if it's True.

like image 50
argaen Avatar answered Sep 19 '22 11:09

argaen


@argaen is absolutely right, source is a DRF core argument, and would most definitely solve your problem. However, it's redundant to use source, if the field name is the same as the source. So the above answer won't require you specify source, since field name is_project is the same as source name is_project.

So in your case:

is_project = serializers.BooleanField()
like image 38
Gabriel Avatar answered Sep 19 '22 11:09

Gabriel