internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: fidals/shopelectro

Issue:
Fix products exclude from price
Production config contains `7387` product excluded, but price contains the product anyway `<offer id="7387" available="true">`


Discussion:

Resolving patch:
diff --git a/shopelectro/management/commands/price.py b/shopelectro/management/commands/price.py
--- a/shopelectro/management/commands/price.py
+++ b/shopelectro/management/commands/price.py
@@ -137,7 +137,7 @@ def qs(self) -> QuerySet:
         return self.FILTERS[self.target](
             models.Product.objects.active()
             .filter(category__in=self.categories, price__gt=0)
-            .exclude(id__in=self.ignored)
+            .exclude(vendor_code__in=self.ignored)
         )
 
 
diff --git a/shopelectro/models.py b/shopelectro/models.py
--- a/shopelectro/models.py
+++ b/shopelectro/models.py
@@ -82,6 +82,7 @@ class Product(
         verbose_name=_('tags'),
     )
 
+    # @todo #910:30m  Explore and doc what is vendor_code.
     vendor_code = models.SmallIntegerField(verbose_name=_('vendor_code'))
     uuid = models.UUIDField(default=uuid4, editable=False)
     purchase_price = models.FloatField(
diff --git a/shopelectro/settings/base.py b/shopelectro/settings/base.py
--- a/shopelectro/settings/base.py
+++ b/shopelectro/settings/base.py
@@ -513,8 +513,7 @@ def get_robots_content():
     ]
 })
 
-
-# contains some values for example. Local.py will contain the real values
+# be careful, list of values should contain vendor_codes, not ids
 PRICE_IGNORED_PRODUCTS_MAP = defaultdict(list, {
     'YM': [1, 2, 3],
 })


Test patch:
diff --git a/shopelectro/tests/tests_commands.py b/shopelectro/tests/tests_commands.py
--- a/shopelectro/tests/tests_commands.py
+++ b/shopelectro/tests/tests_commands.py
@@ -339,6 +339,7 @@ def test_products_excluded_by_id(self):
             offer.attrib['id']
             for offer in self.prices['YM'].offers_node.findall('offer')
         )
+        self.assertTrue(ignored)
         self.assertFalse(to_ignore.intersection(ignored))
 
     def test_products_in_price(self):
Repository: fidals/shopelectro

Issue:
models.py:85: Explore and doc what is vendor_code.
The puzzle `910-f547fee5` from #910 has to be resolved: 

https://github.com/fidals/shopelectro/blob/e7de650f01b749f584f374923d7863083cc83b3c/shopelectro/models.py#L85-L85

The puzzle was created by duker33 on 28-Jun-19. 

Estimate: 30 minutes,  role: DEV. 

If you have any technical questions, don't ask me, submit new tickets instead. The task will be \"done\" when the problem is fixed and the text of the puzzle is _removed_ from the source code. Here is more about [PDD](http://www.yegor256.com/2009/03/04/pdd.html) and [about me](http://www.yegor256.com/2017/04/05/pdd-in-action.html). 



Discussion:
@artemiyds2 @duker33 please pay attention to this new issue.

Resolving patch:
diff --git a/shopelectro/models.py b/shopelectro/models.py
--- a/shopelectro/models.py
+++ b/shopelectro/models.py
@@ -82,7 +82,13 @@ class Product(
         verbose_name=_('tags'),
     )
 
-    # @todo #910:30m  Explore and doc what is vendor_code.
+    # `vendor_code` is a code that refers to the particular stock keeping unit (SKU).
+    # You can treat it as public unique id. Use it to publicly identify a product.
+
+    # We bring codes from 1C database and sync with products.
+    # We doesn't use the id field instead, because it is auto-increment sequence,
+    # that can't be changed easily. We decided to avoid that complexity.
+    # https://www.postgresql.org/docs/current/functions-sequence.html
     vendor_code = models.SmallIntegerField(verbose_name=_('vendor_code'))
     uuid = models.UUIDField(default=uuid4, editable=False)
     purchase_price = models.FloatField(


Test patch:
Repository: fidals/shopelectro

Issue:
main.less:449: Markup mobile tile-about and text for main...
The puzzle `891-9180fe3b` from #891 has to be resolved: 

https://github.com/fidals/shopelectro/blob/bc1d3db1da1faa557d353c2ff9ac4dbfe9296c5c/front/less/pages/main.less#L449-L449

The puzzle was created by @duker33 on 26-Jun-19. 

Estimate: 30 minutes,  role: DEV. 

If you have any technical questions, don't ask me, submit new tickets instead. The task will be \"done\" when the problem is fixed and the text of the puzzle is _removed_ from the source code. Here is more about [PDD](http://www.yegor256.com/2009/03/04/pdd.html) and [about me](http://www.yegor256.com/2017/04/05/pdd-in-action.html). 



Discussion:
@artemiyds2 @duker33 please pay attention to this new issue.
customized styles for js

Resolving patch:
diff --git a/shopelectro/settings/base.py b/shopelectro/settings/base.py
--- a/shopelectro/settings/base.py
+++ b/shopelectro/settings/base.py
@@ -305,10 +305,6 @@
 TOP_PRODUCTS = [291, 438, 1137, 2166, 2725, 2838, 3288, 3884, 3959, 2764]
 CATEGORY_STEP_MULTIPLIERS = [12, 15, 24, 25, 48, 50, 60, 100]
 
-# Reduce retail product prices by PRICE_REDUCER.
-# It is required to make prices on shopelectro.ru and se78.ru unique.
-PRICE_REDUCER = 1
-
 SHOP = {
     'id': '69886',
     'scid': '64788',


Test patch:
diff --git a/shopelectro/tests/tests_selenium.py b/shopelectro/tests/tests_selenium.py
--- a/shopelectro/tests/tests_selenium.py
+++ b/shopelectro/tests/tests_selenium.py
@@ -882,6 +882,19 @@ def test_accordion_minimize_by_double_click(self):
         self.assertFalse(accordion_content.is_displayed())
 
 
+@tag('slow')
+class MainPage(helpers.SeleniumTestCase):
+
+    def test_info_slider_is_desktop(self):
+        """Informational slider should appear only on mobile devices."""
+        self.browser.get('/')
+        self.wait_page_loading()
+        page = self.wait.until(EC.visibility_of_element_located(
+            (By.CSS_SELECTOR, '.tile-about .row')
+        ))
+        self.assertNotIn('slick-slider', page.get_attribute('class'))
+
+
 @tag('slow')
 class Search(helpers.SeleniumTestCase):
 
diff --git a/shopelectro/tests/tests_selenium_mobile.py b/shopelectro/tests/tests_selenium_mobile.py
--- a/shopelectro/tests/tests_selenium_mobile.py
+++ b/shopelectro/tests/tests_selenium_mobile.py
@@ -127,6 +127,13 @@ def test_search_submit(self):
         ))
         self.assertIn('Cate', result.text)
 
+    def test_info_slider_is_mobile(self):
+        """Informational slider should appear only on mobile devices."""
+        page = self.wait.until(EC.visibility_of_element_located(
+            (By.CSS_SELECTOR, '.tile-about .row')
+        ))
+        self.assertIn('slick-slider', page.get_attribute('class'))
+
     def test_catalog(self):
         """Catalog should expand on click on fa fa-chevron icons."""
         catalog = self.browser.find_element_by_class_name('js-mobile-catalog-btn')
Repository: fidals/shopelectro

Issue:
Get rid of pack prices multiplication
PO said that 1C stock now is giving already multiplied prices


Discussion:
`x2` because we are in a hurry so much
changed label since it was requirements bug

Resolving patch:
diff --git a/shopelectro/management/commands/_update_catalog/update_pack.py b/shopelectro/management/commands/_update_catalog/update_pack.py
--- a/shopelectro/management/commands/_update_catalog/update_pack.py
+++ b/shopelectro/management/commands/_update_catalog/update_pack.py
@@ -8,10 +8,9 @@
 import logging
 
 from django.conf import settings
-from django.db import models, transaction
+from django.db import models
 
 from catalog.models_expressions import Substring
-
 from shopelectro.exception import UpdateCatalogException
 from shopelectro.models import TagQuerySet, TagGroup
 
@@ -54,17 +53,6 @@ def update_in_packs(packs: TagQuerySet):
         pack.products.all().update(in_pack=max(in_pack, 1))
 
 
-def update_prices(packs: TagQuerySet):
-    """Multiply product prices on in pack quantity."""
-    fields_to_update = {}
-    for price in PRICES:
-        fields_to_update[price] = models.F(price) * models.F('in_pack')
-
-    with transaction.atomic():
-        packs.products().update(**fields_to_update)
-
-
 def main(*args, **kwargs):
     packs = find_pack_group().tags.all().prefetch_related('products')
     update_in_packs(packs)
-    update_prices(packs)


Test patch:
diff --git a/shopelectro/tests/tests_commands.py b/shopelectro/tests/tests_commands.py
--- a/shopelectro/tests/tests_commands.py
+++ b/shopelectro/tests/tests_commands.py
@@ -46,7 +46,7 @@ class UpdatePack(TestCase):
 
     def assert_prices(self, old: Product, new: Product):
         for price in update_pack.PRICES:
-            self.assertEqual(getattr(new, price), getattr(old, price) * new.in_pack)
+            self.assertEqual(getattr(new, price), getattr(old, price))
 
     def test_command(self):
         """The command updates only packed products."""
@@ -98,16 +98,6 @@ def get_packs():
                     f'Product: {product}, Pack: {pack}'
                 )
 
-    def test_update_prices(self):
-        tags = Tag.objects.get_packs()
-        products = list(tags.products())
-
-        update_pack.update_prices(tags)
-
-        for old, new in zip(products, tags.products()):
-            self.assertEqual(old.id, new.id)
-            self.assert_prices(old, new)
-
 
 @tag('fast')
 class UpdateProductsUnit(TestCase):
Repository: fidals/shopelectro

Issue:
models.py:74-75: Add MatrixBlock to the admin panel....
The puzzle `880-c9d8b5dc` from #880 has to be resolved: 

https://github.com/fidals/shopelectro/blob/6ed077725bf1891cd003097b5acaab3220274046/shopelectro/models.py#L74-L75

The puzzle was created by Artemiy on 04-Jul-19. 

Estimate: 30 minutes,  role: DEV. 

If you have any technical questions, don't ask me, submit new tickets instead. The task will be \"done\" when the problem is fixed and the text of the puzzle is _removed_ from the source code. Here is more about [PDD](http://www.yegor256.com/2009/03/04/pdd.html) and [about me](http://www.yegor256.com/2017/04/05/pdd-in-action.html). 



Discussion:
@artemiyds2 @duker33 please pay attention to this new issue.
>Inline it on Category Edit page. 

Django can't inline `MatrixBlock -> Category -> CategoryPage` relation into `CategoryPageAdmin`. We could use [nested-inline package](https://github.com/s-block/django-nested-inline) or change relation from `category` to `page` to reduce the nesting level.

But it seems for me it doesn't have a sense to inline `MatrixBlock` to `CategoryPage`, because not every page has a `MatrixBlock`. I'd say only several pages can have them.

So creation of separate admin model instead more appropriate. 

@duker33 What do you think? Will be OK just create a separate admin model?


Resolving patch:
diff --git a/shopelectro/admin.py b/shopelectro/admin.py
--- a/shopelectro/admin.py
+++ b/shopelectro/admin.py
@@ -287,6 +287,11 @@ def id_(self, obj):
         return obj.fake_order_number
 
 
+class MatrixBlockAdmin(mixins.PermissionsControl):
+
+    list_display = ['category', 'block_size']
+
+
 se_admin = SEAdminSite(name='se_admin')
 se_admin.register(CustomPage, models.CustomPageAdmin)
 se_admin.register(FlatPage, models.FlatPageAdmin)
@@ -298,5 +303,6 @@ def id_(self, obj):
 se_admin.register(se_models.ProductFeedback, ProductFeedbackPageAdmin)
 se_admin.register(se_models.TagGroup, TagGroupAdmin)
 se_admin.register(se_models.Tag, TagAdmin)
+se_admin.register(se_models.MatrixBlock, MatrixBlockAdmin)
 
 se_admin.register(Redirect)
diff --git a/shopelectro/models.py b/shopelectro/models.py
--- a/shopelectro/models.py
+++ b/shopelectro/models.py
@@ -71,12 +71,6 @@ def blocks(self):
 class MatrixBlock(models.Model):
     """It is an UI element of catalog matrix."""
 
-    # @todo #880:30m Add MatrixBlock to the admin panel.
-    #  Inline it on Category Edit page.
-
-    # @todo #880:60m Use MatrixBlock in the matrix view.
-    #  Get the block_size data from the matrix view and fill out the model.
-
     class Meta:
         verbose_name = _('Matrix block')
         verbose_name_plural = _('Matrix blocks')
@@ -89,7 +83,7 @@ class Meta:
         on_delete=models.CASCADE,
         primary_key=True,
         verbose_name=_('category'),
-        related_name=_('matrix_block'),
+        related_name='matrix_block',
         limit_choices_to={'level': 0},
     )
 


Test patch:
Repository: fidals/shopelectro

Issue:
Move "Возврат" link from header to footer
В шапке сайта убирай “Возврат” туда почти не кликают. переноси ссылку в футер. в блок О компании

From this doc:
https://docs.google.com/document/d/1y4xUQhQ4V_Ln5xwcEB2zMp7KTUQnkn9agKTTAU1z0-c/edit


Discussion:
we already have "Возврат" link at the footer. Just removed it from header
![image](https://user-images.githubusercontent.com/3526796/64946216-00f2cd00-d87b-11e9-85db-7f780f733d81.png)


Resolving patch:
diff --git a/shopelectro/settings/base.py b/shopelectro/settings/base.py
--- a/shopelectro/settings/base.py
+++ b/shopelectro/settings/base.py
@@ -479,7 +479,7 @@ def get_robots_content():
     'about': [
         {'name': 'Реквизиты', 'type': 'navigation', 'slug': 'rekvisity'},
         {'name': 'Купить оптом', 'type': 'articles', 'slug': 'wholesale'},
-        {'name': 'Гарантии и возврат', 'type': 'navigation', 'slug': 'return-goods-instr'},
+        {'name': 'Возврат и гарантии', 'type': 'navigation', 'slug': 'return-goods-instr'},
     ],
 }
 


Test patch:
Repository: fidals/shopelectro

Issue:
Create a custom page for 500 error
The page should have an apology and an suggestion to continue the purchase with the help of a consultant


Discussion:
@duker33 What do you think? 
@artemiy312 , it's great idea, let's move it!

Resolving patch:
diff --git a/shopelectro/urls.py b/shopelectro/urls.py
--- a/shopelectro/urls.py
+++ b/shopelectro/urls.py
@@ -1,15 +1,15 @@
-from datetime import timedelta
 from collections import OrderedDict
+from datetime import timedelta
 
 from django.conf import settings
 from django.conf.urls import url, include
 from django.conf.urls.static import static
 from django.contrib.sitemaps.views import sitemap
 from django.views.decorators.cache import cache_page, never_cache
+from django.views.generic import TemplateView
 
-from pages.views import RobotsView, SitemapPage
 from pages.urls import custom_page_url
-
+from pages.views import RobotsView, SitemapPage
 from shopelectro import sitemaps, views
 from shopelectro.admin import se_admin
 
@@ -132,3 +132,11 @@ def cache_page(arg):  # Ignore PyFlakesBear
         *static(settings.STATIC_URL, document_root=settings.STATIC_ROOT),
         *static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT),
     ]
+
+# Test and Debug environments replace real 404 and 500 error with stack traces.
+# We expose real 404 and 500 pages with separated urls to test them.
+if settings.TEST_ENV or settings.DEBUG:
+    urlpatterns += [
+        url(r'^404/$', TemplateView.as_view(template_name='404.html')),
+        url(r'^500/$', TemplateView.as_view(template_name='500.html')),
+    ]


Test patch:
diff --git a/shopelectro/tests/tests_views.py b/shopelectro/tests/tests_views.py
--- a/shopelectro/tests/tests_views.py
+++ b/shopelectro/tests/tests_views.py
@@ -931,3 +931,20 @@ def test_product_pack(self):
 
         self.assertEqual(len(results), 1, results)
         self.assertIn(str(int(product.price)), results[0], results[0])
+
+
+@tag('fast')
+class ErrorPageContent(TestCase):
+    """Test just error page content, but not it's response status code."""
+
+    def test_404(self):
+        self.assertContains(
+            self.client.get('/404/'),
+            text='Запрашиваемая страница не найдена'
+        )
+
+    def test_500(self):
+        self.assertContains(
+            self.client.get('/500/'),
+            text='Ошибка на стороне сервера'
+        )
Repository: fidals/shopelectro

Issue:
Create landing for sms mailing
See reqs at the [trello task](https://trello.com/c/3ddgHTWC/28-125-5k-%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D1%8B-%D0%B4%D0%BB%D1%8F-%D0%BF%D0%B5%D1%80%D0%B5%D0%BD%D0%B0%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B9-%D0%BD%D0%B0-%D0%BD%D0%B5%D0%B5-%D0%B8%D0%B7-%D1%81%D0%BC%D1%81-%D1%80%D0%B0%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B8)



Discussion:

Resolving patch:
diff --git a/shopelectro/urls.py b/shopelectro/urls.py
--- a/shopelectro/urls.py
+++ b/shopelectro/urls.py
@@ -121,6 +121,7 @@ def cache_page(arg):  # Ignore PyFlakesBear
     url(r'^shop/', include(ecommerce_urls)),
     url(r'^search/', include(search_urls)),
     url(r'^service/', include(service_urls)),
+    url(r'^sms/$', TemplateView.as_view(template_name='sms_landing.html'), name='sms_lending'),
     url(r'^sitemap\.xml$', cached_60d(sitemap), {'sitemaps': sitemaps}, name='sitemap'),
 ]
 


Test patch:
Repository: fidals/shopelectro

Issue:
Resurrect script to process photos to the DB
Now we are having script processing photos to the DB
`shopelectro/management/commands/images.py`

Since we have no tests for it, code may become stale. Resurrect it and move products to the prod


Discussion:

Resolving patch:
diff --git a/shopelectro/logic/header.py b/shopelectro/logic/header.py
--- a/shopelectro/logic/header.py
+++ b/shopelectro/logic/header.py
@@ -14,7 +14,7 @@ class Menu:
     @staticmethod
     def roots() -> pages_models.PageQuerySet:
         """
-        QuerySet with header menu items.
+        Queryset with header menu items.
 
         Contains root categories.
         Result can be tuned HEADER_LINKS settings option.
diff --git a/shopelectro/management/commands/images.py b/shopelectro/management/commands/images.py
--- a/shopelectro/management/commands/images.py
+++ b/shopelectro/management/commands/images.py
@@ -9,7 +9,6 @@
 from pages.models import Page
 from shopelectro.models import Product
 
-
 IMAGES_ROOT_FOLDER_NAME = os.path.join(settings.MEDIA_ROOT, 'products')
 
 
@@ -22,7 +21,7 @@ def iter_files(path: str):
         return (file_ for file_ in os.scandir(path) if file_.is_file())
 
     def get_page(product_id: int) -> Page:
-        product_ = Product.objects.filter(id=product_id).first()
+        product_ = Product.objects.filter(vendor_code=product_id).first()
         return product_.page if product_ else None
 
     def create_image_model(file_, product_id: int, slug):
@@ -42,6 +41,7 @@ def create_image_model(file_, product_id: int, slug):
             model=page,
             # autoincrement file names: '1.jpg', '2.jpg' and so on
             slug=slug,
+            # copies file with to the new path on create
             image=ImageFile(open(file_.path, mode='rb')),
             is_main=(file_short_name == 'main')
         )


Test patch:
diff --git a/shopelectro/tests/tests_views.py b/shopelectro/tests/tests_views.py
--- a/shopelectro/tests/tests_views.py
+++ b/shopelectro/tests/tests_views.py
@@ -7,6 +7,7 @@
 import json
 import re
 import typing
+import unittest
 from functools import lru_cache, partial
 from itertools import chain
 from operator import attrgetter
@@ -1010,6 +1011,7 @@ def children(self, root: str) -> typing.List[str]:
         return self.as_dict()[root]
 
 
+@unittest.expectedFailure
 @tag('fast', 'catalog')
 class Header(ViewsTestCase):
Repository: fidals/shopelectro

Issue:
Resolve stuck tests
CI fails because of stuck tests. They are working at the local and relevant code looks like they should pass
https://ci.fidals.com/fidals/shopelectro/1727/9


Discussion:

Resolving patch:
diff --git a/shopelectro/settings/drone.py b/shopelectro/settings/drone.py
--- a/shopelectro/settings/drone.py
+++ b/shopelectro/settings/drone.py
@@ -5,6 +5,15 @@
 
 DEBUG = True
 
+# Header categories menu uses cache in templates.
+# Disable cache to avoid stale menu testing.
+# See #991 for details.
+CACHES = {
+    'default': {
+        'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
+    }
+}
+
 # http://bit.ly/sorl-thumbnail-docs
 THUMBNAIL_DEBUG = True
 


Test patch:
diff --git a/shopelectro/tests/tests_views.py b/shopelectro/tests/tests_views.py
--- a/shopelectro/tests/tests_views.py
+++ b/shopelectro/tests/tests_views.py
@@ -7,7 +7,6 @@
 import json
 import re
 import typing
-import unittest
 from functools import lru_cache, partial
 from itertools import chain
 from operator import attrgetter
@@ -1011,7 +1010,6 @@ def children(self, root: str) -> typing.List[str]:
         return self.as_dict()[root]
 
 
-@unittest.expectedFailure
 @tag('fast', 'catalog')
 class Header(ViewsTestCase):
Repository: fidals/shopelectro

Issue:
Has_no_category admin filter issue
https://www.shopelectro.ru/admin/shopelectro/productpage/?all=&has_category=no

Has no category admin filter contains products that really has categories.

The first product from the screen contains category
![image](https://user-images.githubusercontent.com/3526796/66552064-5d32cf00-eb51-11e9-9e4a-2e64809f01e0.png).

[Link](https://www.shopelectro.ru/admin/shopelectro/productpage/7499/change) to the admin product

Write instruction to fill the products with missed categories with the same issue


Discussion:

Resolving patch:
diff --git a/shopelectro/admin.py b/shopelectro/admin.py
--- a/shopelectro/admin.py
+++ b/shopelectro/admin.py
@@ -6,14 +6,12 @@
 from django.urls import reverse
 from django.utils.html import format_html
 from django.utils.translation import ugettext_lazy as _
-
 from django_select2.forms import ModelSelect2Widget
 
 from ecommerce import mailer
 from ecommerce.models import Position
-from pages.models import CustomPage, FlatPage, PageTemplate
 from generic_admin import inlines, mixins, models, sites, filters
-
+from pages.models import CustomPage, FlatPage, PageTemplate
 from shopelectro import models as se_models
 from shopelectro.views.admin import TableEditor
 
@@ -24,16 +22,6 @@ class SEAdminSite(sites.SiteWithTableEditor):
     table_editor_view = TableEditor
 
 
-def prepare_has_filter_queryset(value, db_table, queryset):
-    if not value:
-        return
-
-    query = '{}__tags__isnull'.format(db_table)
-
-    # Use brackets, because `Explicit is better than implicit`.
-    return queryset.filter(**{query: value != 'yes'})
-
-
 class ProductPriceFilter(filters.PriceRange):
 
     price_lookup = 'shopelectro_product__price'
@@ -52,11 +40,10 @@ def lookups(self, request, model_admin):
         )
 
     def queryset(self, request, queryset):
-        return prepare_has_filter_queryset(
-            self.value(),
-            self.product_model._meta.db_table,
-            queryset
-        )
+        db_table = self.product_model._meta.db_table
+        return queryset.filter(
+            **{f'{db_table}__tags__isnull': self.value() != 'yes'}
+        ) if self.value() else None
 
 
 class HasCategoryFilter(admin.SimpleListFilter):
@@ -71,12 +58,13 @@ def lookups(self, request, model_admin):
             ('no', _('Has no category')),
         )
 
+    # @todo #996:30m  Test category filtering at the admin page.
+    #  Use standard django test client, without selenium.
     def queryset(self, request, queryset):
-        return prepare_has_filter_queryset(
-            self.value(),
-            self.product_model._meta.db_table,
-            queryset
-        )
+        db_table = self.product_model._meta.db_table
+        return queryset.filter(
+            **{f'{db_table}__category__isnull': self.value() != 'yes'}
+        ) if self.value() else None
 
 
 class TagInline(admin.StackedInline):


Test patch: