Как получить один объект из отношения "многие ко многим" с помощью elasticsearch в django

Я пытаюсь использовать elasticsearch для функциональности поиска на моем сайте django. Вот мой код.

models.py:

class Category(MPTTModel):
    name = models.CharField()
   
class Product(models.Model):
    name = models.CharField()
    description = models.TextField()
    category = TreeManyToManyField(Category)    
   
class ProductImage(models.Model):
    product = models.ForeignKey(
        Product,
        on_delete=models.PROTECT,
        related_name= "product_pictures",
        )

    image = models.ImageField(...)
   

class ProductInventory(models.Model):
    product = models.ForeignKey(Product, related_name="product", on_delete=models.PROTECT)     
    product_images =  models.ManyToManyField(
                      ProductImage, 
                      related_name = "product_inventory_images",
                      through="MediaProductImage")
    is_default_varient = models.BooleanField(default=False)
    
class MediaProductImage(models.Model):
    product_inventory = models.ForeignKey(
        ProductInventory,
        on_delete=models.PROTECT,
        related_name="media_product_inventory",
    )
    image = models.ForeignKey(
        ProductImage,
        on_delete=models.PROTECT,
        related_name="media_product_image",
    )

    is_feature = models.BooleanField(
        default=False,
    )
   

documents.py:

@registry.register_document
class ProductInventoryDocument(Document):  
    product = fields.ObjectField(
        properties={
            "name": fields.TextField(),
            "description": fields.TextField()
        }
    )

    category = fields.ObjectField(
        properties={
            "name": fields.TextField()
        }
    )

    product_images = fields.NestedField(
        properties={
        'image': fields.FileField(), #**Is it correct field for image?**
    })

    media_product_inventory = fields.ObjectField(
        properties={
        'is_feature': fields.BooleanField(),
    })

    class Index:
        # Name of the Elasticsearch index
        name = 'productinventory'


    class Django:
        model = ProductInventory # The model associated with this Document

        # The fields of the model you want to be indexed in Elasticsearch
        fields = [
            'id',
            'is_default_varient',
        ]

Я хочу найти продукт из таблицы ProductInventory, соответствующий

  1. by matching product name [product model]
  2. by matching product description [product model]
  3. by matching product category # if possible [by filtering Category name]
  4. filter by is_deafult_varient=True # for getting default ProductInventory object
  5. filter by media_product_inventory__is_feature=True # for getting single-image(ProductImage table) through matching is_feature=True(MediaProductImage table)

Подобный нормальный запрос для достижения этого (3,4,5). Я хочу получить такой результат:

products = 
ProductInventory.objects.filter(product__category__name=category.name).filter(is_default_varient=True).filter(media_product_inventory__is_feature=True).values("id","product__name","media_product_inventory__image__image")

# products[0]
{'id': 3, 'product__name': "Men's Formal Shirt", 'media_product_inventory__image__image': 'products/images/light-blue-one_8cn7hBL.jpg'}

Пакеты:

django-elasticsearch-dsl==7.2.2 elasticsearch==7.17.2 elasticsearch-dsl==7.4.0

views.py:

from .documents import ProductInventoryDocument
from elasticsearch_dsl import Q

    def search(request):
            search_document = ProductInventoryDocument
            q = Q(
                'multi_match',
                query=query,
                fields=[
                    'product.name',
                    'product.description',
                ], fuzziness="auto") & Q(
                    'bool',
                    should=[
                        Q('match', is_default_varient=True),
                        ], minimum_should_match=1 
                )
            
            search = search_document.search().query(q)
            s = search.filter('term', media_product_inventory__is_feature=True)
            results = s.execute()
    
            for item in results:
                    print(item.media_product_inventory)
                    # more than one item - [{'is_feature': True}, {'is_feature': False},..]
                    print(item.media_product_inventory[0].is_feature)   # True
                    print(item.product_images) 
                    # more than one item - [{'image': '/media/products/images/one.jpg...}, {'image': '/media/products/images/two.jpg...},...]
                    print(item.product_images[0].image)
                    # /media/products/images/one.jpg

Шаблон:

<body>
    {% for item in products %}
    
      <p>{{ item.product.category.name }}</p>  # Not giving any result
      <p> {{ item.product.name }}</p>
      <div>{{ item.product.description }}</div>

      {% for x in item.media_product_inventory %}   
         <p>{{ x.is_feature }}</p> # True False False ...
      {% endfor %}

    {% endfor %}
</body>

Что я достиг до сих пор: Поиск продуктаИнвентарный элемент

  1. by matching product name -- done
  2. by matching product description -- done
  3. by matching category # tried but didn't work
  4. filter by is_deafult_varient=True -- done -- return default productinventory object

Проблемы, с которыми я сталкиваюсь:

  1. filter by media_product_inventory__is_feature=True # returns a list of media_product_inventory objects. See the views.py result. I want single image object from ProductImage model.
  2. Filter by category doesn't work. In template "{{item.product.category.name}}" don't show the category name of the product.
  3. To display the the image of the product. I had to use loop.

Кто-нибудь может помочь мне найти решение этих проблем?

Вернуться на верх