2

I have successfully been able to declare, map, and populate all model fields to Elasticsearch Index except the Price field on the BookPrice model which is using through attribute. If I remove the Price field from the document then it works fine.

However, I had successfully been able to build the Index in the past, but I think I made some changes and I lost the code integrity, and it's not working now.

I tried all possible combinations I found on help forums and official documents, but with no luck.

I am currently using

Elasticsearch == 7.8.0
elasticsearch-dsl == 7.2.1
django-elasticsearch-dsl == 7.1.1

models.py

class Publisher(models.Model):
    publisher_name = models.CharField(max_length=50)

class BookType(models.Model):
    book_type = models.CharField(max_length=20) # Hard Cover, Soft Cover, Kindle Edition, Digital PDF etc.

class Book(models.Model):
    book_title = models.TextField()
    book_types = models.ManyToManyField(BookType, through='BookPrice', through_fields=('book', 'book_type'))

class BookPrice(models.Model):
    book_type = models.ForeignKey(Booktype, on_delete=models.CASCADE)
    book = models.ForeignKey(Book, on_delete=models.CASCADE)
    price = models.DecimalField(max_digits=7, decimal_places=2)

documents.py

@registry.register_document
class BookDocument(Document):
    book_title = fields.TextField(
        attr='book_title',
        fields={
            'suggest': fields.CompletionField(),
        })

    publisher = fields.ObjectField(properties={
        'publisher_name': fields.TextField(),
    })

    book_types = fields.NestedField(properties={
        'id': fields.IntegerField(),
        'book_type': fields.TextField(),
        'price': fields.FloatField()
    })

    class Index:
        name = 'books'
        settings = {'number_of_shards': 1,
                    'number_of_replicas': 0}

    class Django:
        model = Book
        fields = [
            'id',
            'slug',
            'published_date',
            'pages',
        ]

        related_models = [BookPrice, BookType]

        def get_queryset(self):
            return super(BookDocument, self).get_queryset().select_related('publisher', 'book_types')

        def get_instances_from_related(self, related_instance):
            if isinstance(related_instance, BookPrice):
                return related_instance.book
            return related_instance.book_set.all()

Errors

Traceback (most recent call last):
  File "C:\Users\dev\PycharmProjects\books\venv\lib\site-packages\django_elasticsearch_dsl\fields.py", line 53, in get_value_from_instance
    instance = instance[attr]
TypeError: 'BookType' object is not subscriptable

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
   File "C:\Users\dev\PycharmProjects\books\venv\lib\site-packages\django_elasticsearch_dsl\fields.py", line 59, in get_value_from_instance
    instance = getattr(instance, attr)
AttributeError: 'BookType' object has no attribute 'price'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\dev\PycharmProjects\books\venv\lib\site-packages\django_elasticsearch_dsl\fields.py", line 64, in get_value_from_instance
    instance = instance[int(attr)]
ValueError: invalid literal for int() with base 10: 'price'

File "C:\Users\dev\PycharmProjects\books\venv\lib\site-packages\django_elasticsearch_dsl\fields.py", line 69, in get_value_from_instance
    raise VariableLookupError(
django_elasticsearch_dsl.exceptions.VariableLookupError: Failed lookup for key [price] in <BookType: Hard Cover>
Exception ignored in: <generator object cursor_iter at 0x00000000052F07B0>

Traceback (most recent call last):
  File "C:\Users\dev\PycharmProjects\books\venv\lib\site-packages\django\db\models\sql\compiler.py", line 1586, in cursor_iter
    cursor.close()
sqlite3.ProgrammingError: Cannot operate on a closed database.
Devendra
  • 73
  • 7
  • Can you post your Report model? – Andy Nov 07 '20 at 17:52
  • It is actually a Book model. Sorry for the typo. – Devendra Nov 08 '20 at 02:12
  • What if you use `NestedField` for `price`? – Andy Nov 08 '20 at 07:17
  • I tried using ```NestedField``` for ```price``` but the issue remains the same. A little example code will help me achieve this correctly. – Devendra Nov 10 '20 at 06:28
  • I see, `NestedField` works for normal `M2M`, `through` causes an error. Honesty, I don't know how to index `through` fields. It turns out that you seem to "miss your stop". He goes straight to the `BookType` model. – Andy Nov 11 '20 at 10:48

1 Answers1

0

I got this issue solved and worked using prepare_foo(self, instance) method. Using prepare_field

documents.py

def prepare_book_types(self, instance):
    return instance.get_price_for_es()

models.py

class Book(models.Model):
   ......

    def get_price_for_es(self):
       book_types= self.bookprice_set.all()
       book_types_price_list = []
       for x in book_types:
           book_types_price_list.append({'id': x.booktype_id, 'book_type': x.booktype.book_type, 'price': x.price})
       return book_types_price_list
Devendra
  • 73
  • 7