From 870dc43dc6feef04c2f981a0ed5cf804df13804b Mon Sep 17 00:00:00 2001 From: duker Date: Tue, 11 Sep 2018 14:07:18 +0300 Subject: [PATCH] rf#168 Implement new interface for `context.prepare_products` --- shopelectro/context.py | 14 ++++----- shopelectro/tests/tests_views.py | 54 ++++++++++++++++---------------- shopelectro/views/catalog.py | 15 ++++++--- 3 files changed, 45 insertions(+), 38 deletions(-) diff --git a/shopelectro/context.py b/shopelectro/context.py index 6f90eea9..8fd4b269 100644 --- a/shopelectro/context.py +++ b/shopelectro/context.py @@ -32,8 +32,6 @@ from images.models import Image from pages.models import ModelPage -from shopelectro import models - class SortingOption: def __init__(self, index=0): @@ -85,18 +83,16 @@ def number_url_map(self): # @todo #550:30m Split to ProductImagesContext and ProductBrandContext @lru_cache(maxsize=64) def prepare_tile_products( - products: ProductQuerySet, product_pages: QuerySet=None, tags: TagQuerySet=None + products: ProductQuerySet, product_pages: QuerySet, tags: TagQuerySet=None ): # @todo #550:60m Move prepare_tile_products func to context # Now it's separated function with huge of inconsistent queryset deps. assert isinstance(products, ProductQuerySet) - product_pages = product_pages or models.ProductPage.objects.all() images = Image.objects.get_main_images_by_pages( product_pages.filter(shopelectro_product__in=products) ) - tags = tags or models.Tag.objects.all() brands = ( tags .filter_by_products(products) @@ -368,7 +364,9 @@ def get_context_data(self): context = self.super.get_context_data() return { **context, - 'products_data': prepare_tile_products(self.products), + 'products_data': prepare_tile_products( + self.products, self.product_pages + ), 'sort': self.get_sorting_index(), } @@ -440,7 +438,9 @@ def get_context_data(self): return { **context, - 'products_data': prepare_tile_products(self.products), + 'products_data': prepare_tile_products( + self.products, self.product_pages + ), 'total_products': total_products, 'products_count': self.products_count, 'paginated': paginated, diff --git a/shopelectro/tests/tests_views.py b/shopelectro/tests/tests_views.py index cc0b2212..3e1d0783 100644 --- a/shopelectro/tests/tests_views.py +++ b/shopelectro/tests/tests_views.py @@ -19,8 +19,7 @@ from django.urls import reverse from django.utils.translation import ugettext as _ -from shopelectro import context -from shopelectro.models import Category, Product, Tag, TagGroup, TagQuerySet +from shopelectro import context, models from shopelectro.views.service import generate_md5_for_ya_kassa, YANDEX_REQUEST_PARAM from shopelectro.tests.helpers import create_doubled_tag @@ -31,7 +30,7 @@ def reverse_catalog_url( url: str, route_kwargs: dict, - tags: TagQuerySet=None, + tags: models.TagQuerySet=None, sorting: int=None, query_string: dict=None, ) -> str: @@ -60,13 +59,13 @@ class BaseCatalogTestCase(TestCase): fixtures = ['dump.json'] def setUp(self): - self.category = Category.objects.root_nodes().select_related('page').first() - self.tags = Tag.objects.order_by(*settings.TAGS_ORDER).all() + self.category = models.Category.objects.root_nodes().select_related('page').first() + self.tags = models.Tag.objects.order_by(*settings.TAGS_ORDER).all() def get_category_page( self, - category: Category=None, - tags: TagQuerySet=None, + category: models.Category=None, + tags: models.TagQuerySet=None, sorting: int=None, query_string: dict=None, ): @@ -80,13 +79,14 @@ class CatalogPage(BaseCatalogTestCase): def test_merge_product_cache(self): """Context merging should cached.""" - products = Product.objects.all()[:2] - with self.assertNumQueries(8): + products = models.Product.objects.all()[:2] + product_pages = models.ProductPage.objects.all() + with self.assertNumQueries(5): # N db queries without before cached - context.prepare_tile_products(products) + context.prepare_tile_products(products, product_pages) with self.assertNumQueries(0): # no db queries after cached - context.prepare_tile_products(products) + context.prepare_tile_products(products, product_pages) class CatalogTags(BaseCatalogTestCase): @@ -97,7 +97,7 @@ def test_category_page_contains_all_tags(self): tags = set(chain.from_iterable(map( lambda x: x.tags.all(), ( - Product.objects + models.Product.objects .get_by_category(self.category) .prefetch_related('tags') ) @@ -149,7 +149,7 @@ def test_contains_product_with_certain_tags(self): products_count = len(list(filter( lambda x: x.category.is_descendant_of(self.category), - Product.objects.filter(Q(tags=tags[0]) | Q(tags=tags[1])) + models.Product.objects.filter(Q(tags=tags[0]) | Q(tags=tags[1])) ))) self.assertContains(response, products_count) @@ -161,7 +161,7 @@ def test_tag_titles_content_disjunction(self): CategoryTagsPage with tags "Напряжение 6В" и "Напряжение 24В" should contain tag_titles var content: "6В или 24В". """ - tag_group = TagGroup.objects.first() + tag_group = models.TagGroup.objects.first() tags = tag_group.tags.order_by(*settings.TAGS_ORDER).all() response = self.get_category_page(tags=tags) self.assertEqual(response.status_code, 200) @@ -176,9 +176,9 @@ def test_tag_titles_content_conjunction(self): CategoryTagsPage with tags "Напряжение 6В" и "Cила тока 1А" should contain tag_titles var content: "6В и 1А". """ - tag_groups = TagGroup.objects.order_by('position', 'name').all() + tag_groups = models.TagGroup.objects.order_by('position', 'name').all() tag_ids = [g.tags.first().id for g in tag_groups] - tags = Tag.objects.filter(id__in=tag_ids) + tags = models.Tag.objects.filter(id__in=tag_ids) response = self.get_category_page(tags=tags) self.assertEqual(response.status_code, 200) delimiter = settings.TAG_GROUPS_TITLE_DELIMITER @@ -192,7 +192,7 @@ def test_tags_var(self): CategoryTagsPage should contain "tags" template var tag=each(tags) is Tag class instance. """ - tags = Tag.objects.order_by(*settings.TAGS_ORDER).all() + tags = models.Tag.objects.order_by(*settings.TAGS_ORDER).all() response = self.get_category_page(tags=tags) self.assertEqual(response.status_code, 200) tag_names = ', '.join([t.name for t in tags]) @@ -202,7 +202,7 @@ def test_doubled_tag(self): """Category tags page filtered by the same tag from different tag groups.""" tag_ = create_doubled_tag() response = self.get_category_page( - tags=Tag.objects.filter(id=tag_.id) + tags=models.Tag.objects.filter(id=tag_.id) ) self.assertEqual(response.status_code, 200) self.assertContains(response, tag_.name) @@ -211,7 +211,7 @@ def test_doubled_tag(self): def test_product_tag_linking(self): """Product should contain links on CategoryTagPage for it's every tag.""" - product = Product.objects.first() + product = models.Product.objects.first() self.assertGreater(product.tags.count(), 0) property_links = [ @@ -226,7 +226,7 @@ def test_product_tag_linking(self): def test_non_existing_tags_404(self): """Product should contain links on CategoryTagPage for it's every tag.""" - product = Product.objects.first() + product = models.Product.objects.first() self.assertGreater(product.tags.count(), 0) bad_tag_url = reverse('category', kwargs={ @@ -319,12 +319,12 @@ class LoadMore(TestCase): DEFAULT_LIMIT = 48 def setUp(self): - self.category = Category.objects.root_nodes().select_related('page').first() + self.category = models.Category.objects.root_nodes().select_related('page').first() def load_more( self, - category: Category=None, - tags: TagQuerySet=None, + category: models.Category=None, + tags: models.TagQuerySet=None, offset: int=0, # uncomment after implementation urls for load_more with pagination # limit: int=0, @@ -346,7 +346,7 @@ def test_pagination_numbering_first_page(self): self.assertEqual(get_page_number(self.load_more()), 1) def test_pagination_numbering_last_page(self): - offset = Product.objects.get_by_category(self.category).count() - 1 + offset = models.Product.objects.get_by_category(self.category).count() - 1 self.assertEqual( get_page_number(self.load_more(offset=offset)), offset // self.DEFAULT_LIMIT + 1, @@ -483,7 +483,7 @@ class ProductPage(TestCase): fixtures = ['dump.json'] def setUp(self): - self.product = Product.objects.first() + self.product = models.Product.objects.first() def test_orphan_product(self): self.product.category = None @@ -524,7 +524,7 @@ def test_available(self): """Page of an product with stock > 0 has $schema_url/InStock link.""" self.assertContains( self.client.get( - Product.objects.filter(in_stock__gt=0).first().url + models.Product.objects.filter(in_stock__gt=0).first().url ), f'{self.schema_url}/InStock', ) @@ -533,7 +533,7 @@ def test_not_available(self): """Page of an product with stock = 0 has $schema_url/PreOrder link.""" self.assertContains( self.client.get( - Product.objects.filter(in_stock=0).first().url + models.Product.objects.filter(in_stock=0).first().url ), f'{self.schema_url}/PreOrder', ) diff --git a/shopelectro/views/catalog.py b/shopelectro/views/catalog.py index 1e64a7dc..36d48203 100644 --- a/shopelectro/views/catalog.py +++ b/shopelectro/views/catalog.py @@ -67,7 +67,8 @@ def get_context_data(self, **kwargs): 'price_bounds': settings.PRICE_BOUNDS, 'group_tags_pairs': product.get_params(), 'tile_products': context.prepare_tile_products( - product.get_siblings(offset=settings.PRODUCT_SIBLINGS_COUNT) + product.get_siblings(offset=settings.PRODUCT_SIBLINGS_COUNT), + models.ProductPage.objects.all() ), } @@ -87,7 +88,8 @@ def render_siblings_on_404( tile_products=context.prepare_tile_products( inactive_product.get_siblings( offset=settings.PRODUCT_SIBLINGS_COUNT - ) + ), + models.ProductPage.objects.all() ), tile_title='Возможно вас заинтересуют похожие товары:', **url_kwargs, @@ -112,7 +114,10 @@ def get_context_data(self, **kwargs): .prefetch_related('category') .select_related('page') ) - tile_products = context.prepare_tile_products(top_products) + tile_products = context.prepare_tile_products( + top_products, + models.ProductPage.objects.all() + ) return { **context_, @@ -199,7 +204,9 @@ def load_more(request, category_slug, offset=0, limit=0, sorting=0, tags=None): view = request.session.get('view_type', 'tile') return render(request, 'catalog/category_products.html', { - 'products_data': context.prepare_tile_products(products), + 'products_data': context.prepare_tile_products( + products, models.ProductPage.objects.all() + ), 'paginated': paginated, 'paginated_page': paginated_page, 'view_type': view,