Как получить один объект из отношения "многие ко многим" с помощью 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, соответствующий
- by matching product name [product model]
- by matching product description [product model]
- by matching product category # if possible [by filtering Category name]
- filter by is_deafult_varient=True # for getting default ProductInventory object
- 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>
Что я достиг до сих пор: Поиск продуктаИнвентарный элемент
- by matching product name -- done
- by matching product description -- done
- by matching category # tried but didn't work
- filter by is_deafult_varient=True -- done -- return default productinventory object
Проблемы, с которыми я сталкиваюсь:
- 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.
- Filter by category doesn't work. In template "{{item.product.category.name}}" don't show the category name of the product.
- To display the the image of the product. I had to use loop.
Кто-нибудь может помочь мне найти решение этих проблем?