Search code examples
djangodjango-modelsdjango-ormdjango-q

Django: 'Q' object is not iterable


I have following APIView:

class SubmitFormAPIView(APIView):
    def put(self, request, pk):
        # some other codes
        
        form = Form.objects.get(id=pk)
        tu_filter, target_user = self._validate_target_user(request, form)
        user_status, created = UserFormStatus.objects.get_or_create(
            tu_filter,
            form_id=pk,
            user_id=request.user.pk
        )

        # Some other codes.
    
    def _validate_target_user(request, form):
        if some_conditions:
            return Q(), None
        else:
            try:
                target_user_id = int(request.GET.get('target_user_id))
            except ValueError:
                raise ValidationError()

            target_user = get_user_model().objects.get(id=target_user_id)
            return Q(target_user_id=target_user_id), target_user

but when django wants to execude get_or_create method, raises following error:

TypeError: 'Q' object is not iterable

Note: If _validate_target_user() returns Q(), None, no errors raised and view works fine. The error will be raised when return Q(target_user_id=target_user_id), target_user is returned.

I know, question information is not completed, just I want to know, what may cause this error?


Solution

  • Instead of returning a Q object, you can also just pass a dictionary of filters instead, like

    { 'target_user_id': target_user_id }
    

    The you can run the get_or_create with **tu_filter as arguments, bypassing the need for Q.

    class SubmitFormAPIView(APIView):
        def put(self, request, pk):
            # some other codes
            
            form = Form.objects.get(id=pk)
            tu_filter, target_user = self._validate_target_user(request, form)
            user_status, created = UserFormStatus.objects.get_or_create(
                **tu_filter,
                form_id=pk,
                user_id=request.user.pk
            )
    
            # Some other codes.
        
        def _validate_target_user(request, form):
            if some_conditions:
                return {}, None
            else:
                try:
                    target_user_id = int(request.GET.get('target_user_id))
                except ValueError:
                    raise ValidationError()
    
                target_user = get_user_model().objects.get(id=target_user_id)
                return { 'target_user_id': target_user_id }, target_user
    

    Edit: As to what causes the error, my guess would be that using Q as part of your get_or_create statement is unclear to Django, because it doesn't know what to do with it in case the object needs to be created. A better approach would therefor be:

    UserFormStats.objects.filter(tu_filter).get_or_create(form_id=pk, user_id=request.user.pk)