Search code examples
djangodjango-rest-frameworkdjango-rest-viewsets

Django Rest Framework ViewSet filtering issue based on foreignkey User field


I have a django project that I am working on. There are two models in this project. There is a user and account model. I am integreating the django rest framework viewsets. I will include them below. I am now integrating the Django Rest Framework in the project. I am trying to digure out how to do two things.

2 models:

Default django user

Account Model:

class Account(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    _id = models.CharField(max_length=45)
    name = models.CharField(max_length=140)
    balance = models.DecimalField(max_digits=100, decimal_places=2)
    currency = models.CharField(max_length=12)
    bank_name = models.CharField(max_length=120)
    routing = models.CharField(max_length=8)
    _class = models.CharField(max_length=22)
    type = models.CharField(max_length=22)
    active = models.BooleanField(default=True)
    main = models.BooleanField(default=False)
    synapse = models.BooleanField(default=False)
    create_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.user.username + ' - ' + self.name + ': ' + str(self.balance)

1 => I want to be able to type in the endpoint /api/users/accounts/omarjandlai and grab the single or many acounts with the omarjandali user foreignkey

2 => I want to be able to type into the following api/users/accounts/ and return all of the accounts that are in the database

I tried 4 - 5 different ways to get this to work and I couldnt get it to work.

Here are my serializers and views

serializers:

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ('user', '_id', 'name', 'balance', 'currency', 'bank_name',
                  'routing', '_class', 'type', 'active', 'main', 'synapse')

Views:

class AccountViewSet(viewsets.ModelViewSet):
    queryset = Account.objects.all();
    serializer_class = AccountSerializer
    filter_backends = (filters.DjangoFilterBackend,)
    filter_fields = ('user',)

what is happening is that when I do the

api/users/account i see all the accounts

api/users/account/omarjandali I get detail not found

urls:

router = DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
router.register(r'accounts', AccountViewSet, basename='account')
urlpatterns = router.urls

Solution

  • I can see that you are using DefaultRouter´s default functionality. That way you only can access records detail by using theirs primary key. Since you didn't specify any field as primary key explicitly (primary=True), Django sets field id, which is PositiveIntegerField as the primary key of the model. So, if you want to access the detail of a record you must use api/users/account/112/.

    You can access records by name using filters: api/users/account/?search='omarjandali', but you need to add SearchFilter.

    from rest_framework.filters import SearchFilter 
    
    class AccountViewSet(viewsets.ModelViewSet):
        queryset = Account.objects.all();
        serializer_class = AccountSerializer
        filter_backends = (filters.DjangoFilterBackend, SearchFilter)
        filter_fields = ('user',)
        search_fields = ('name',)
    

    If you want to stick to the url pattern api/users/account/omarjandali/, you have to add it to urls.py and use another viewset in order to let the original one to function accordingly to DefaultRouter definition:

    urls.py:

    router = DefaultRouter()
    router.register(r'users', UserViewSet, basename='user')
    router.register(r'accounts', AccountViewSet, basename='account')
    
    urlpatterns = [
        path('', include(router.urls)),
        path('api/users/account/<str:username>/', views.GetRecordsByNameViewSet.as_view({'get': 'list'}),
    ]
    

    views.py

    from rest_framework.response import Response
    
    class GetRecordsByNameViewSet(viewsets.ViewSet):
        def list(self, request, username):
            accounts = Account.objects.filter(user__username=username)
            accounts_to_return = AccountSerializer(accounts, many=True).data
    
            return Response(accounts_to_return)