No matter how hard we try to get our data model correct (and even if we’ve done so!) sometimes by the time we need to use it, either for a public-facing API or an internal one, we need things to look a little different. Django Rest Framework(DRF) has some useful tools for allowing us to build serializers based on our models that differ somewhat from how we’ve defined things internally.
For our example, we’ll say we have a data model that looks, in part, like this:
class Item(models.Model): name = models.CharField(max_length=255) ... class Offer(models.Model): item = models.ForeignKey(Item) ... class Purchase(models.Model): offer = models.ForeignKey(Offer) ...
If we want to allow users to access
Items via the API, we may add an
ItemSerializer that leverages DRF’s ModelSerializer functionality, and looks like this, to start:
class ItemSerializer(serializers.ModelSerializer): class Meta: model = Item fields = ('name',)
Below are just two of the most common ways I’ve seen folks override their external data models to differ from internal ones. Other things you’re trying to accomplish that you’re not sure how to do? Feel free to reach out and I’ll add to this post! ✉️
Not all of the information we present is stored in the database, or should be. Sometimes we need to calculate or generate a piece of information at the time the data is requested. One example of this would be generating a
login_token for a
User when that record is accessed. For our example, we’ll include the number of times an item was purchased by adding a
purchase_count to our
ItemSerializer. The SerializerMethodField is perfect for this.
SerializerMethodField is a read-only field that allows you to define a method that is evaluated when the resource is accessed, and the return data used as the value for this field. The method should be given the name
get_<field_name> to be referenced automatically. Once we’ve added this field, our serializer will look like this:
from rest_framework.fields import SerializerMethodField class ItemSerializer(serializers.ModelSerializer): purchase_count = SerializerMethodField() class Meta: model = Item fields = ('name', 'purchase_count') def get_purchase_count(item): return Purchase.objects.filter( offer__item=item, state="success").count()
Item class has a field called
name. But what if we decide that we want to call this
display_name on our external-facing data model? We can do that by defining a new field on our serializer and giving it a
source that matches the internal data model, and updating the value in our
fields list in the
Meta class. After making that change, our serializer would look like this:
from rest_framework.fields import SerializerMethodField class ItemSerializer(serializers.ModelSerializer): purchase_count = SerializerMethodField() display_name = fields.CharField(source='name') class Meta: model = Item fields = ('display_name', 'purchase_count`) def get_purchase_count(item): return Purchase.objects.filter( offer__item=item, state="success").count()