We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN 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

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `shopelectro/admin.py`
Content:
```
1 from django.conf import settings
2 from django.contrib import admin
3 from django.contrib.admin.widgets import FilteredSelectMultiple
4 from django.contrib.redirects.models import Redirect
5 from django.db import models as django_models
6 from django.urls import reverse
7 from django.utils.html import format_html
8 from django.utils.translation import ugettext_lazy as _
9 
10 from django_select2.forms import ModelSelect2Widget
11 
12 from ecommerce import mailer
13 from ecommerce.models import Position
14 from pages.models import CustomPage, FlatPage, PageTemplate
15 from generic_admin import inlines, mixins, models, sites, filters
16 
17 from shopelectro import models as se_models
18 from shopelectro.views.admin import TableEditor
19 
20 
21 class SEAdminSite(sites.SiteWithTableEditor):
22 
23     site_header = 'Shopelectro administration'
24     table_editor_view = TableEditor
25 
26 
27 def prepare_has_filter_queryset(value, db_table, queryset):
28     if not value:
29         return
30 
31     query = '{}__tags__isnull'.format(db_table)
32 
33     # Use brackets, because `Explicit is better than implicit`.
34     return queryset.filter(**{query: value != 'yes'})
35 
36 
37 class ProductPriceFilter(filters.PriceRange):
38 
39     price_lookup = 'shopelectro_product__price'
40 
41 
42 class HasTagsFilter(admin.SimpleListFilter):
43 
44     product_model = se_models.Product
45     title = _('has tags')
46     parameter_name = 'has_tags'
47 
48     def lookups(self, request, model_admin):
49         return (
50             ('yes', _('Has tags')),
51             ('no', _('Has no tags')),
52         )
53 
54     def queryset(self, request, queryset):
55         return prepare_has_filter_queryset(
56             self.value(),
57             self.product_model._meta.db_table,
58             queryset
59         )
60 
61 
62 class HasCategoryFilter(admin.SimpleListFilter):
63 
64     product_model = se_models.Product
65     title = _('has category')
66     parameter_name = 'has_category'
67 
68     def lookups(self, request, model_admin):
69         return (
70             ('yes', _('Has category')),
71             ('no', _('Has no category')),
72         )
73 
74     def queryset(self, request, queryset):
75         return prepare_has_filter_queryset(
76             self.value(),
77             self.product_model._meta.db_table,
78             queryset
79         )
80 
81 
82 class TagInline(admin.StackedInline):
83 
84     model = se_models.Tag
85     extra = 0
86 
87 
88 class CategoryInline(inlines.CategoryInline):
89 
90     model = se_models.Category
91 
92     fieldsets = ((None, {
93         'classes': ('primary-chars', ),
94         'fields': (
95             ('name', 'id'),
96             ('parent', 'correct_parent_id'),
97         )
98     }),)
99 
100 
101 class ProductInline(inlines.ProductInline):
102 
103     model = se_models.Product
104 
105     formfield_overrides = {
106         django_models.ManyToManyField: {
107             'widget': FilteredSelectMultiple(verbose_name='Tags', is_stacked=False)
108         },
109     }
110 
111     fieldsets = ((None, {
112         'classes': ('primary-chars', ),
113         'fields': (
114             ('name', 'id', 'vendor_code'),
115             ('category', 'correct_category_id'),
116             ('price', 'in_stock', 'in_pack'),
117             'is_popular',
118             ('purchase_price', 'wholesale_small'),
119             ('wholesale_medium', 'wholesale_large'),
120             'tags',
121         )
122     }),)
123 
124     def formfield_for_dbfield(self, db_field, **kwargs):
125         if db_field.name == 'category':
126             kwargs['widget'] = ModelSelect2Widget(
127                 model=se_models.Category,
128                 search_fields=[
129                     'name__icontains',
130                     'pk__startswith',
131                 ],
132             )
133         return super().formfield_for_dbfield(
134             db_field,
135             **kwargs,
136         )
137 
138 
139 class PositionInline(admin.StackedInline):
140 
141     model = Position
142 
143 
144 class CategoryPageAdmin(models.CategoryPageAdmin):
145 
146     add = True
147     delete = False
148     inlines = [CategoryInline, inlines.ImageInline]
149 
150     def get_queryset(self, request):
151         return (
152             super(CategoryPageAdmin, self)
153             .get_queryset(request)
154             .select_related('shopelectro_category')
155         )
156 
157 
158 class ProductPageAdmin(models.ProductPageAdmin):
159 
160     add = False
161     delete = False
162     category_page_model = se_models.CategoryPage
163     list_filter = [
164         *models.ProductPageAdmin.list_filter,
165         ProductPriceFilter,
166         HasTagsFilter,
167         HasCategoryFilter,
168     ]
169     list_display = ['model_id', 'name', 'custom_parent', 'price', 'links', 'is_active']
170     inlines = [ProductInline, inlines.ImageInline]
171     search_fields = [
172         'shopelectro_product__vendor_code', 'name', 'slug',
173     ]
174 
175     def model_id(self, obj):
176         return obj.model.vendor_code
177 
178     model_id.short_description = _('Vendor code')
179     model_id.admin_order_field = 'shopelectro_product__vendor_code'
180 
181     def price(self, obj):
182         return obj.model.price
183 
184     price.short_description = _('Price')
185     price.admin_order_field = '_product_price'
186 
187     def get_queryset(self, request):
188         qs = super().get_queryset(request)
189         return (
190             self.add_reference_to_field_on_related_model(qs, _product_price='price')
191             .select_related('shopelectro_product')
192         )
193 
194 
195 class ProductFeedbackPageAdmin(admin.ModelAdmin):
196 
197     add = False
198     delete = False
199     list_filter = ['rating']
200     list_display = ['rating', 'name', 'dignities', 'limitations', 'general', 'links']
201 
202     def links(self, obj):
203         return format_html(
204             '''
205             <a href="{url}" class="field-link" title="Посмотреть на сайте" target="_blank">
206               <i class="fa fa-link" aria-hidden="true"></i>
207             </a>
208             '''.format(url=obj.product.url))
209 
210     links.short_description = _('Link')
211 
212     def get_queryset(self, request):
213         return (
214             super(ProductFeedbackPageAdmin, self)
215             .get_queryset(request)
216             .select_related('product')
217         )
218 
219 
220 class TagGroupAdmin(admin.ModelAdmin):
221 
222     list_display = ['id', 'name', 'position', 'count_tags']
223     list_display_links = ['name']
224 
225     inlines = [TagInline]
226 
227     def get_queryset(self, request):
228         return super(TagGroupAdmin, self).get_queryset(request).prefetch_related('tags')
229 
230     def count_tags(self, obj):
231         return obj.tags.count()
232 
233 
234 class TagAdmin(admin.ModelAdmin):
235 
236     search_fields = ['id', 'name']
237     list_display = ['id', 'name', 'position', 'custom_group']
238     list_display_links = ['name']
239 
240     def get_queryset(self, request):
241         return super(TagAdmin, self).get_queryset(request).prefetch_related('group')
242 
243     def custom_group(self, obj):
244         group = obj.group
245 
246         return format_html(
247             '<a href="{url}">{group}</a>',
248             group=group,
249             url=reverse('admin:shopelectro_taggroup_change', args=(group.id, ))
250         )
251 
252     custom_group.admin_order_field = 'group'
253     custom_group.short_description = _('Group')
254 
255 
256 def send_order_emails(admin_model, request, order_qs):
257     for order in order_qs:
258         context = {'shop': settings.SHOP}
259         if order.email:
260             mailer.send_order(
261                 subject=settings.EMAIL_SUBJECTS['order'],
262                 order=order,
263                 extra_context=context,
264             )
265         else:
266             mailer.send_order(
267                 subject=settings.EMAIL_SUBJECTS['one_click'],
268                 order=order,
269                 to_customer=False,
270                 extra_context=context,
271             )
272 
273 
274 send_order_emails.short_description = _('Sends email notifications about placed orders')
275 
276 
277 class OrderAdmin(mixins.PermissionsControl):
278 
279     add = False
280     inlines = [PositionInline]
281     list_display = ['id_', 'name', 'email', 'phone', 'total_price', 'payment_type', 'paid']
282     search_fields = ['name', 'email', 'phone']
283     list_display_links = ['name']
284     actions = [send_order_emails]
285 
286     def id_(self, obj):
287         return obj.fake_order_number
288 
289 
290 class MatrixBlockAdmin(mixins.PermissionsControl):
291 
292     list_display = ['category', 'block_size']
293 
294 
295 se_admin = SEAdminSite(name='se_admin')
296 se_admin.register(CustomPage, models.CustomPageAdmin)
297 se_admin.register(FlatPage, models.FlatPageAdmin)
298 se_admin.register(PageTemplate, models.CustomPageTemplateAdmin)
299 
300 se_admin.register(se_models.CategoryPage, CategoryPageAdmin)
301 se_admin.register(se_models.Order, OrderAdmin)
302 se_admin.register(se_models.ProductPage, ProductPageAdmin)
303 se_admin.register(se_models.ProductFeedback, ProductFeedbackPageAdmin)
304 se_admin.register(se_models.TagGroup, TagGroupAdmin)
305 se_admin.register(se_models.Tag, TagAdmin)
306 se_admin.register(se_models.MatrixBlock, MatrixBlockAdmin)
307 
308 se_admin.register(Redirect)
309
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

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 @@
     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 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 @@
             ('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):

{"golden_diff": "diff --git a/shopelectro/admin.py b/shopelectro/admin.py\n--- a/shopelectro/admin.py\n+++ b/shopelectro/admin.py\n@@ -6,14 +6,12 @@\n from django.urls import reverse\n from django.utils.html import format_html\n from django.utils.translation import ugettext_lazy as _\n-\n from django_select2.forms import ModelSelect2Widget\n \n from ecommerce import mailer\n from ecommerce.models import Position\n-from pages.models import CustomPage, FlatPage, PageTemplate\n from generic_admin import inlines, mixins, models, sites, filters\n-\n+from pages.models import CustomPage, FlatPage, PageTemplate\n from shopelectro import models as se_models\n from shopelectro.views.admin import TableEditor\n \n@@ -24,16 +22,6 @@\n     table_editor_view = TableEditor\n \n \n-def prepare_has_filter_queryset(value, db_table, queryset):\n-    if not value:\n-        return\n-\n-    query = '{}__tags__isnull'.format(db_table)\n-\n-    # Use brackets, because `Explicit is better than implicit`.\n-    return queryset.filter(**{query: value != 'yes'})\n-\n-\n class ProductPriceFilter(filters.PriceRange):\n \n     price_lookup = 'shopelectro_product__price'\n@@ -52,11 +40,10 @@\n         )\n \n     def queryset(self, request, queryset):\n-        return prepare_has_filter_queryset(\n-            self.value(),\n-            self.product_model._meta.db_table,\n-            queryset\n-        )\n+        db_table = self.product_model._meta.db_table\n+        return queryset.filter(\n+            **{f'{db_table}__tags__isnull': self.value() != 'yes'}\n+        ) if self.value() else None\n \n \n class HasCategoryFilter(admin.SimpleListFilter):\n@@ -71,12 +58,13 @@\n             ('no', _('Has no category')),\n         )\n \n+    # @todo #996:30m  Test category filtering at the admin page.\n+    #  Use standard django test client, without selenium.\n     def queryset(self, request, queryset):\n-        return prepare_has_filter_queryset(\n-            self.value(),\n-            self.product_model._meta.db_table,\n-            queryset\n-        )\n+        db_table = self.product_model._meta.db_table\n+        return queryset.filter(\n+            **{f'{db_table}__category__isnull': self.value() != 'yes'}\n+        ) if self.value() else None\n \n \n class TagInline(admin.StackedInline):\n", "issue": "Has_no_category admin filter issue\nhttps://www.shopelectro.ru/admin/shopelectro/productpage/?all=&has_category=no\r\n\r\nHas no category admin filter contains products that really has categories.\r\n\r\nThe first product from the screen contains category\r\n![image](https://user-images.githubusercontent.com/3526796/66552064-5d32cf00-eb51-11e9-9e4a-2e64809f01e0.png).\r\n\r\n[Link](https://www.shopelectro.ru/admin/shopelectro/productpage/7499/change) to the admin product\r\n\r\nWrite instruction to fill the products with missed categories with the same issue\n", "before_files": [{"content": "from django.conf import settings\nfrom django.contrib import admin\nfrom django.contrib.admin.widgets import FilteredSelectMultiple\nfrom django.contrib.redirects.models import Redirect\nfrom django.db import models as django_models\nfrom django.urls import reverse\nfrom django.utils.html import format_html\nfrom django.utils.translation import ugettext_lazy as _\n\nfrom django_select2.forms import ModelSelect2Widget\n\nfrom ecommerce import mailer\nfrom ecommerce.models import Position\nfrom pages.models import CustomPage, FlatPage, PageTemplate\nfrom generic_admin import inlines, mixins, models, sites, filters\n\nfrom shopelectro import models as se_models\nfrom shopelectro.views.admin import TableEditor\n\n\nclass SEAdminSite(sites.SiteWithTableEditor):\n\n    site_header = 'Shopelectro administration'\n    table_editor_view = TableEditor\n\n\ndef prepare_has_filter_queryset(value, db_table, queryset):\n    if not value:\n        return\n\n    query = '{}__tags__isnull'.format(db_table)\n\n    # Use brackets, because `Explicit is better than implicit`.\n    return queryset.filter(**{query: value != 'yes'})\n\n\nclass ProductPriceFilter(filters.PriceRange):\n\n    price_lookup = 'shopelectro_product__price'\n\n\nclass HasTagsFilter(admin.SimpleListFilter):\n\n    product_model = se_models.Product\n    title = _('has tags')\n    parameter_name = 'has_tags'\n\n    def lookups(self, request, model_admin):\n        return (\n            ('yes', _('Has tags')),\n            ('no', _('Has no tags')),\n        )\n\n    def queryset(self, request, queryset):\n        return prepare_has_filter_queryset(\n            self.value(),\n            self.product_model._meta.db_table,\n            queryset\n        )\n\n\nclass HasCategoryFilter(admin.SimpleListFilter):\n\n    product_model = se_models.Product\n    title = _('has category')\n    parameter_name = 'has_category'\n\n    def lookups(self, request, model_admin):\n        return (\n            ('yes', _('Has category')),\n            ('no', _('Has no category')),\n        )\n\n    def queryset(self, request, queryset):\n        return prepare_has_filter_queryset(\n            self.value(),\n            self.product_model._meta.db_table,\n            queryset\n        )\n\n\nclass TagInline(admin.StackedInline):\n\n    model = se_models.Tag\n    extra = 0\n\n\nclass CategoryInline(inlines.CategoryInline):\n\n    model = se_models.Category\n\n    fieldsets = ((None, {\n        'classes': ('primary-chars', ),\n        'fields': (\n            ('name', 'id'),\n            ('parent', 'correct_parent_id'),\n        )\n    }),)\n\n\nclass ProductInline(inlines.ProductInline):\n\n    model = se_models.Product\n\n    formfield_overrides = {\n        django_models.ManyToManyField: {\n            'widget': FilteredSelectMultiple(verbose_name='Tags', is_stacked=False)\n        },\n    }\n\n    fieldsets = ((None, {\n        'classes': ('primary-chars', ),\n        'fields': (\n            ('name', 'id', 'vendor_code'),\n            ('category', 'correct_category_id'),\n            ('price', 'in_stock', 'in_pack'),\n            'is_popular',\n            ('purchase_price', 'wholesale_small'),\n            ('wholesale_medium', 'wholesale_large'),\n            'tags',\n        )\n    }),)\n\n    def formfield_for_dbfield(self, db_field, **kwargs):\n        if db_field.name == 'category':\n            kwargs['widget'] = ModelSelect2Widget(\n                model=se_models.Category,\n                search_fields=[\n                    'name__icontains',\n                    'pk__startswith',\n                ],\n            )\n        return super().formfield_for_dbfield(\n            db_field,\n            **kwargs,\n        )\n\n\nclass PositionInline(admin.StackedInline):\n\n    model = Position\n\n\nclass CategoryPageAdmin(models.CategoryPageAdmin):\n\n    add = True\n    delete = False\n    inlines = [CategoryInline, inlines.ImageInline]\n\n    def get_queryset(self, request):\n        return (\n            super(CategoryPageAdmin, self)\n            .get_queryset(request)\n            .select_related('shopelectro_category')\n        )\n\n\nclass ProductPageAdmin(models.ProductPageAdmin):\n\n    add = False\n    delete = False\n    category_page_model = se_models.CategoryPage\n    list_filter = [\n        *models.ProductPageAdmin.list_filter,\n        ProductPriceFilter,\n        HasTagsFilter,\n        HasCategoryFilter,\n    ]\n    list_display = ['model_id', 'name', 'custom_parent', 'price', 'links', 'is_active']\n    inlines = [ProductInline, inlines.ImageInline]\n    search_fields = [\n        'shopelectro_product__vendor_code', 'name', 'slug',\n    ]\n\n    def model_id(self, obj):\n        return obj.model.vendor_code\n\n    model_id.short_description = _('Vendor code')\n    model_id.admin_order_field = 'shopelectro_product__vendor_code'\n\n    def price(self, obj):\n        return obj.model.price\n\n    price.short_description = _('Price')\n    price.admin_order_field = '_product_price'\n\n    def get_queryset(self, request):\n        qs = super().get_queryset(request)\n        return (\n            self.add_reference_to_field_on_related_model(qs, _product_price='price')\n            .select_related('shopelectro_product')\n        )\n\n\nclass ProductFeedbackPageAdmin(admin.ModelAdmin):\n\n    add = False\n    delete = False\n    list_filter = ['rating']\n    list_display = ['rating', 'name', 'dignities', 'limitations', 'general', 'links']\n\n    def links(self, obj):\n        return format_html(\n            '''\n            <a href=\"{url}\" class=\"field-link\" title=\"\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043d\u0430 \u0441\u0430\u0439\u0442\u0435\" target=\"_blank\">\n              <i class=\"fa fa-link\" aria-hidden=\"true\"></i>\n            </a>\n            '''.format(url=obj.product.url))\n\n    links.short_description = _('Link')\n\n    def get_queryset(self, request):\n        return (\n            super(ProductFeedbackPageAdmin, self)\n            .get_queryset(request)\n            .select_related('product')\n        )\n\n\nclass TagGroupAdmin(admin.ModelAdmin):\n\n    list_display = ['id', 'name', 'position', 'count_tags']\n    list_display_links = ['name']\n\n    inlines = [TagInline]\n\n    def get_queryset(self, request):\n        return super(TagGroupAdmin, self).get_queryset(request).prefetch_related('tags')\n\n    def count_tags(self, obj):\n        return obj.tags.count()\n\n\nclass TagAdmin(admin.ModelAdmin):\n\n    search_fields = ['id', 'name']\n    list_display = ['id', 'name', 'position', 'custom_group']\n    list_display_links = ['name']\n\n    def get_queryset(self, request):\n        return super(TagAdmin, self).get_queryset(request).prefetch_related('group')\n\n    def custom_group(self, obj):\n        group = obj.group\n\n        return format_html(\n            '<a href=\"{url}\">{group}</a>',\n            group=group,\n            url=reverse('admin:shopelectro_taggroup_change', args=(group.id, ))\n        )\n\n    custom_group.admin_order_field = 'group'\n    custom_group.short_description = _('Group')\n\n\ndef send_order_emails(admin_model, request, order_qs):\n    for order in order_qs:\n        context = {'shop': settings.SHOP}\n        if order.email:\n            mailer.send_order(\n                subject=settings.EMAIL_SUBJECTS['order'],\n                order=order,\n                extra_context=context,\n            )\n        else:\n            mailer.send_order(\n                subject=settings.EMAIL_SUBJECTS['one_click'],\n                order=order,\n                to_customer=False,\n                extra_context=context,\n            )\n\n\nsend_order_emails.short_description = _('Sends email notifications about placed orders')\n\n\nclass OrderAdmin(mixins.PermissionsControl):\n\n    add = False\n    inlines = [PositionInline]\n    list_display = ['id_', 'name', 'email', 'phone', 'total_price', 'payment_type', 'paid']\n    search_fields = ['name', 'email', 'phone']\n    list_display_links = ['name']\n    actions = [send_order_emails]\n\n    def id_(self, obj):\n        return obj.fake_order_number\n\n\nclass MatrixBlockAdmin(mixins.PermissionsControl):\n\n    list_display = ['category', 'block_size']\n\n\nse_admin = SEAdminSite(name='se_admin')\nse_admin.register(CustomPage, models.CustomPageAdmin)\nse_admin.register(FlatPage, models.FlatPageAdmin)\nse_admin.register(PageTemplate, models.CustomPageTemplateAdmin)\n\nse_admin.register(se_models.CategoryPage, CategoryPageAdmin)\nse_admin.register(se_models.Order, OrderAdmin)\nse_admin.register(se_models.ProductPage, ProductPageAdmin)\nse_admin.register(se_models.ProductFeedback, ProductFeedbackPageAdmin)\nse_admin.register(se_models.TagGroup, TagGroupAdmin)\nse_admin.register(se_models.Tag, TagAdmin)\nse_admin.register(se_models.MatrixBlock, MatrixBlockAdmin)\n\nse_admin.register(Redirect)\n", "path": "shopelectro/admin.py"}], "after_files": [{"content": "from django.conf import settings\nfrom django.contrib import admin\nfrom django.contrib.admin.widgets import FilteredSelectMultiple\nfrom django.contrib.redirects.models import Redirect\nfrom django.db import models as django_models\nfrom django.urls import reverse\nfrom django.utils.html import format_html\nfrom django.utils.translation import ugettext_lazy as _\nfrom django_select2.forms import ModelSelect2Widget\n\nfrom ecommerce import mailer\nfrom ecommerce.models import Position\nfrom generic_admin import inlines, mixins, models, sites, filters\nfrom pages.models import CustomPage, FlatPage, PageTemplate\nfrom shopelectro import models as se_models\nfrom shopelectro.views.admin import TableEditor\n\n\nclass SEAdminSite(sites.SiteWithTableEditor):\n\n    site_header = 'Shopelectro administration'\n    table_editor_view = TableEditor\n\n\nclass ProductPriceFilter(filters.PriceRange):\n\n    price_lookup = 'shopelectro_product__price'\n\n\nclass HasTagsFilter(admin.SimpleListFilter):\n\n    product_model = se_models.Product\n    title = _('has tags')\n    parameter_name = 'has_tags'\n\n    def lookups(self, request, model_admin):\n        return (\n            ('yes', _('Has tags')),\n            ('no', _('Has no tags')),\n        )\n\n    def queryset(self, request, queryset):\n        db_table = self.product_model._meta.db_table\n        return queryset.filter(\n            **{f'{db_table}__tags__isnull': self.value() != 'yes'}\n        ) if self.value() else None\n\n\nclass HasCategoryFilter(admin.SimpleListFilter):\n\n    product_model = se_models.Product\n    title = _('has category')\n    parameter_name = 'has_category'\n\n    def lookups(self, request, model_admin):\n        return (\n            ('yes', _('Has category')),\n            ('no', _('Has no category')),\n        )\n\n    # @todo #996:30m  Test category filtering at the admin page.\n    #  Use standard django test client, without selenium.\n    def queryset(self, request, queryset):\n        db_table = self.product_model._meta.db_table\n        return queryset.filter(\n            **{f'{db_table}__category__isnull': self.value() != 'yes'}\n        ) if self.value() else None\n\n\nclass TagInline(admin.StackedInline):\n\n    model = se_models.Tag\n    extra = 0\n\n\nclass CategoryInline(inlines.CategoryInline):\n\n    model = se_models.Category\n\n    fieldsets = ((None, {\n        'classes': ('primary-chars', ),\n        'fields': (\n            ('name', 'id'),\n            ('parent', 'correct_parent_id'),\n        )\n    }),)\n\n\nclass ProductInline(inlines.ProductInline):\n\n    model = se_models.Product\n\n    formfield_overrides = {\n        django_models.ManyToManyField: {\n            'widget': FilteredSelectMultiple(verbose_name='Tags', is_stacked=False)\n        },\n    }\n\n    fieldsets = ((None, {\n        'classes': ('primary-chars', ),\n        'fields': (\n            ('name', 'id', 'vendor_code'),\n            ('category', 'correct_category_id'),\n            ('price', 'in_stock', 'in_pack'),\n            'is_popular',\n            ('purchase_price', 'wholesale_small'),\n            ('wholesale_medium', 'wholesale_large'),\n            'tags',\n        )\n    }),)\n\n    def formfield_for_dbfield(self, db_field, **kwargs):\n        if db_field.name == 'category':\n            kwargs['widget'] = ModelSelect2Widget(\n                model=se_models.Category,\n                search_fields=[\n                    'name__icontains',\n                    'pk__startswith',\n                ],\n            )\n        return super().formfield_for_dbfield(\n            db_field,\n            **kwargs,\n        )\n\n\nclass PositionInline(admin.StackedInline):\n\n    model = Position\n\n\nclass CategoryPageAdmin(models.CategoryPageAdmin):\n\n    add = True\n    delete = False\n    inlines = [CategoryInline, inlines.ImageInline]\n\n    def get_queryset(self, request):\n        return (\n            super(CategoryPageAdmin, self)\n            .get_queryset(request)\n            .select_related('shopelectro_category')\n        )\n\n\nclass ProductPageAdmin(models.ProductPageAdmin):\n\n    add = False\n    delete = False\n    category_page_model = se_models.CategoryPage\n    list_filter = [\n        *models.ProductPageAdmin.list_filter,\n        ProductPriceFilter,\n        HasTagsFilter,\n        HasCategoryFilter,\n    ]\n    list_display = ['model_id', 'name', 'custom_parent', 'price', 'links', 'is_active']\n    inlines = [ProductInline, inlines.ImageInline]\n    search_fields = [\n        'shopelectro_product__vendor_code', 'name', 'slug',\n    ]\n\n    def model_id(self, obj):\n        return obj.model.vendor_code\n\n    model_id.short_description = _('Vendor code')\n    model_id.admin_order_field = 'shopelectro_product__vendor_code'\n\n    def price(self, obj):\n        return obj.model.price\n\n    price.short_description = _('Price')\n    price.admin_order_field = '_product_price'\n\n    def get_queryset(self, request):\n        qs = super().get_queryset(request)\n        return (\n            self.add_reference_to_field_on_related_model(qs, _product_price='price')\n            .select_related('shopelectro_product')\n        )\n\n\nclass ProductFeedbackPageAdmin(admin.ModelAdmin):\n\n    add = False\n    delete = False\n    list_filter = ['rating']\n    list_display = ['rating', 'name', 'dignities', 'limitations', 'general', 'links']\n\n    def links(self, obj):\n        return format_html(\n            '''\n            <a href=\"{url}\" class=\"field-link\" title=\"\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043d\u0430 \u0441\u0430\u0439\u0442\u0435\" target=\"_blank\">\n              <i class=\"fa fa-link\" aria-hidden=\"true\"></i>\n            </a>\n            '''.format(url=obj.product.url))\n\n    links.short_description = _('Link')\n\n    def get_queryset(self, request):\n        return (\n            super(ProductFeedbackPageAdmin, self)\n            .get_queryset(request)\n            .select_related('product')\n        )\n\n\nclass TagGroupAdmin(admin.ModelAdmin):\n\n    list_display = ['id', 'name', 'position', 'count_tags']\n    list_display_links = ['name']\n\n    inlines = [TagInline]\n\n    def get_queryset(self, request):\n        return super(TagGroupAdmin, self).get_queryset(request).prefetch_related('tags')\n\n    def count_tags(self, obj):\n        return obj.tags.count()\n\n\nclass TagAdmin(admin.ModelAdmin):\n\n    search_fields = ['id', 'name']\n    list_display = ['id', 'name', 'position', 'custom_group']\n    list_display_links = ['name']\n\n    def get_queryset(self, request):\n        return super(TagAdmin, self).get_queryset(request).prefetch_related('group')\n\n    def custom_group(self, obj):\n        group = obj.group\n\n        return format_html(\n            '<a href=\"{url}\">{group}</a>',\n            group=group,\n            url=reverse('admin:shopelectro_taggroup_change', args=(group.id, ))\n        )\n\n    custom_group.admin_order_field = 'group'\n    custom_group.short_description = _('Group')\n\n\ndef send_order_emails(admin_model, request, order_qs):\n    for order in order_qs:\n        context = {'shop': settings.SHOP}\n        if order.email:\n            mailer.send_order(\n                subject=settings.EMAIL_SUBJECTS['order'],\n                order=order,\n                extra_context=context,\n            )\n        else:\n            mailer.send_order(\n                subject=settings.EMAIL_SUBJECTS['one_click'],\n                order=order,\n                to_customer=False,\n                extra_context=context,\n            )\n\n\nsend_order_emails.short_description = _('Sends email notifications about placed orders')\n\n\nclass OrderAdmin(mixins.PermissionsControl):\n\n    add = False\n    inlines = [PositionInline]\n    list_display = ['id_', 'name', 'email', 'phone', 'total_price', 'payment_type', 'paid']\n    search_fields = ['name', 'email', 'phone']\n    list_display_links = ['name']\n    actions = [send_order_emails]\n\n    def id_(self, obj):\n        return obj.fake_order_number\n\n\nclass MatrixBlockAdmin(mixins.PermissionsControl):\n\n    list_display = ['category', 'block_size']\n\n\nse_admin = SEAdminSite(name='se_admin')\nse_admin.register(CustomPage, models.CustomPageAdmin)\nse_admin.register(FlatPage, models.FlatPageAdmin)\nse_admin.register(PageTemplate, models.CustomPageTemplateAdmin)\n\nse_admin.register(se_models.CategoryPage, CategoryPageAdmin)\nse_admin.register(se_models.Order, OrderAdmin)\nse_admin.register(se_models.ProductPage, ProductPageAdmin)\nse_admin.register(se_models.ProductFeedback, ProductFeedbackPageAdmin)\nse_admin.register(se_models.TagGroup, TagGroupAdmin)\nse_admin.register(se_models.Tag, TagAdmin)\nse_admin.register(se_models.MatrixBlock, MatrixBlockAdmin)\n\nse_admin.register(Redirect)\n", "path": "shopelectro/admin.py"}]}