parent
17b21e6dbe
commit
8a94098546
12 changed files with 1153 additions and 0 deletions
@ -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…
Reference in new issue