Kenneth_H
Kenneth_H

Reputation: 141

Django REST framework queryset object has no attribute pk

I am building a larger Django-based web app, but I have a blocking issue with an API view that should return some data.

I the application I have a model (mail.models.Message) and a matching serializer and viewset.

For a reporting feature, I need to get filtered set of results and have therefoer created a seperate rest_framework.views.APIView for the purpose of the reporting. The model is located in one app and the reporting is in another app.

Here is the model:

class Message(models.Model):
class Meta:
    ordering = ('-timestamp',)
    get_latest_by = 'timestamp'

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    from_address = models.CharField("From", max_length=511, blank=True, default="", db_index=True)
    from_domain = models.CharField(max_length=255, blank=True, default="", db_index=True)
    to_address = models.CharField("To", max_length=511, blank=True, default="", db_index=True)
    to_domain = models.CharField(max_length=255, blank=True, default="", db_index=True)
    subject = models.TextField(blank=True, default="", db_index=True)
    client_ip = models.GenericIPAddressField("Client IP", db_index=True, null=True)
    mailscanner_hostname = models.CharField(max_length=255, db_index=True)
    spam_score = models.DecimalField(db_index=True, default=0.00, max_digits=7, decimal_places=2)
    mcp_score = models.DecimalField(db_index=True, default=0.00, max_digits=7, decimal_places=2)
    timestamp = models.DateTimeField(db_index=True)
    size = models.FloatField(default=0)
    token = models.CharField(max_length=255, null=True)
    mailq_id = models.TextField("Mailqueue identification", null=True)
    whitelisted = models.BooleanField(db_index=True, default=False)
    blacklisted = models.BooleanField(db_index=True, default=False)
    is_spam = models.BooleanField(db_index=True, default=False)
    is_mcp = models.BooleanField(db_index=True, default=False)
    is_rbl_listed = models.BooleanField("Is RBL listed", db_index=True, default=False)
    quarantined = models.BooleanField(db_index=True, default=False)
    infected = models.BooleanField(db_index=True, default=False)
    released = models.BooleanField(db_index=True, default=False)

    def __str__(self):
        return str(self.id) + "[" + str(self.from_address) + " to " + str(self.to_address) + "]"

And the matching serializer:

class MessageSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Message
        fields = (
            'id',
            'url',
            'from_address',
            'from_domain',
            'to_address',
            'to_domain',
            'subject',
            'client_ip',
            'mailscanner_hostname',
            'spam_score',
            'timestamp',
            'size',
            'token',
            'whitelisted',
            'blacklisted',
            'is_spam',
            'is_rbl_listed',
            'quarantined',
            'infected'
        )

The model has a lot of indexes on it to improve the performance search and filtering, but I have excluded these.

For the reporting, I have created this special view:

class MessageListApiView(ReportApiView):
    serializer_class = MessageSerializer
    queryset = Message.objects.all()

    def get_queryset(self):
        if self.request.method == 'POST':
            filters = self.request.data
            return MessageQuerySetFilter.filter(MessageQuerySetFilter, self.queryset, filters)
        else:
            return self.queryset


    def post(self, request, format=None):
        filters = request.data
        qs = self.get_queryset()
        serializer = MessageSerializer(qs, context={'request': request})
        return Response(serializer.data, 200)

In get_queryset, I use a special class and the only purpose of that class is to apply the appropriate filtering on the queryset and return it. Doing some testing, I have found the issue occurs when I have to return the Response.

I get this traceback:

Internal Server Error: /api/reports/messages/
Traceback (most recent call last):
  File "/Users/kenneth/Code/mailware/lib/python3.6/site-packages/django/core/handlers/exception.py", line 35, in inner
response = get_response(request)
  File "/Users/kenneth/Code/mailware/lib/python3.6/site-packages/django/core/handlers/base.py", line 128, in _get_response
response = self.process_exception_by_middleware(e, request)
      File "/Users/kenneth/Code/mailware/lib/python3.6/site-packages/django/core/handlers/base.py", line 126, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/kenneth/Code/mailware/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
  File "/Users/kenneth/Code/mailware/lib/python3.6/site-packages/django/views/generic/base.py", line 69, in view
