5

I am using Django 1.7 and I working with REST Framework, I have implemented a simple API to get all the objects in a Model, but i want to filter the result putting a foreign object value.

For example, actually I can have a list of Objects with this URL

http://localhost:8000/api/ocompradetalle

and if I want only one, i only can put the PK (PrimaryKey) like this

http://localhost:8000/api/ocompradetalle/1/

I want to change the primary key to filter by a Foreign field value, I mean, I have a document (OCompra) with a value on the "folio" field, this document will have many details (OCompraDetalle), so, I want to be able to put something like this:

http://localhost:8000/api/ocompradetalle/F2033

being "F2033" a "folio" value and the response should bring me all the details of the OCompra Object with this value in the "folio" field.

this is what I have at the moment.

urls.py >> I have the router that takes the ViewSets

from rest_framework import routers
from inventario_rfid.views import OCompraViewSet, OCompraDetalleViewSet
from administracion.views import ProductoViewSet

router = routers.DefaultRouter()
router.register(r'ocompra',OCompraViewSet)
router.register(r'ocompradetalle',OCompraDetalleViewSet)
router.register(r'producto',ProductoViewSet)

urlpatterns = patterns('',
...
#APIS
url(r'^api/',include(router.urls)),
)

serializers.py

from rest_framework import serializers
from administracion.serializers import ProductoSerializer
from .models import OCompra, OCompraDetalle

class OCompraSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = OCompra
        fields = ('folio','codigo_proveedor','nombre_proveedor','status','fecha','subtotal','iva','envio',
                  'otro','total',)

class OCompraDetalleSerializer(serializers.HyperlinkedModelSerializer):
    producto = ProductoSerializer(many=False)
    ocompra = OCompraSerializer(many = False) << I WANT TO FILTER BY THIS (ocompra__folio)
    class Meta:
        model = OCompraDetalle
        fields = ('ocompra','producto','cantidad_ordenada','cantidad_recibida','fecha_entrega','precio','epc')

views.py

class OCompraDetalleViewSet(viewsets.ModelViewSet):
    queryset = OCompraDetalle.objects.all()
    serializer_class = OCompraDetalleSerializer


class OCompraViewSet(viewsets.ModelViewSet):
    queryset = OCompra.objects.all()
    serializer_class = OCompraSerializer

models.py

class OCompra(models.Model):
    folio = models.CharField(max_length=15)
    codigo_proveedor = models.CharField(max_length=15)
    nombre_proveedor = models.CharField(max_length=100)
    status = models.IntegerField(default=0)
    fecha = models.DateTimeField(auto_now_add=True)
    usuario = models.ForeignKey(User)
    subtotal = models.DecimalField(default=0,decimal_places=2, max_digits=20)
    iva = models.DecimalField(default=0,decimal_places=2, max_digits=20)
    envio = models.DecimalField(default=0,decimal_places=2, max_digits=20)
    otro = models.DecimalField(default=0,decimal_places=2, max_digits=20)
    total = models.DecimalField(default=0,decimal_places=2, max_digits=20)
    def __unicode__(self):
        return self.folio

class OCompraDetalle(models.Model):
    ocompra = models.ForeignKey(OCompra,related_name='detalles')
    producto = models.ForeignKey(Producto)
    cantidad_ordenada = models.DecimalField(default=0,decimal_places=2, max_digits=10)
    cantidad_recibida = models.DecimalField(default=0,decimal_places=2, max_digits=10)
    fecha_entrega = models.DateField(blank=True,null=True)
    precio = models.DecimalField(default=0,decimal_places=2, max_digits=10)
    epc = models.CharField(max_length=25,null=True,blank=True)
3
  • Could you not simply make another route, that is meant to call the API not by primary key, but by filtering on this foreign key? For example: localhost:8000/api/ocompradetalle_filtered_by_folio/F2033 and just write a filter query in the view? Commented Oct 27, 2014 at 20:14
  • How can I put the "folio" value like a parameter in the view? Commented Oct 27, 2014 at 20:23
  • Check drf documentation on filtering. It's all there :) Commented Oct 27, 2014 at 21:10

1 Answer 1

6

Ok, so if I'm understanding correctly you want to be able to filter on a list of OCompraDetalle objects AND be able to set the lookup field to a value in a joined table (Ocompra). This viewset should do both for you

from rest_framework import filters
OCompraDetalleViewSet(viewsets.ModelViewSet):
    filter_backends = (filters.DjangoFilterBackend,)
    filter_fields = ('precio','ocompra__envio')

    serializer_class = OCompraDetalleSerializer
    queryset = OCompraDetalle.objects.all()
    lookup_field = "ocompra__folio"

So:

http://localhost:8000/api/ocompradetalle/1/

will give you the ocompradetalle object where ocompradetalle.ocompra.folio is equal to 1. This will only work if the mapping is one-to-one or the folio column has a unique index. Detail views cannot return more than one object

If you would like to filter by a column on a joined table you could do:

http://localhost:8000/api/ocompradetalle/?ocompra__envio=blah

And you will get all the ocompradetalles where ocompradetalle.ocompra.envio = blah

Sign up to request clarification or add additional context in comments.

5 Comments

This one works perfect if I have one detail for a single purchase order like you said, one-to-one, but I wish to get ALL the details of a purchase order because an order has too many details, actually, every single order has a unique "folio" value. The result of testing your code is this: get() returned more than one OCompraDetalle -- it returned more than 20!
I have tested the second option, the filter_field with the question mark, and it returned me all the details successfully. THANK YOU SO MUCH!!
do you have an idea why I can't PUT in this api? a get this error when I try to PUT the exactly same JSON: "detail": "Method 'PUT' not allowed."
Are you trying to PUT to the right endpoint? PUT request must use the "detail" view. So, for example "localhost:8000/api/ocompradetalle/1" and not "localhost:8000/api/ocompradetalle"
I have resolved it, I was PUTting the whole JSON, like a list, by default, django rest framework can't process that so i have installed django-rest-framework-bulk and then, I can PUT/PATCH now. Thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.