Home > Mobile >  How to pass pk argument within class based view to queryset in Django
How to pass pk argument within class based view to queryset in Django

Time:06-10

I have the following Django urls/views and Models:

Models

ORDER_COLUMN_CHOICES = Choices(
    ('0', 'id'),
    ('1', 'code'),
    ('2', 'code_type'),
    ('3', 'created'),
    ('4', 'updated'),
    ('5', 'valid'),
)

class Identifier(TimeStampMixin, models.Model):
    code_type = models.CharField(max_length=10, null=True)
    code = models.CharField(max_length=12)
    account = models.ForeignKey(Account, on_delete=models.CASCADE, null=True)
    actflag = models.CharField(max_length=1, blank=True)
    valid = models.BooleanField(default=False)

    def __str__(self):
        return self.code

    class Meta:
        db_table = "portfolio_identifier"


def query_identifier_by_args(**kwargs):
    draw = int(kwargs.get('draw', None)[0])
    length = int(kwargs.get('length', None)[0])
    start = int(kwargs.get('start', None)[0])
    search_value = kwargs.get('search[value]', None)[0]
    order_column = kwargs.get('order[0][column]', None)[0]
    order = kwargs.get('order[0][dir]', None)[0]

    order_column = ORDER_COLUMN_CHOICES[order_column]
    # django orm '-' -> desc
    if order == 'desc':
        order_column = '-'   order_column

    queryset = Identifier.objects.all()
    total = queryset.count()

    if search_value:
        queryset = queryset.filter(Q(id__icontains=search_value) |
                                        Q(code__icontains=search_value) |
                                        Q(code_type__icontains=search_value) |
                                        Q(created__icontains=search_value) |
                                        Q(updated__icontains=search_value) |
                                        Q(valid__icontains=search_value))

    count = queryset.count()
    queryset = queryset.order_by(order_column)[start:start   length]
    return {
        'items': queryset,
        'count': count,
        'total': total,
        'draw': draw
    }

URLS

from . import views
from rest_framework.routers import DefaultRouter
from apps.portfolio.views import IdentifierViewSet

router = DefaultRouter()
router.register(r'portfolio', IdentifierViewSet)


urlpatterns = [
    path('portfolios/', views.portfolios, name="portfolios"),
    path('portfolio/<str:pk>/', views.portfolio, name="portfolio"),
    path('api/', include(router.urls)),

]

Views

def portfolio(request, pk):
    portfolio = Account.objects.get(id=pk)

    identifiers = Identifier.objects.filter(account=pk)

    context = {"portfolio": portfolio, "identifiers": identifiers}

    return render(request, 'portfolio.html', context)


class IdentifierViewSet(viewsets.ModelViewSet):

    queryset = Identifier.objects.all()

    serializer_class = IdentifierSerializer

    authentication_classes = []

    def list(self, request, **kwargs):

        try:
            identifier = query_identifier_by_args(**request.query_params)
            serializer = IdentifierSerializer(identifier['items'], many=True)
            result = dict()
            result['data'] = serializer.data
            result['draw'] = identifier['draw']
            result['recordsTotal'] = identifier['total']
            result['recordsFiltered'] = identifier['count']
            return Response(result, status=status.HTTP_200_OK, template_name=None, content_type=None)

        except Exception as e:
            return Response(e, status=status.HTTP_404_NOT_FOUND, template_name=None, content_type=None)

Within the Views I have a Class based view IdentifierViewSet with the following queryset line queryset = Identifier.objects.all() which retreievs all data from the db model however I would like to only retrieve queryset based on portfolio associated with the user's account which works using the following lines of code taken from the function based view portfolio:

 portfolio = Account.objects.get(id=pk)

 identifiers = Identifier.objects.filter(account=pk)

I was unable to pass the pk from urls to this class based view but was able to do so with teh function based view.

How can I go about passing the above object queries to within the Class based view to replace the queryset in the Class based view queryset = Identifier.objects.all()? Thanks

CodePudding user response:

To do that you can override the get_queryset method from the Django ModelViewSet.

def get_queryset(self):
    portfolio = Account.objects.get(id=self.request.account.pk)
    #... do something
    identifiers = Identifier.objects.filter(account=self.request.account.pk)
    return identifiers

CodePudding user response:

There are list(), retrieve(), create(), update(), partial_update(), destroy() functions and the pk parameter can be passed to four functions except list and create.

For example, for PUT method of the API, you can customize the update function.

class IdentifierViewSet(viewsets.ModelViewSet):
    queryset = Identifier.objects.all()
    serializer_class = IdentifierSerializer
    authentication_classes = []

    def list(self, request, *args, **kwargs):
        ...

    def update(self, request, pk):
        # you can get the pk here
        print(pk)
        
        # you can get the object with the current `pk` 
        instance = self.get_object()
        ...

You can also customize other functions like this.

  • Related