Skip to content

Commit

Permalink
миксин для поиска
Browse files Browse the repository at this point in the history
  • Loading branch information
fivan999 committed Feb 18, 2024
1 parent 55ae597 commit 1415be1
Show file tree
Hide file tree
Showing 14 changed files with 128 additions and 60 deletions.
1 change: 1 addition & 0 deletions backend/cfehome/cfehome/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
'rest_framework.authtoken',
'products.apps.ProductsConfig',
'users.apps.UsersConfig',
'search.apps.SearchConfig',
]

MIDDLEWARE = [
Expand Down
1 change: 1 addition & 0 deletions backend/cfehome/cfehome/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
'rest_framework.authtoken',
'products.apps.ProductsConfig',
'users.apps.UsersConfig',
'search.apps.SearchConfig',
]

MIDDLEWARE = [
Expand Down
6 changes: 5 additions & 1 deletion backend/cfehome/products/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,9 @@
class ProductModelIndex(algoliasearch_django.AlgoliaIndex):
"""индекс Algolia для модели Product"""

fields = ('title', 'description', 'user')
fields = ('title', 'description', 'user', 'is_public')
settings = {
'attributesForFaceting': ['user', 'is_public'],
'searchableAttributes': ['title', 'description'],
}
index_name = 'product_index'
3 changes: 1 addition & 2 deletions backend/cfehome/products/models.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import users.models

import django.db.models

import products.managers
import users.models


class Product(django.db.models.Model):
Expand Down
18 changes: 0 additions & 18 deletions backend/cfehome/products/search_service.py

This file was deleted.

5 changes: 3 additions & 2 deletions backend/cfehome/products/serializers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import rest_framework.reverse
import rest_framework.serializers
import users.serializers

import products.models
import users.serializers


class ProductListSerializer(rest_framework.serializers.ModelSerializer):
Expand All @@ -11,10 +11,11 @@ class ProductListSerializer(rest_framework.serializers.ModelSerializer):
url = rest_framework.serializers.HyperlinkedIdentityField(
view_name='products:product-detail'
)
user = users.serializers.UserPublicSerializer(read_only=True)

class Meta:
model = products.models.Product
fields = ['pk', 'url', 'title', 'price', 'description']
fields = ['pk', 'url', 'title', 'price', 'description', 'user']


class ProductDetailSerializer(rest_framework.serializers.ModelSerializer):
Expand Down
49 changes: 17 additions & 32 deletions backend/cfehome/products/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,29 @@

import products.models
import products.permissions
import products.search_service
import products.serializers
import search.mixins
import search.services


class ProductViewSet(rest_framework.viewsets.ModelViewSet):
class ProductViewSet(
search.mixins.ListSearchMixin, rest_framework.viewsets.ModelViewSet
):
"""вьюсет для модели Product"""

queryset = products.models.Product.objects.all()

def get_queryset(self) -> django.db.models.QuerySet:
"""
получаем queryset в зависимости от
поискового запроса и пользователя
"""
if self.action == 'list':
user_pk = None
if self.request.user.is_authenticated:
user_pk = self.request.user.pk
user_based_queryset = (
products.models.Product.objects.search_by_user(user_pk)
)

query = self.request.GET.get('query', '')
if not query:
return user_based_queryset

return user_based_queryset.filter(
pk__in=products.search_service.perform_search(
query, 'product_index'
)
)
return self.__class__.queryset
availabe_search_params = {'user': str, 'is_public': lambda val: val == '1'}
index_name = 'product_index'

def get_default_queryset(self) -> django.db.models.QuerySet:
"""получаем базовый queryset для поиска по нему"""
user_pk = None
if self.request.user.is_authenticated:
user_pk = self.request.user.pk
user_based_queryset = (
products.models.Product.objects.search_by_user(user_pk)
).select_related('user')
return user_based_queryset

def get_serializer_class(self) -> rest_framework.serializers.Serializer:
"""получаем serializer для запроса"""
Expand All @@ -59,9 +50,3 @@ def get_permissions(self) -> list:
products.permissions.RetrieveUpdateDestroyProductPermission
]
return [permission() for permission in permission_classes]

# def list(
# self, request: django.http.HttpRequest, *args, **kwargs
# ) -> django.http.HttpResponse:
# """список элементов"""
# return super().list(request, *args, **kwargs)
Empty file.
9 changes: 9 additions & 0 deletions backend/cfehome/search/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import django.apps


class SearchConfig(django.apps.AppConfig):
"""базовый класс приложения Search"""

default_auto_field = 'django.db.models.BigAutoField'
name = 'search'
verbose_name = 'поиск'
41 changes: 41 additions & 0 deletions backend/cfehome/search/mixins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import abc
import typing

import django.db.models

import search.services


class ListSearchMixin:
"""миксин для посика по списку"""

availabe_search_params = dict[str, typing.Callable]
index_name: str

@abc.abstractmethod
def get_default_queryset(self) -> django.db.models.QuerySet:
"""базовый queryset для поиска"""
...

def get_queryset(self) -> django.db.models.QuerySet:
"""
получаем queryset в зависимости от поискового запроса
"""
if self.action == 'list':
default_queryset = self.get_default_queryset()

query = self.request.GET.get('query', '')

params = search.services.extract_search_params(
self.request.GET, self.availabe_search_params
)

if not params and not query:
return default_queryset

return default_queryset.filter(
pk__in=search.services.get_results_ids(
query, self.index_name, **params
)
)
return self.__class__.queryset
45 changes: 45 additions & 0 deletions backend/cfehome/search/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import typing

import algoliasearch.search_index
import algoliasearch_django


def extract_search_params(
request_get: dict, available_params: dict[str, typing.Callable]
) -> dict:
"""получаем параметры поиска из словаря request.GET"""
params = {}
for param in available_params:
val = request_get.get(param, '')
if val:
params[param] = available_params[param](val)
return params


def get_search_index(
index_name: str,
) -> algoliasearch.search_index.SearchIndex:
"""возвращаем объект индекса по его названию"""
return algoliasearch_django.algolia_engine.client.init_index(index_name)


def get_search_results(query: str, search_index: str, **kwargs) -> dict:
"""
получаем ответ от algolia в виде словаря
по запросу query и search_index
"""
search_params = {}
kwargs_filters = [f'{k}:{v}' for k, v in kwargs.items() if v]
if kwargs_filters:
search_params['facetFilters'] = kwargs_filters
index = get_search_index(search_index)
return index.search(query, search_params)


def get_results_ids(query: str, search_index: str, **kwargs) -> list[int]:
"""получаем id записей по запросу query и search_index"""
result_inds = [
int(item['objectID'])
for item in get_search_results(query, search_index, **kwargs)['hits']
]
return result_inds
4 changes: 2 additions & 2 deletions backend/cfehome/users/admin.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import users.models

import django.contrib.admin
import django.contrib.auth.admin

import users.models


class UserAdmin(django.contrib.auth.admin.UserAdmin):
"""отображение модели пользователя в админке"""
Expand Down
4 changes: 2 additions & 2 deletions backend/cfehome/users/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import users.managers

import django.contrib.auth.models

import users.managers


class User(django.contrib.auth.models.AbstractUser):
"""кастомный пользователь"""
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ extend-exclude='''
[tool.isort]
default_section = "THIRDPARTY"
known_django = "django"
known_local_folder = ["cfehome", "api", "products"]
known_local_folder = ["cfehome", "products", "search", "users"]
sections = ["FUTURE","STDLIB","THIRDPARTY","DJANGO","FIRSTPARTY","LOCALFOLDER"]
skip = [".gitignore", "venv", "env"]
skip_glob = ["*/migrations/*"]
Expand Down

0 comments on commit 1415be1

Please sign in to comment.