mingle
mingle

Reputation: 620

Getting values from generic foreign key in Django

I have a model defined with a generic foreign key like this:

class Status(models.Model):
    request_type = models.ForeignKey(ContentType)
    request_id = models.PositiveIntegerField()
    request = GenericForeignKey('request_type', 'request_id')

When I grab the relations with a normal queryset it works fine.

statuses = Status.objects.all()
for status in statuses:
    print status.request.name

This prints all the names normally.

However I am using Django eztables which relies on grabbing the values like

statuses = Status.objects.all().values('request__name')

However when I try this I get:

FieldError: Cannot resolve keyword 'request' into field. Choices are: request_id, request_type, request_type_id

Is there a way to achieve this in Django?

Upvotes: 1

Views: 2541

Answers (2)

mingle
mingle

Reputation: 620

I figured it out.

You must define your object with names like this:

class Status(BaseRequestStatus):
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')
    ... more data

Then in the object you would like to filter on you must create a reverse generic relationship:

class Request(models.Model):
    statuses = GenericRelation(Status, related_query_name='request')
    ... other data

Then finally you can do fun stuff like

statuses = Status.objects.all().values('request__name')

or

Status.objects.filter(request__name__contains="stack overflow")

Upvotes: 3

Mike Covington
Mike Covington

Reputation: 2157

According to the docs for the contenttypes framework:

Due to the way GenericForeignKey is implemented, you cannot use such fields directly with filters (filter() and exclude(), for example) via the database API.

Since values() is a part of this API and is listed just two sections below filter()/exclude() in the docs for Aggregations and other QuerySet clauses, my guess is that is why your approach fails.

I would recommend this instead:

status_list = Status.objects.all()
statuses = [{'request__name': status.request.name} for status in status_list]

Of course, the dict keys in the list comprehension can be whatever you'd like. I left them as 'request__name', since that is what you would've gotten if your approach had worked properly.

Upvotes: 1

Related Questions