Eric
Eric

Reputation: 343

Django add GenericForeignKey search fields

Django 1.8.4 add GenericForeignKey search fields does not work.

I've created several Product models like:

class Product1(models.Model):
    ...
    orders = GenericRelation(Order)

class Product2(models.Model):
    ...
    orders = GenericRelation(Order)

And in Order model:

class Order(models.Model):
    content_type = models.ForeignKey(
        ContentType,
        blank=True,
        null=True
        )
    object_id = models.PositiveIntegerField(
        blank=True,
        null=True
        )
    product = generic.GenericForeignKey('content_type','object_id')

This all works Fine,

But when I want to search Produt name in OrderAdmin, I added prodct__name search fields like this:

class OrderAdmin(admin.ModelAdmin):
    ...
    search_fields = [
        'product__name',
    ]

This Does Not Work!

Django raise that:

Field 'product' does not generate an automatic reverse relation and therefore cannot be used for reverse querying. 
If it is a GenericForeignKey, consider adding a GenericRelation.

Don't understand How Django 1.8 GenericForeignKey works, GenericRelations Already exist in Products models, but still doesn't work.

Upvotes: 1

Views: 732

Answers (1)

Eric
Eric

Reputation: 343

By override the default get_search_results method, the following code Generally works, but still got a little problem.

def get_search_results(self, request, queryset, search_term):
    """Override to Include searching `product__name` which is a
     GenericForeignKey Field of all product types.
     Returns a tuple containing a queryset to implement the search,
     and a boolean indicating if the results may contain duplicates.
    """
    products = [v for k, v in pro_models.PRODUCT_DICT.iteritems()]
    generic_queryset = queryset

    product_list = list()
    for bit in search_term.split():
        for product in products:
            product_queryset = product.objects.filter(
                Q(name__icontains=bit))
            product_list += list(product_queryset)

        generic_query = [Q(**{
            'content_type': ContentType.objects.get_for_model(product),
            'object_id': product.id
        }) for product in product_list]

        if generic_query:
            generic_queryset = generic_queryset.filter(
                reduce(operator.or_, generic_query))
        else:
            generic_queryset = generic_queryset.none()

    default_queryset, use_distinct = \
        super(OrderAdmin, self).get_search_results(
            request,
            queryset,
            search_term
        )
    return default_queryset | generic_queryset, use_distinct

Upvotes: 2

Related Questions