简体   繁体   中英

django-rest-framework: How to serialize join of database models?

I am working on a simple project for selling products from a website for which the model definition is as follows:

class Product(models.Model):
    """
        Model for Products
    """
    price = models.FloatField()
    description = models.TextField()
    url = models.CharField(max_length=200)

    def __str__(self):
        return self.description

class Order(models.Model):
    """
    Model for Orders
    """
    UNPAID = 0
    PAID = 1
    FAILED = 2

    STATUS = (
        (UNPAID, 'UNPAID'),
        (PAID, 'PAID'),
        (FAILED, 'FAILED'),
    )

    user = models.ForeignKey(User)
    product = models.ForeignKey(Product)
    orderdate = models.DateTimeField()
    token = models.CharField(max_length=30)
    paymentstatus = models.IntegerField(choices=STATUS)

Correspondingly the Serializers are defined as following:

class ProductSerializer(serializers.ModelSerializer):
    """
    Serialize Product list
    """
    class Meta:
        """
        Metadata for Product Serializationt to expose API
        """
        model = Product
        fields = ('id', 'price', 'description', 'url')

class OrderSerializer(serializers.ModelSerializer):
    """
    Serialize Order of Product
    """
    class Meta:
        """
        Order metadata
        """
        model = Order
        fields = ('id', 'user', 'orderdate', 'token', 'paymentstatus',
                  'product')


class OrderDetailSerializer(serializers.ModelSerializer):
    """
    Serialize Order Details
    """
    product = ProductSerializer(read_only=True)
    class Meta:
        """
        Order metadata
        """
        model = Order
        fields = ('id', 'user', 'orderdate', 'paymentstatus', 'product')

In the above example, is it possible to combine OrderSerializer and OrderDetailsSerializer into a single serializer ?

I use the OrderSerializer when the user places a new Order ie writes to database and OrderDetailSerializer to fetch the details of an order from the database.

You can do this with a single Serializer by using a custom field and by making the token write_only

class ProductSerializer(serializers.ModelSerializer):
    """
    Serialize Product list
    """
    class Meta:
        """
        Metadata for Product Serializationt to expose API
        """
        model = Product
        fields = ('id', 'price', 'description', 'url')

class ProductField(serializers.PrimaryKeyRelatedField):

    def to_representation(self, value):
        id = super(ProductField, self).to_representation(value)
        try:
          product = Product.objects.get(pk=id)
          serializer = ProductSerializer(product)
          return serializer.data
        except Product.DoesNotExist:
            return None

    def get_choices(self, cutoff=None):
        queryset = self.get_queryset()
        if queryset is None:
            return {}

        return OrderedDict([(item.id, self.display_value(item)) for item in queryset])

class OrderSerializer(serializers.ModelSerializer):
    """
    Serialize Order of Product
    """
    product = ProductField(queryset=Product.objects.all())

    class Meta:
        """
        Order metadata
        """
        model = Order
        fields = ('id', 'user', 'orderdate', 'token', 
                  'paymentstatus', 'product')
        extra_kwargs = {'token': {'write_only': True}}

The custom field will allow you to use the Model ID when posting, while getting the nested serializer when you are getting the item. eg.

POST:

{
    "product": 10,
    ...
}

GET:

{
    "product": {
        "url": "http://localhost:8000/..."
        "price": "$2.50",
        ...
    }
    ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM