Search code examples
djangodjango-querysetwagtaildjango-taggit

Getting related objects via tags in Django


My goal is to build a "Recommended Products" section in my e-commerce website when accessing an individual product page.

I have a sereis of products which have several user-defined tags in the admin. The tagging system is a combination of django-taggit and modelcluster, as detailed in the Wagtail-CMS docs.

I am trying to make it so that when a product page is accessed, Django looks at all other products with the same/similar tags and lists them in the "Recommended Products" section, based on the number of identical tags. The django-taggit docs seem to address this need in their API with the get_related() function, as per their docs.

I am struggling to get this working however as I keep on encountering errors, the latest being Exception Type: KeyError at /categories/test-category/test-product/ Exception Value: (15,). Here's my code so far:

class ProductTag(TaggedItemBase):
    content_object = ParentalKey('Product', related_name='tagged_items')

class Product(Page):
    ...

    tags = ClusterTaggableManager(through=ProductTag, blank=True)

def get_context(self, request):
    context = super(Product, self).get_context(request)

    current_tags = self.tags
    related_products = Product.objects.filter(current_tags.similar_objects())

    context['related_products'] = related_products
    return context

EDIT | Full error traceback below:

Environment:


Request Method: GET
Request URL: http://127.0.0.1:8000/categories/test-category/test-product/

Django Version: 1.11.5
Python Version: 3.5.2
Installed Applications:
['home',
 'search',
 'products',
 'wagtail.wagtailforms',
 'wagtail.wagtailredirects',
 'wagtail.wagtailembeds',
 'wagtail.wagtailsites',
 'wagtail.wagtailusers',
 'wagtail.wagtailsnippets',
 'wagtail.wagtaildocs',
 'wagtail.wagtailimages',
 'wagtail.wagtailsearch',
 'wagtail.wagtailadmin',
 'wagtail.wagtailcore',
 'modelcluster',
 'taggit',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles']
Installed Middleware:
['django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'django.middleware.security.SecurityMiddleware',
 'wagtail.wagtailcore.middleware.SiteMiddleware',
 'wagtail.wagtailredirects.middleware.RedirectMiddleware']



Traceback:

File "C:\Users\ddl_9\Envs\fstvl\lib\site-packages\django\core\handlers\exception.py" in inner
  41.             response = get_response(request)

File "C:\Users\ddl_9\Envs\fstvl\lib\site-packages\django\core\handlers\base.py" in _get_response
  187.                 response = self.process_exception_by_middleware(e, request)

File "C:\Users\ddl_9\Envs\fstvl\lib\site-packages\django\core\handlers\base.py" in _get_response
  185.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "C:\Users\ddl_9\Envs\fstvl\lib\site-packages\wagtail\wagtailcore\views.py" in serve
  26.     return page.serve(request, *args, **kwargs)

File "C:\Users\ddl_9\Envs\fstvl\lib\site-packages\wagtail\wagtailcore\models.py" in serve
  773.             self.get_context(request, *args, **kwargs)

File "C:\Users\ddl_9\Desktop\fstvl\products\models.py" in get_context
  143.      related_products = current_tags.similar_objects()

File "C:\Users\ddl_9\Envs\fstvl\lib\site-packages\taggit\utils.py" in inner
  146.         return func(self, *args, **kwargs)

File "C:\Users\ddl_9\Envs\fstvl\lib\site-packages\taggit\managers.py" in similar_objects
  350.                 tuple(result[k] for k in lookup_keys)

Exception Type: KeyError at /categories/test-category/test-product/
Exception Value: (15,)

If I try and access other product pages I get the same error, just with a different Exception Value. The function is expecting a key from a dict but for some reason it's being given the value instead... Could this be a compatibility issue with the code?


Solution

  • OK so after much research and tinkering I have found a solution, however this only works for Django >= 1.9.

    Github user nickhudkins ran into the same issue and edited the similar_objects() function to resolve the KeyError, as detailed in his ticket.

    The solution is to go into the taggit managers.py and edit the function as follows (+ denotes adding the lines and - removing them).

                 objs = rel_model._default_manager.filter(**{
                      "%s__in" % remote_field.field_name: [r["content_object"] for r in qs]
                  })
     +            actual_remote_field_name = remote_field.field_name
     +            if VERSION > (1, 9):
     +                actual_remote_field_name = f.target_field.get_attname()
     +            else:
     +                actual_remote_field_name = f.related_field.get_attname()
                  for obj in objs:
     -                items[(getattr(obj, remote_field.field_name),)] = obj
     +                items[(getattr(obj, actual_remote_field_name),)] = obj
              else:
                  preload = {}
                  for result in qs: