add products app

remotes/origin/HEAD
Max Yakovenko 8 years ago
parent 17b21e6dbe
commit 8a94098546
  1. 0
      products/__init__.py
  2. 195
      products/admin.py
  3. 5
      products/apps.py
  4. 10
      products/context_processors.py
  5. 533
      products/fixtures/products.json
  6. 56
      products/forms.py
  7. 137
      products/models.py
  8. 20
      products/search_indexes.py
  9. 3
      products/tests.py
  10. 39
      products/urls.py
  11. 39
      products/utils.py
  12. 116
      products/views.py

@ -0,0 +1,195 @@
# from django.contrib import admin
# from import_export import resources, fields, widgets
# from import_export.admin import ImportExportModelAdmin
# from .models import *
#
# class CustomModelResource(resources.ModelResource):
# def before_import_row(self, row, **kwargs):
# """
# Override to add additional logic. Does nothing by default.
# """
# try:
# row['attributes'] = eval(row['attributes'])
# except:
# try:
# row['discount_policy'] = eval(row['discount_policy'])
# except:
# pass
#
# class CustomManyToManyWidget(widgets.ManyToManyWidget):
# def clean(self, value, row=None, *args, **kwargs):
# t1 = super(CustomManyToManyWidget, self).clean(value)
# return self.model.objects.get(name=t1) if t1 else None
#
#
# # class CustomForeignKeyWidget(widgets.ForeignKeyWidget):
# # def clean(self, value, row=None, *args, **kwargs):
# # return self.model.objects.get_or_create(name=value)[0]
#
# # class ProductImageInline(admin.TabularInline):
# # model = ProductImage
# # extra = 0
#
# # class ProductAttributeInline(admin.TabularInline):
# # model = ProductAttribute
# # extra = 1
# # verbose_name_plural = 'ProductAttribute'
# # suit_classes = 'suit-tab suit-tab-PA'
# #
# class AttributeChoiceValueInline(admin.TabularInline):
# model = ProductAttributeChoiceValue
# # prepopulated_fields = {'slug': ('name',)}
# extra = 1
# verbose_name_plural = 'AttributeChoiceValue'
# suit_classes = 'suit-tab suit-tab-ACV'
# #
# # class OfferInline(admin.TabularInline):
# # model = Offer
# # extra = 1
# # verbose_name_plural = 'Offers'
# # suit_classes = 'suit-tab suit-tab-offers'
#
# class ProductCategoryAdmin(admin.ModelAdmin):
# list_display = [field.name for field in ProductCategory._meta.fields]
#
# class Meta:
# model = ProductCategory
#
# # class AttributeChoiceValueAdmin(admin.ModelAdmin):
# # list_display = [field.name for field in ProductCategory._meta.fields]
# #
# # class Meta:
# # model = AttributeChoiceValue
# #
# # admin.site.register(AttributeChoiceValue, AttributeChoiceValueAdmin)
#
# class ProductAttributeAdmin(admin.ModelAdmin):
# list_display = [field.name for field in ProductAttribute._meta.fields]
# inlines = [AttributeChoiceValueInline]
# # prepopulated_fields = {'slug': ('name',)}
#
# suit_form_tabs = (('general', 'General'),
# ('ACV', 'AttributeValues'),)
#
# class Meta:
# model = ProductAttribute
#
# admin.site.register(ProductAttribute, ProductAttributeAdmin)
#
# @admin.register(Manufacturer)
# class ProducerAdmin(admin.ModelAdmin):
# list_display = [field.name for field in Manufacturer._meta.fields]
#
#
# class ProductResource(CustomModelResource):
# # id = fields.Field(default=generate_Jid(prefix='J'),
# # readonly=True,
# # widget=widgets.CharWidget(),
# # )
#
# name = fields.Field(column_name='name', attribute='name',
# default=None,
# widget=widgets.CharWidget(),
# )
# # price = fields.Field(column_name='price', attribute='price',
# # default=0,
# # widget=widgets.DecimalWidget(),
# # )
# description = fields.Field(column_name='description', attribute='description',
# default=None,
# widget=widgets.CharWidget(),
# )
#
# # producer = fields.Field(column_name='producer', attribute='producer',
# # default=None,
# # widget=widgets.CharWidget(),
# # )
#
# category = fields.Field(column_name='category', attribute='category',
# default=None,
# widget=widgets.ForeignKeyWidget(ProductCategory, field='name'),
# )
# producer = fields.Field(column_name='producer', attribute='producer',
# default=None,
# widget=widgets.ForeignKeyWidget(Manufacturer, field='name'),
# )
# attributes = fields.Field(column_name='attributes', attribute='attributes',
# default=None,
# widget=CustomManyToManyWidget(ProductAttribute, field="name"),
# )
# is_active = fields.Field(column_name='is_active', attribute='is_active',
# default=1,
# widget=widgets.BooleanWidget())
#
# discount_policy = fields.Field(column_name='discount_policy', attribute='discount_policy',
# default={},
# widget=widgets.CharWidget())
#
# # delete = fields.Field(column_name='delete', attribute='delete',
# # default=0,
# # widget=widgets.BooleanWidget())
#
# # def for_delete(self, row, instance):
# # return self.fields['delete'].clean(row)
#
# class Meta:
# model = Product
# fields = ('id', 'name', 'description', 'producer', 'category', 'is_active', 'attributes', 'discount_policy')
# export_order = ('id', 'name', 'producer', 'is_active', 'category', 'attributes', 'description', 'discount_policy')
# # import_id_fields = ('name',)
#
# def dehydrate_str_choices(self, obj):
# if obj.id:
# return obj.str_choices()
#
# @admin.register(Product)
# class ProductAdmin(ImportExportModelAdmin):
# list_display = ['id', 'name', 'category', 'manufacturer','status']
# # inlines = [OfferInline]
# list_filter = ['status', 'create_at', 'updated_at', 'category']
# search_fields = ['name', 'id']
#
# resource_class = ProductResource
#
# # class OfferResource(CustomModelResource):
# # name = fields.Field(column_name='name', attribute='name',
# # default=None,
# # widget=widgets.CharWidget(),
# # )
# #
# # price = fields.Field(column_name='price', attribute='price',
# # default=0,
# # widget=widgets.DecimalWidget(),
# # )
# #
# # products = fields.Field(column_name='products', attribute='products',
# # widget=widgets.ForeignKeyWidget(Product, field='name'),
# # )
# #
# # is_active = fields.Field(column_name='is_active', attribute='is_active',
# # default=1,
# # widget=widgets.BooleanWidget())
# #
# # attributes = fields.Field(column_name='attributes', attribute='attributes',
# # default={},
# # widget=widgets.CharWidget())
# #
# # class Meta:
# # model = Offer
# # fields = ('name', 'products', 'price', 'is_active', 'attributes')
# # export_order = ('name', 'products', 'attributes', 'is_active', 'price')
# # import_id_fields = ('name',)
#
# # class OfferAdmin(ImportExportModelAdmin):
# # list_display = ['id', 'name', 'products', 'price', 'is_active', 'attributes']
# # resource_class = OfferResource
# # class ProductImageAdmin(admin.ModelAdmin):
# # list_display = [field.name for field in ProductImage._meta.fields]
# #
# # class Meta:
# # model = ProductImage
#
# # admin.site.register(ProductImage, ProductImageAdmin)
# # admin.site.register(ProductCategory, ProductCategoryAdmin)
# # admin.site.register(Product, ProductAdmin)
# # admin.site.register(Offer, OfferAdmin)

@ -0,0 +1,5 @@
from django.apps import AppConfig
class ProductsConfig(AppConfig):
name = 'products'

@ -0,0 +1,10 @@
from products.forms import ProductSearchForm
def product_search_form(request):
return {'product_search_form': ProductSearchForm()}
def product_root_categories(request):
categories = {'product_root_categories': []}
return categories

@ -0,0 +1,533 @@
[
{
"model": "products.productcategory",
"pk": 1,
"fields": {
"name": "Information security",
"slug": "information-security",
"is_active": true,
"parent": null,
"lft": 1,
"rght": 6,
"tree_id": 2,
"level": 0
}
},
{
"model": "products.productcategory",
"pk": 2,
"fields": {
"name": "Document manipulation",
"slug": "document-manipulation",
"is_active": true,
"parent": null,
"lft": 1,
"rght": 4,
"tree_id": 1,
"level": 0
}
},
{
"model": "products.productcategory",
"pk": 3,
"fields": {
"name": "System software",
"slug": "system-software",
"is_active": true,
"parent": null,
"lft": 1,
"rght": 6,
"tree_id": 3,
"level": 0
}
},
{
"model": "products.productcategory",
"pk": 4,
"fields": {
"name": "Anti-virus software",
"slug": "anti-virus-software",
"is_active": true,
"parent": 1,
"lft": 2,
"rght": 3,
"tree_id": 2,
"level": 1
}
},
{
"model": "products.productcategory",
"pk": 5,
"fields": {
"name": "Data recovery",
"slug": "data-recovery",
"is_active": true,
"parent": 1,
"lft": 4,
"rght": 5,
"tree_id": 2,
"level": 1
}
},
{
"model": "products.productcategory",
"pk": 6,
"fields": {
"name": "OS",
"slug": "os",
"is_active": true,
"parent": 3,
"lft": 4,
"rght": 5,
"tree_id": 3,
"level": 1
}
},
{
"model": "products.productcategory",
"pk": 7,
"fields": {
"name": "DB",
"slug": "db",
"is_active": true,
"parent": 3,
"lft": 2,
"rght": 3,
"tree_id": 3,
"level": 1
}
},
{
"model": "products.productcategory",
"pk": 8,
"fields": {
"name": "Microsoft Office",
"slug": "microsoft-office",
"is_active": true,
"parent": 2,
"lft": 2,
"rght": 3,
"tree_id": 1,
"level": 1
}
},
{
"model": "products.productattribute",
"pk": 1,
"fields": {
"name": "License type",
"slug": "license-type"
}
},
{
"model": "products.productattribute",
"pk": 2,
"fields": {
"name": "License term",
"slug": "license-term"
}
},
{
"model": "products.productattribute",
"pk": 3,
"fields": {
"name": "Technical support",
"slug": "technical-support"
}
},
{
"model": "products.productattribute",
"pk": 4,
"fields": {
"name": "Number users",
"slug": "number-users"
}
},
{
"model": "products.productattribute",
"pk": 5,
"fields": {
"name": "Type of organization",
"slug": "type-organization"
}
},
{
"model": "products.attributechoicevalue",
"pk": 1,
"fields": {
"name": "New",
"slug": "new",
"attribute": 1
}
},
{
"model": "products.attributechoicevalue",
"pk": 2,
"fields": {
"name": "Prolongation",
"slug": "prolongation",
"attribute": 1
}
},
{
"model": "products.attributechoicevalue",
"pk": 3,
"fields": {
"name": "Migration",
"slug": "migration",
"attribute": 1
}
},
{
"model": "products.attributechoicevalue",
"pk": 4,
"fields": {
"name": "One year",
"slug": "one-year",
"attribute": 2
}
},
{
"model": "products.attributechoicevalue",
"pk": 5,
"fields": {
"name": "Two years",
"slug": "two-years",
"attribute": 2
}
},
{
"model": "products.attributechoicevalue",
"pk": 6,
"fields": {
"name": "Standard AAS",
"slug": "standard-aas",
"attribute": 3
}
},
{
"model": "products.attributechoicevalue",
"pk": 7,
"fields": {
"name": "Extended AAP",
"slug": "extended-aap",
"attribute": 3
}
},
{
"model": "products.attributechoicevalue",
"pk": 8,
"fields": {
"name": "from 1 to 49",
"slug": "1-49",
"attribute": 4
}
},
{
"model": "products.attributechoicevalue",
"pk": 9,
"fields": {
"name": "from 50 to 99",
"slug": "50-99",
"attribute": 4
}
},
{
"model": "products.attributechoicevalue",
"pk": 10,
"fields": {
"name": "from 100 to 299",
"slug": "100-299",
"attribute": 4
}
},
{
"model": "products.attributechoicevalue",
"pk": 11,
"fields": {
"name": "Version Upgrade",
"slug": "version-upgrade",
"attribute": 1
}
},
{
"model": "products.attributechoicevalue",
"pk": 12,
"fields": {
"name": "commercial",
"slug": "commercial",
"attribute": 5
}
},
{
"model": "products.attributechoicevalue",
"pk": 13,
"fields": {
"name": "educational",
"slug": "educational",
"attribute": 5
}
},
{
"model": "products.productclass",
"pk": 1,
"fields": {
"name": "Antivirus software class",
"has_variants": true,
"variant_attributes": [
2,
1,
5
]
}
},
{
"model": "products.productclass",
"pk": 2,
"fields": {
"name": "Data recovery class",
"has_variants": true,
"variant_attributes": [
1,
3
]
}
},
{
"model": "products.productclass",
"pk": 3,
"fields": {
"name": "Document manipulation class",
"has_variants": true,
"variant_attributes": []
}
},
{
"model": "products.productclass",
"pk": 4,
"fields": {
"name": "DB class",
"has_variants": true,
"variant_attributes": [
2,
4
]
}
},
{
"model": "products.productclass",
"pk": 5,
"fields": {
"name": "OS class",
"has_variants": true,
"variant_attributes": [
5
]
}
},
{
"model": "products.product",
"pk": 85,
"fields": {
"name": "Kaspersky Endpoint Security \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430 CLOUD",
"slug": "kaspersky-endpoint-security-dlya-biznesa-cloud",
"price": "1730.00",
"points": "173.00",
"description": "Kaspersky Endpoint Security Cloud \u2013 \u044d\u0442\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043f\u043e\u0442\u0440\u0435\u0431\u043d\u043e\u0441\u0442\u044f\u043c \u043c\u0430\u043b\u043e\u0433\u043e \u0431\u0438\u0437\u043d\u0435\u0441\u0430 \u0438 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u043d\u0430\u0434\u0435\u0436\u043d\u0443\u044e \u0437\u0430\u0449\u0438\u0442\u0443 \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u043e\u0432, \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 \u0438 \u0444\u0430\u0439\u043b\u043e\u0432\u044b\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432 \u0438\u0437 \u043e\u0431\u043b\u0430\u0447\u043d\u043e\u0439 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f. \u0420\u0435\u0448\u0435\u043d\u0438\u0435 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043f\u043e\u043a\u0443\u043f\u043a\u0438 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 \u0441 \u043b\u044e\u0431\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u043e\u0433\u043e \u043a \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0443.",
"short_description": "",
"producer": "Kaspersky",
"image": "products/2017/05/26/thumb_1473758588.png",
"discount": 0,
"stock": 10,
"category": 4,
"product_class": 1,
"is_active": true,
"is_hit": false,
"is_new": false,
"created": "2017-05-26",
"updated": "2017-07-07"
}
},
{
"model": "products.product",
"pk": 86,
"fields": {
"name": "ESET NOD32 Antivirus Business Edition",
"slug": "eset-nod32-antivirus-business-edition",
"price": "2400.00",
"points": "240.00",
"description": "ESET Endpoint Antivirus \u2013 \u043d\u043e\u0432\u043e\u0435 \u0441\u043b\u043e\u0432\u043e \u0432 \u043f\u0440\u043e\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u0437\u0430\u0449\u0438\u0442\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0445 \u0440\u0430\u0431\u043e\u0447\u0438\u0445 \u0441\u0442\u0430\u043d\u0446\u0438\u0439 \u043e\u0442 \u043b\u044e\u0431\u043e\u0433\u043e \u0438\u0437 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u0432\u0440\u0435\u0434\u043e\u043d\u043e\u0441\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e\u0433\u043e \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u044f. \u041f\u0440\u043e\u0434\u0443\u043a\u0442 \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043d \u043d\u0430 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439, \u0437\u0430\u043f\u0440\u0435\u0449\u0435\u043d\u043d\u044b\u0445 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u0444\u0438\u0434\u0435\u043d\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438. \u0421\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0430\u0442\u0435\u043d\u0442\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u044d\u0432\u0440\u0438\u0441\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0430\u043d\u0430\u043b\u0438\u0437\u0430 \u0438 \u043d\u043e\u0432\u0435\u0439\u0448\u0438\u0445 \u0438\u043d\u0442\u0435\u043b\u043b\u0435\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439 \u043e\u0431\u043b\u0430\u0447\u043d\u043e\u0433\u043e \u0442\u0438\u043f\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438 \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 \u0442\u0438\u043f\u044b \u0443\u0433\u0440\u043e\u0437, \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u0443\u044f \u043c\u043e\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u0440\u0435\u0430\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430 \u043f\u043e\u043f\u044b\u0442\u043a\u0438 \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u044f \u0432\u0440\u0435\u0434\u043e\u043d\u043e\u0441\u043d\u043e\u0433\u043e \u041f\u041e \u0432 \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u0443\u044e \u0441\u0435\u0442\u044c.",
"short_description": "",
"producer": "Eset",
"image": "products/2017/05/26/thumb_1381486034.jpg",
"discount": 0,
"stock": 10,
"category": 4,
"product_class": 1,
"is_active": true,
"is_hit": false,
"is_new": false,
"created": "2017-05-26",
"updated": "2017-07-07"
}
},
{
"model": "products.product",
"pk": 87,
"fields": {
"name": "Acronis Backup 12 Workstation License",
"slug": "acronis-backup-12-workstation-license",
"price": "3190.00",
"points": "319.00",
"description": "\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438\r\n\r\n\u0411\u044b\u0441\u0442\u0440\u043e\u0435 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0441\u0438\u0441\u0442\u0435\u043c, \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439, \u0444\u0430\u0439\u043b\u043e\u0432 \u0438 \u0434\u0430\u043d\u043d\u044b\u0445\r\n\r\n \u0411\u044b\u0441\u0442\u0440\u043e\u0435 \u0438 \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0435 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u043e\u0431\u0440\u0430\u0437\u0430 \u0434\u0438\u0441\u043a\u0430\r\n \u0423\u0434\u043e\u0431\u043d\u043e\u0435 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \"\u043d\u0430 \u0433\u043e\u043b\u043e\u0435 \u0436\u0435\u043b\u0435\u0437\u043e\" \u043d\u0430 \u0442\u043e\u043c \u0436\u0435 \u0438\u043b\u0438 \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0449\u0435\u043c\u0441\u044f \u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u0438, \u043b\u0438\u0431\u043e \u043d\u0430 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0439 \u043c\u0430\u0448\u0438\u043d\u0435\r\n \u0420\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0435 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u0430\u043f\u043e\u043a \u043d\u0430 \u0434\u0438\u0441\u043a\u0435 \u0438\u043b\u0438 \u0441\u0435\u0442\u0435\u0432\u044b\u0445 \u043f\u0430\u043f\u043e\u043a \u043e\u0431\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f\r\n \u0412\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432 \u0438 \u043f\u0430\u043f\u043e\u043a \u0438\u0437 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0439 \u043a\u043e\u043f\u0438\u0438 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043e\u0431\u0440\u0430\u0437\u0430\r\n \u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0445 \u043a\u043e\u043f\u0438\u0439 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u043c\u0430\u0448\u0438\u043d",
"short_description": "",
"producer": "Acronis",
"image": "products/2017/05/26/thumb_1384933473.jpg",
"discount": 0,
"stock": 10,
"category": 5,
"product_class": 2,
"is_active": true,
"is_hit": false,
"is_new": false,
"created": "2017-05-26",
"updated": "2017-07-07"
}
},
{
"model": "products.product",
"pk": 88,
"fields": {
"name": "Symantec System Recovery Desktop",
"slug": "symantec-system-recovery-desktop",
"price": "3830.00",
"points": "383.00",
"description": "Symantec System Recovery 2013 \u2013 \u044d\u0442\u043e \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0445 \u043a\u043e\u043f\u0438\u0439 \u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0447\u0438\u0445 \u0441\u0442\u0430\u043d\u0446\u0438\u0439 \u0438 \u043d\u043e\u0443\u0442\u0431\u0443\u043a\u043e\u0432 \u0432 \u0430\u0432\u0430\u0440\u0438\u0439\u043d\u044b\u0445 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f\u0445, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043c\u0438\u043d\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u044f \u0438 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u043e \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a \u0440\u0435\u0448\u0435\u043d\u0438\u044e \u0440\u0430\u0431\u043e\u0447\u0438\u0445 \u0437\u0430\u0434\u0430\u0447.\r\n\r\n\u0422\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044f Restore Anyware, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0430\u044f \u0432 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0435, \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u043c \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e \u0440\u0435\u0448\u0430\u0442\u044c \u0432\u043e\u043f\u0440\u043e\u0441 \u043e \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u0442\u043e\u0433\u043e, \u0447\u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e, \u0432 \u043d\u0443\u0436\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0438 \u0432 \u043d\u0443\u0436\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u0435. \u0414\u0430\u043d\u043d\u0430\u044f \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0444\u0430\u0439\u043b\u044b, \u043f\u0430\u043f\u043a\u0438 \u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439, \u043d\u043e \u0438 \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440 \u0446\u0435\u043b\u0438\u043a\u043e\u043c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0442\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u0438\u043b\u0438 \u043d\u043e\u0432\u043e\u0435 \u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u0435.",
"short_description": "",
"producer": "Symantec",
"image": "products/2017/05/26/thumb_1401701995.png",
"discount": 0,
"stock": 5,
"category": 5,
"product_class": 2,
"is_active": true,
"is_hit": false,
"is_new": false,
"created": "2017-05-26",
"updated": "2017-07-07"
}
},
{
"model": "products.product",
"pk": 89,
"fields": {
"name": "Microsoft Windows 10 Professional GetGenuine",
"slug": "microsoft-windows-10-professional-getgenuine",
"price": "12300.00",
"points": "1230.00",
"description": "Get Gunuine Windows Agreement GGWA \u2013 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0434\u043b\u044f \u043b\u0438\u0446\u0435\u043d\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u043f\u0438\u0439 \u041e\u0421 Windows, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0445 \u0440\u0430\u043d\u0435\u0435 \u0431\u0435\u0437 \u043b\u0438\u0446\u0435\u043d\u0437\u0438\u0438. \u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442 \u043e\u0442 5 \u0438 \u0431\u043e\u043b\u0435\u0435 \u043b\u0438\u0446\u0435\u043d\u0437\u0438\u0439 \u0438 \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043d\u0430 \u043d\u0430 \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.",
"short_description": "",
"producer": "Microsoft",
"image": "products/2017/05/26/thumb_1438765887.jpg",
"discount": 0,
"stock": 2,
"category": 6,
"product_class": 5,
"is_active": true,
"is_hit": false,
"is_new": false,
"created": "2017-05-26",
"updated": "2017-07-07"
}
},
{
"model": "products.product",
"pk": 90,
"fields": {
"name": "PERFEXPERT",
"slug": "perfexpert",
"price": "50000.00",
"points": "5000.00",
"description": "\u041a\u043e\u043c\u043f\u043b\u0435\u043a\u0441\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430, \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f, \u0430\u043d\u0430\u043b\u0438\u0437\u0430 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0438 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 24\u04257. \u041e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u043c\u0438 \u0438\u043d\u0446\u0438\u0434\u0435\u043d\u0442\u0430\u043c\u0438 \u0431\u0438\u0437\u043d\u0435\u0441 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043f\u0440\u0435\u0434\u043f\u0440\u0438\u044f\u0442\u0438\u044f, \u0438 \u0442\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0438 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0439 \u0431\u0438\u0437\u043d\u0435\u0441 \u0441\u0438\u0441\u0442\u0435\u043c\u044b. PERFEXPERT \u043d\u043e\u0432\u044b\u0439 \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0432\u0441\u0435\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0439 \u0431\u0438\u0437\u043d\u0435\u0441 \u0441\u0438\u0441\u0442\u0435\u043c\u044b.",
"short_description": "",
"producer": "SOFTPOINT",
"image": "products/2017/05/26/thumb_1424247372.png",
"discount": 0,
"stock": 5,
"category": 7,
"product_class": 4,
"is_active": true,
"is_hit": false,
"is_new": false,
"created": "2017-05-26",
"updated": "2017-07-07"
}
},
{
"model": "products.product",
"pk": 91,
"fields": {
"name": "Office 365 Business Open",
"slug": "office-365-business-open",
"price": "5700.00",
"points": "570.00",
"description": "\u041f\u043b\u0430\u043d Office 365 \u0411\u0438\u0437\u043d\u0435\u0441 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0432 \u0441\u0435\u0431\u044f \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0438 \u0446\u0435\u043d\u043e\u0432\u044b\u0435 \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u044b, \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0443\u0434\u043e\u0432\u043b\u0435\u0442\u0432\u043e\u0440\u0435\u043d\u0438\u044f \u043f\u043e\u0442\u0440\u0435\u0431\u043d\u043e\u0441\u0442\u0435\u0439 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 \u043c\u0430\u043b\u043e\u0433\u043e \u0438 \u0441\u0440\u0435\u0434\u043d\u0435\u0433\u043e \u0431\u0438\u0437\u043d\u0435\u0441\u0430 \u2013 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0439 \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u043c \u043e\u0442 1 \u0434\u043e 300 \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432.",
"short_description": "",
"producer": "Microsoft",
"image": "products/2017/05/26/office.png",
"discount": 5,
"stock": 13,
"category": 8,
"product_class": 3,
"is_active": true,
"is_hit": false,
"is_new": false,
"created": "2017-05-26",
"updated": "2017-07-07"
}
},
{
"model": "products.offer",
"pk": 30,
"fields": {
"product": 85,
"name": "Kaspersky-New-One-year",
"price": "1730.00",
"points": "0.00",
"attributes": "{\"License type\": \"New\", \"License term\": \"One year\"}"
}
},
{
"model": "products.offer",
"pk": 31,
"fields": {
"product": 85,
"name": "Kaspersky-New-One-year",
"price": "2600.00",
"points": "0.00",
"attributes": "{\"License type\": \"New\", \"License term\": \"Two years\"}"
}
},
{
"model": "products.offer",
"pk": 32,
"fields": {
"product": 85,
"name": "Kaspersky-Prolongation-One-year",
"price": "1200.00",
"points": "0.00",
"attributes": "{\"License type\": \"Prolongation\", \"License term\": \"One year\"}"
}
},
{
"model": "products.offer",
"pk": 33,
"fields": {
"product": 85,
"name": "Kaspersky-Migration-One-year",
"price": "1900.00",
"points": "0.00",
"attributes": "{\"License type\": \"Migration\", \"License term\": \"One year\"}"
}
}
]

@ -0,0 +1,56 @@
# from haystack.forms import FacetedSearchForm
# class FacetedProductSearchForm(FacetedSearchForm):
# def __init__(self, *args, **kwargs):
# data = dict(kwargs.get("data", []))
# self.categories = data.get('category', [])
# self.producers = data.get('producer', [])
# super(FacetedProductSearchForm, self).__init__(*args, **kwargs)
#
# def search(self):
# sqs = super(FacetedProductSearchForm, self).search()
# if self.categories:
# query = None
# for category in self.categories:
# if query:
# query += u' OR '
# else:
# query = u''
# query += u'"%s"' % sqs.query.clean(category)
# sqs = sqs.narrow(u'category_exact:%s' % query)
# if self.producers:
# query = None
# for producer in self.producers:
# if query:
# query += u' OR '
# else:
# query = u''
# query += u'"%s"' % sqs.query.clean(producer)
# sqs = sqs.narrow(u'brand_exact:%s' % query)
# return sqs
from crispy_forms.layout import Layout, ButtonHolder, Submit, HTML, Field, Button
from django import forms
from crispy_forms.helper import FormHelper
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from .models import Product
class ProductSearchForm(forms.ModelForm):
field_template = 'bootstrap/forms/product_search.html'
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_action = reverse_lazy('products:search')
self.helper.form_method = 'get'
self.helper.layout = Layout(
Field('name', template=self.field_template, placeholder="Поиск программы..."),
Button(_('search'), 'search', template=self.field_template)
)
super().__init__(*args, **kwargs)
class Meta:
model = Product
fields = ['name']

