Shadow Walker
Shadow Walker

Reputation: 1199

How to get field name instead of foreign id in django

In my django app, i have four model tables linked using foreign keys, the problem is, when i make a query for any model table, the fields that are linked through foreign keys are returned as id's instead of the name.

My way does not work.

My customers models.py file

1st model

class Customer(models.Model):
    name = models.CharField(max_length=50)
    phone = models.CharField(max_length=20, unique=True)
    email = models.EmailField(max_length=255, unique=True, blank=True)
    image = models.ImageField(default='default.png', upload_to='customer_photos/%Y/%m/%d/')
    data_added = models.DateField(default=datetime.now, blank=True)

    def __str__(self):
        return self.name

2nd model

class ShippingAddress(models.Model):
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name="customer_ship_address")
    description = models.TextField(blank=True)   
    frequent_customer = models.BooleanField(default=False)
    address = models.CharField(max_length=50, blank=True)
    zip_code = models.CharField(max_length=12, blank=True)
    location = models.CharField(max_length=255)

    def __str__(self):
        return self.customer.name

3rd model - PaymentInvoice

class paymentInvoice(models.Model):

    shipping_address_owner = models.ForeignKey(
        ShippingAddress, on_delete=models.CASCADE, related_name="customer_invoice")
    product = models.ManyToManyField(
        Product, related_name='product_invoice')
    mode = models.CharField(max_length=20, choices=paymentMode.choices, default=paymentMode.MPESA)
    date = models.DateField(default=datetime.now)
    invoice_id = models.CharField(
        max_length=50, unique=True, default=increment_invoice_number)
    quantity = models.PositiveSmallIntegerField()
    status = models.CharField(
        max_length=20, choices=paymentStatus.choices, default=paymentStatus.PENDING)
    payment_made = models.DecimalField(max_digits=20, decimal_places=2)

    def __str__(self):
        return self.shipping_address_owner.customer.name

My Products models.py file

class Product(models.Model):
    slug = models.CharField(max_length=200, unique=True)
    name = models.CharField(max_length=200)
    available = models.BooleanField(default=True)
    description = models.TextField(blank=True)
    image = models.ImageField(default='default_product.jpg', upload_to='product_photos')
    category = models.CharField(max_length=200)
    qty_amount = models.CharField(
        max_length=20, choices=Qty_Choices, default='250ml')
    price = models.DecimalField(max_digits=10, decimal_places=2)

    def __str__(self):
        return self.name

My PaymentInvoice views.py file

class paymentInvoiceListCreateView(ListCreateAPIView):
    """
    ListCreateAPIView executes both 'GET' and 'POST' requests. i.e listing a queryset or creating a model instance.
    """
    serializer_class = paymentInvoiceSerializer
    queryset = paymentInvoice.objects.all().order_by(
        '-date').values(shipping_address_owner__customer)

When i make the above query the api returns the following, where the product field and shipping_address_owner field are just id's. I need there respective names instead.

{
    "count": 6,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": 9,
            "mode": "Mpesa",
            "date": "2020-07-27",
            "invoice_id": "INV-0006",
            "quantity": 1,
            "status": "Pending",
            "payment_made": "500.00",
            "shipping_address_owner": 9,
            "product": [
                1
            ]
        },

EDIT: paymentInvoiceSerializer

class paymentInvoiceSerializer(serializers.ModelSerializer):
    class Meta:
        model = paymentInvoice
        fields = '__all__'

Upvotes: 1

Views: 2071

Answers (2)

Arjun Shahi
Arjun Shahi

Reputation: 7330

You can try like this in your serializer.

class paymentInvoiceSerializer(serializers.ModelSerializer):
    shipping_address_owner = serializers.CharField(source='shipping_address_owner.customer')
    class Meta:
        model = paymentInvoice
        fields = ['shipping_address_owner',...]

Upvotes: 1

Navid Zarepak
Navid Zarepak

Reputation: 4208

You need to make some changes to your serializer.

If you just need the name and nothing more:

class paymentInvoiceSerializer(serializers.ModelSerializer):
    product = serializers.SerializerMethodField()
    shipping_address_owner = serializers.SerializerMethodField()

    class Meta:
        model = paymentInvoice
        fields = '__all__'
    
    def get_product(self, instance):
        names = []
        for product in instance.product.all():
            names.append(product.name)
        return names
    
    def get_shipping_address_owner(self, instance):
        return instance.shipping_address_owner.customer.name

You can also create different serializer for each model and pass them to their fields to get the full serialized data for them. It would be something like this:

ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = '__all__'

And then in your payment serializer:

class paymentInvoiceSerializer(serializers.ModelSerializer):
    product = ProductSerializer()

    class Meta:
        model = paymentInvoice
        fields = '__all__'

You can do the same for shipping_address_owner.

Upvotes: 1

Related Questions