return self.dispatch(request, *args, **kwargs)
  File "/Users/kenneth/Code/mailware/lib/python3.6/site-packages/rest_framework/views.py", line 494, in dispatch
response = self.handle_exception(exc)
  File "/Users/kenneth/Code/mailware/lib/python3.6/site-packages/rest_framework/views.py", line 454, in handle_exception
self.raise_uncaught_exception(exc)
  File "/Users/kenneth/Code/mailware/lib/python3.6/site-packages/rest_framework/views.py", line 491, in dispatch
response = handler(request, *args, **kwargs)
  File "/Users/kenneth/Code/mailware/src/reports/views.py", line 44, in post
return Response(serializer.data, 200)
  File "/Users/kenneth/Code/mailware/lib/python3.6/site-packages/rest_framework/serializers.py", line 537, in data
ret = super(Serializer, self).data
  File "/Users/kenneth/Code/mailware/lib/python3.6/site-packages/rest_framework/serializers.py", line 262, in data
self._data = self.to_representation(self.instance)
  File "/Users/kenneth/Code/mailware/lib/python3.6/site-packages/rest_framework/serializers.py", line 504, in to_representation
ret[field.field_name] = field.to_representation(attribute)
  File "/Users/kenneth/Code/mailware/lib/python3.6/site-packages/rest_framework/relations.py", line 376, in to_representation
url = self.get_url(value, self.view_name, request, format)
  File "/Users/kenneth/Code/mailware/lib/python3.6/site-packages/rest_framework/relations.py", line 312, in get_url
lookup_value = getattr(obj, self.lookup_field)
AttributeError: 'QuerySet' object has no attribute 'pk'
[19/Jan/2018 20:05:24] "POST /api/reports/messages/ HTTP/1.1" 500 16389

The above APIView class inherits a custom base class, which is what inherits the rest_framework.views.APIView class. This class is called ReportApiView and simply sets the paginator_class and the permission_class

class ReportApiView(APIView):
    permission_classes = (IsAuthenticated,)
    pagination_class = PageNumberPaginationWithPageCount

In the MessageListApiView post method it seems to go wrong in the return line, but I cannot find the reason why it is not working

Upvotes: 2

Views: 9373

Answers (5)

Raphael
Raphael

Reputation: 1812

I faced the similar issue after adding many=True, finally by making the below change it worked.

class Meta:
       extra_kwargs = {"id": {"validators": []}}

Upvotes: 0

run_the_race
run_the_race

Reputation: 2418

Are you using a nested serializer, and the parent serializer has a model which has unique_together constraint? See below on how to overriding the get_unique_together_validators` method to disable unique together checks, because the instance is not passed into nested serializer, and hence fails the check

class FooSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField()

    class Meta:
        model = models.Foo
        fields = '__all__'
        list_serializer_class = FooListSerializer

    def get_unique_together_validators(self):
        """Overriding method to disable unique together checks, because the instance is not passed into nested serializer, and hence fails the check"""
        return []

Upvotes: 1

Dardan Iljazi
Dardan Iljazi

Reputation: 837

I had this error, for the ones coming here from Google, my problem was this one:

The model I wanted to serialize had a field containing unique=True in it. The multiple data I sent to the Serializer (with many=True) were not unique (already in db), and instead of having an error telling me this, the "'QuerySet' object has no attribute 'pk'" was thrown which is really confusing. Anyway, after getting rid of the uniqueness, my code works.

Upvotes: 0

Awais Niaz
Awais Niaz

Reputation: 49

just you need to add many = true in serializer serializer = userSerializer.UserSeria(user,data=request.data,many=True)

Upvotes: 0

neverwalkaloner
neverwalkaloner

Reputation: 47374

You need to add many=True to serializer queryset:

serializer = MessageSerializer(qs, many=True, context={'request': request})

Upvotes: 9

Related Questions