@ -0,0 +1,137 @@
from django.db import models
from django.urls import reverse_lazy
from django.contrib.postgres.fields import HStoreField
from django.utils.translation import ugettext_lazy as _
import mptt
from mptt.models import MPTTModel, TreeForeignKey
from autoslug import AutoSlugField
from core.models import AbstractStatusModel, AbstractDateTimeModel
class ProductAttribute(AbstractStatusModel):
name = models.CharField(max_length=64, blank=True, null=True, default=None)
slug = AutoSlugField(populate_from='name')
main_attribute = models.BooleanField(default=False)
def __str__(self):
return self.name
class Meta:
ordering = ('slug',)
verbose_name = _('Product attribute')
verbose_name_plural = _('Product attributes')
class ProductAttributeChoiceValue(AbstractDateTimeModel):
name = models.CharField(max_length=64, blank=True, null=True, default=None)
slug = AutoSlugField(populate_from='name')
attribute = models.ForeignKey(ProductAttribute, on_delete=models.CASCADE, related_name='values')
def __str__(self):
return self.name
class Meta:
unique_together = ('name', 'attribute')
verbose_name = 'attribute choices value'
verbose_name_plural = 'attribute choices values'
class Manufacturer(AbstractStatusModel):
name = models.CharField(max_length=64, blank=True, null=True, default=None)
slug = AutoSlugField(populate_from='name')
image = models.ImageField(upload_to='producers', blank=True, verbose_name="image of producer")
def __str__(self):
return self.name
def get_absolute_url(self):
# return reverse('products:CategoriesListByProducer', args=[self.slug])
return reverse_lazy('products:manufacturer', kwargs={'producer_slug': self.slug, 'path': ''})
class Meta:
verbose_name = 'Producer'
verbose_name_plural = 'Producers'
class ProductCategory(MPTTModel, AbstractDateTimeModel):
name = models.CharField(db_index=True, unique=True, max_length=64, blank=True, null=True, default=None)
slug = AutoSlugField(populate_from='name')
parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')
image = models.ImageField(upload_to='categories', blank=True, verbose_name="image of category")
def __str__(self):
return self.name
class Meta:
verbose_name = 'Product''s category'
verbose_name_plural = 'Category of products'
ordering = ('tree_id', 'level')
class MPTTMeta:
order_insertion_by = ['name']
mptt.register(ProductCategory, order_insertion_py=['name'])
class Product(AbstractStatusModel):
name = models.CharField(max_length=64, db_index=True, blank=True, null=True, default=None)
slug = AutoSlugField(populate_from='name')
price = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
description = models.TextField(db_index=True, blank=True, null=True, default=None)
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.PROTECT, related_name='products')
image = models.ImageField(upload_to='products', blank=True, verbose_name="image of products")
category = models.ForeignKey(ProductCategory, on_delete=models.SET_NULL, related_name='products', blank=True,
null=True, default=None)
attributes = models.ManyToManyField(ProductAttribute, related_name='categories', blank=True)
discount_policy = HStoreField(blank=True, null=True, default={})
is_active = models.BooleanField(default=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse_lazy('products:item', args=[self.slug])
class Meta:
indexes = [
models.Index(fields=['id', 'slug'])
]
verbose_name = _('product')
verbose_name_plural = _('products')
# def save(self, *args, **kwargs):
# if self.category:
# super(Product, self).save(*args, **kwargs)
#
# for cp in ProductClass.objects.filter(category=self.product_class):
# pp = ProductProperty.objects.filter(category_property=cp,
# products=self)
# if not pp:
# pp = ProductProperty(category_property=cp, products=self, value="--")
# pp.save()
# class Offer(models.Model):
# name = models.CharField(max_length=64, blank=True, null=True, default=None)
# slug = AutoSlugField(populate_from='name')
# price = models.DecimalField(max_digits=8, decimal_places=2, null=True, default=0.00)
# # points = models.DecimalField(max_digits=8, decimal_places=2, null=True, default=0.00)
# products = models.ForeignKey(Product, on_delete=models.CASCADE, blank=True, null=True, default=None,
# related_name='variants')
# is_active = models.BooleanField(default=True)
# attributes = HStoreField(blank=True, null=True, default={})
#
# def __str__(self):
# return self.name
#
# class Meta:
# verbose_name = 'Offer'
# verbose_name_plural = 'Offers'
#
# def save(self, *args, **kwargs):
# self.points = self.price * decimal.Decimal('0.1')
# super(Offer, self).save(*args, **kwargs)

@ -0,0 +1,20 @@
import datetime
from haystack import indexes
from .models import *
class ProductIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.EdgeNgramField(document=True, use_template=True, template_name="search/product_text.txt")
name = indexes.EdgeNgramField(model_attr='name')
description = indexes.EdgeNgramField(model_attr='description')
category = indexes.CharField(model_attr='category', faceted=True)
producer = indexes.CharField(model_attr='producer', faceted=True)
content_auto = indexes.EdgeNgramField(model_attr='name')
suggestions = indexes.FacetCharField()
def get_model(self):
return Product
def index_queryset(self, using=None):
return self.get_model().objects.all()

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

@ -0,0 +1,39 @@
"""Eshop URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.10/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.urls import re_path
import mptt_urls
from . import views
from .models import ProductCategory
urlpatterns = [
re_path(r'^search/$', views.ProductSearchView.as_view(), name='search')
#url(r'^products/(?P<product_id>\w+)/$', views.products, name='products'),
# url(r'^$', producerslist, name='ProductList'),
# Uncomment for elasticsearch
# url(r'^autocomplete/$', autocomplete),
# url(r'^find/$', FacetedSearchView.as_view(), name='haystack_search'),
# url(r'^products/(?P<product_slug>[-\w]+)/$', products, name='Product'),
# url(r'^(?P<producer_slug>[-\w]+)/(?P<path>.*)',
# mptt_urls.view(model=ProductCategory, view=categorieslist, slug_field='slug'),
# name='CategoriesListByProducer'),
# url(r'^(?P<producer_slug>[-\w]+)/$', categorieslist, name='CategoriesListByProducer'),
# url(r'^(?P<producer_slug>[-\w]+)/(?P<category_slug>[-\w]+)/$', productslist, name='ProductListByCategory')
]

@ -0,0 +1,39 @@
from .models import Product
def get_variant_picker_data(product):
variants = product.variants.all()
variant_attributes = product.attributes.all()
data = {'variants': [], 'variantAttributes': [], 'discount_policy': product.discount_policy}
for attribute in sorted(variant_attributes, key=lambda x: x.main_attribute, reverse=True):
data['variantAttributes'].append({
'name': attribute.name,
'public_name': attribute.name.split('_')[1],
'slug': attribute.slug,
'values': [{'name': value.name, 'slug': value.slug} for value in attribute.values.all()]
})
for variant in variants:
price = variant.price
variant_data = {
'id': variant.id,
'slug': variant.slug,
'name': variant.name,
'price': int(price),
'attributes': variant.attributes,
}
data['variants'].append(variant_data)
return data
def expand_categories(categories):
products = None
for e in categories:
if e.name.startswith('None'):
products = Product.objects.filter(category=e)
return [x for x in categories if not x.name.startswith('None')], products

@ -0,0 +1,116 @@
from django.shortcuts import render, get_list_or_404, get_object_or_404
from django.contrib import auth
from django.http import Http404
import json
import decimal
from django.views.generic import ListView
from cart.forms import CartAddProductForm
from .utils import *
from cart.cart import Cart
from .models import *
class ProductSearchView(ListView):
model = Product
template_name = 'products/search.html'
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(name__icontains=self.request)
# Uncomment for elasticsearch
# from .layout import FacetedProductSearchForm
# from haystack.generic_views import FacetedSearchView as BaseFacetedSearchView
# from haystack.query import SearchQuerySet
def serialize_decimal(obj):
if isinstance(obj, decimal.Decimal):
return str(obj)
return json.JSONEncoder.default(obj)
def producerslist(request):
username = auth.get_user(request).username
# category = None
# categories = ProductCategory.objects.filter(level__lte=0)
# products = Product.objects.filter(is_active=True)
producers = Producer.objects.filter(is_active=True)
# if category_slug:
# category = get_object_or_404(ProductCategory, slug=category_slug)
# products = products.filter(category__in=category.get_descendants(include_self=True))
return render(request, 'products/list.html', locals())
# def categorieslist(request, producer_slug, category_slug=None):
# username = accounts_ext.get_user(request).username
# producer = Producer.objects.get(slug=producer_slug)
# if category_slug:
# _categories = ProductCategory.objects.filter(is_active=True, parent=category_slug)
# else:
# _categories = ProductCategory.objects.filter(is_active=True, producer=producer, level__lte=0)
# categories, products = expand_categories(_categories)
# return render(request, 'products/categorieslist.html', {'username': username, 'categories':categories,
# 'products': products})
def categorieslist(request, path, instance, producer_slug):
username = auth.get_user(request).username
if instance:
_categories = instance.get_children()
else:
_categories = get_list_or_404(ProductCategory, producer__slug=producer_slug, level__lte=0)
if _categories:
categories, products = expand_categories(_categories)
else:
return productslist(request, producer_slug, instance.slug)
return render(
request,
'products/categorieslist.html',
{
'username': username,
'instance': instance,
'categories': categories,
'producer_slug': producer_slug,
'products': products
}
)
def productslist(request, producer_slug, category_slug):
username = auth.get_user(request).username
category = ProductCategory.objects.get(slug=category_slug)
products = Product.objects.filter(is_active=True, category=category)
return render(request, 'products/productslist.html', locals())
def product(request, product_slug):
username = auth.get_user(request).username
product = get_object_or_404(Product, slug=product_slug, is_active=True)
cart_product_form = CartAddProductForm()
variant_picker_data = get_variant_picker_data(product)
show_variant_picker = all([v.attributes for v in product.variants.all()])
# session_key = request.session.session_key
# if not session_key:
# request.session.cycle_key()
return render(request, 'products/product.html', {'username': username, 'products': product, 'form': cart_product_form,
'show_variant_picker': show_variant_picker,
'variant_picker_data': variant_picker_data,
})
# Uncomment for elasticsearch
# def autocomplete(request):
# sqs = SearchQuerySet().autocomplete(content_auto=request.GET.get('query', ''))[:5]
# s = []
# for result in sqs:
# print(result)
# d = {"value": result.name, "data": result.object.slug}
# s.append(d)
# output = {'suggestions': s}
# return JsonResponse(output)
#
# class FacetedSearchView(BaseFacetedSearchView):
# form_class = FacetedProductSearchForm
# facet_fields = ['category', 'producer']
# template_name = 'search/search.html'
# paginate_by = 3
# context_object_name = 'object_list'
Loading…
Cancel
Save