Я использовал этот SO вопрос для включения полнотекстового поиска в mysql дБ в Django приложении.
# models.py
class CaseSnapshot(BaseModel):
text = models.TextField(blank=True)
class Search(models.Lookup):
lookup_name = "search"
def as_mysql(self, compiler, connection):
lhs, lhs_params = self.process_lhs(compiler, connection)
rhs, rhs_params = self.process_rhs(compiler, connection)
params = lhs_params + rhs_params
return f"MATCH (%s) AGAINST (%s IN BOOLEAN MODE)" % (lhs, rhs), params
models.TextField.register_lookup(Search)
Поскольку Django не поддерживает полнотекстовый поиск на mysql мы должны добавить наш индекс, поэтому мы изменим сгенерированный файл миграции:
# Generated by Django 2.2 on 2020-04-28 03:41
from django.db import migrations, models
# Table details
table_name = "by_api_casesnapshot"
field_name = "text"
index_name = f"{table_name}_{field_name}_index"
class Migration(migrations.Migration):
dependencies = [("by_api", "0033_add_tag_color")]
operations = [
migrations.CreateModel(...), # As auto-generated
migrations.RunSQL(
f"CREATE FULLTEXT INDEX {index_name} ON {table_name} ({field_name})",
f"DROP INDEX {index_name} ON {table_name}",
),
]
Проверка работы
# dbshell
mysql> SHOW INDEX FROM by_api_casesnapshot;
+---------------------+--------------------------------+-------------+-----+------------+-----+
| Table | Key_name | Column_name | ... | Index_type | ... |
+---------------------+--------------------------------+-------------+-----+------------+-----+
| by_api_casesnapshot | PRIMARY | id | ... | BTREE | ... |
| by_api_casesnapshot | by_api_casesnapshot_text_index | text | ... | FULLTEXT | ... |
+---------------------+--------------------------------+-------------+-----+------------+-----+
#shell
>>> snap = CaseSnapshot.objects.create(text="XXX")
# Regular query first
>>> CaseSnapshot.objects.filter(text__contains="X")
<QuerySet [<CaseSnapshot pk=1>]>
# Now test custom search lookup
>>> CaseSnapshot.objects.filter(text__search="XXX")
<QuerySet [<CaseSnapshot pk=1>]>
>>> CaseSnapshot.objects.filter(text__search="X*")
<QuerySet [<CaseSnapshot pk=1>]>
>>> CaseSnapshot.objects.filter(text__search="X*").query.__str__()
'SELECT `by_api_casesnapshot`.`id`, `by_api_casesnapshot`.`text` FROM `by_api_casesnapshot` WHERE MATCH (`by_api_casesnapshot`.`text`) AGAINST (X* IN BOOLEAN M
ODE)'
Все, что работает в точности так, как задумано.
Проблема
Здесь все не получается - все запросы, использующие __search
lookups во время тестовых прогонов , возвращают пустой набор наборов запросов Я даже думал, что сгенерированный запрос идентичен.
Тесты
from django.test import TestCase
from by_api.models import CaseSnapshot
class TestModels(TestCase):
def test_search(self):
CaseSnapshot.objects.create(text="XXX")
rv1 = CaseSnapshot.objects.filter(text__contains="XXX")
# Passes
self.assertEqual(rv1.count(), 1)
rv2 = CaseSnapshot.objects.filter(text__search="XXX")
# Fails - count i
self.assertEqual(rv2.count(), 1)
Я попытался отладить это с помощью pdb в тестовом прогоне, и обратите внимание, что сгенерированный запрос идентичен приведенному выше в среда оболочки:
>>> rv2.query.__str__())
'SELECT `by_api_casesnapshot`.`id`, `by_api_casesnapshot`.`text` FROM `by_api_casesnapshot` WHERE MATCH (`by_api_casesnapshot`.`text`) AGAINST (XXX IN BOOLEAN MODE)'
Вопрос
Почему использование поиска __search
во время тестов приводит к пустому набору запросов?
Что Я пытался
- просмотр mysql стоп-слов, вызывающих пустые запросы * 104 3 *
- убедитесь, что тестовые базы данных и оболочки db имеют одинаковые значения
- попытался выполнить произведенный запрос с помощью курсора - также пусто
Обновление
Я только что заметил если я использую unittest.TestCase
вместо django.test.TestCase
, тесты пройдены - может ли это быть связано с тем, как тестовый пример django оборачивает транзакции в БД?