Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django REST Framework with viewset-router queryset filtering

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)
like image 995
Alex Lord Mordor Avatar asked Oct 27 '14 20:10

Alex Lord Mordor


People also ask

How do I filter Queryset in Django REST framework?

The simplest way to filter the queryset of any view that subclasses GenericAPIView is to override the . get_queryset() method. Overriding this method allows you to customize the queryset returned by the view in a number of different ways.

What is Queryset in Django REST framework?

The root QuerySet provided by the Manager describes all objects in the database table. Usually, though, you'll need to select only a subset of the complete set of objects. The default behavior of REST framework's generic list views is to return the entire queryset for a model manager.

What is ViewSet in Django REST framework?

Django REST framework allows you to combine the logic for a set of related views in a single class, called a ViewSet . In other frameworks you may also find conceptually similar implementations named something like 'Resources' or 'Controllers'.

When should I use ViewSet vs APIView?

APIView and ViewSet all have their right use cases. But most of the time, if you are only doing CRUD on resources, you can directly use ViewSets to respect the DRY principle. But if you are looking for more complex features, you can go low-level because after all, viewsets are also a subclass of APIView .


1 Answers

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

like image 99
JoeyP Avatar answered Oct 20 '22 08:10

JoeyP