added endpoints, filters, serializers and extend model Product, ProductType, ProductSubtype

This commit is contained in:
Anatoly 2019-10-26 18:54:22 +03:00
parent e3616ee7f8
commit 37d745eeaa
13 changed files with 227 additions and 9 deletions

View File

@ -6,6 +6,7 @@ from django.utils.translation import gettext_lazy as _
from comment.models import Comment from comment.models import Comment
from establishment import models from establishment import models
from main.models import Award from main.models import Award
from product.models import Product
from review import models as review_models from review import models as review_models
@ -46,13 +47,18 @@ class CommentInline(GenericTabularInline):
extra = 0 extra = 0
class ProductInline(admin.TabularInline):
model = Product
extra = 0
@admin.register(models.Establishment) @admin.register(models.Establishment)
class EstablishmentAdmin(admin.ModelAdmin): class EstablishmentAdmin(admin.ModelAdmin):
"""Establishment admin.""" """Establishment admin."""
list_display = ['id', '__str__', 'image_tag', ] list_display = ['id', '__str__', 'image_tag', ]
inlines = [ inlines = [
AwardInline, ContactPhoneInline, ContactEmailInline, AwardInline, ContactPhoneInline, ContactEmailInline,
ReviewInline, CommentInline] ReviewInline, CommentInline, ProductInline]
@admin.register(models.Position) @admin.register(models.Position)

View File

@ -486,6 +486,11 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
""" """
return self.id return self.id
@property
def wines(self):
"""Return list products with type wine"""
return self.products.wines()
class Position(BaseAttributes, TranslatedFieldsMixin): class Position(BaseAttributes, TranslatedFieldsMixin):
"""Position model.""" """Position model."""

18
apps/product/admin.py Normal file
View File

@ -0,0 +1,18 @@
"""Product admin conf."""
from django.contrib import admin
from .models import Product, ProductType, ProductSubType
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
"""Admin page for model Product."""
@admin.register(ProductType)
class ProductTypeAdmin(admin.ModelAdmin):
"""Admin page for model ProductType."""
@admin.register(ProductSubType)
class ProductSubTypeAdmin(admin.ModelAdmin):
"""Admin page for model ProductSubType."""

34
apps/product/filters.py Normal file
View File

@ -0,0 +1,34 @@
"""Filters for app Product."""
from django.core.validators import EMPTY_VALUES
from django_filters import rest_framework as filters
from product import models
class ProductListFilterSet(filters.FilterSet):
"""Product filter set."""
establishment_id = filters.NumberFilter()
type = filters.ChoiceFilter(method='by_type',
choices=models.ProductType.INDEX_NAME_TYPES)
subtype = filters.ChoiceFilter(method='by_subtype',
choices=models.ProductSubType.INDEX_NAME_TYPES)
class Meta:
"""Meta class."""
model = models.Product
fields = [
'establishment_id',
'type',
'subtype',
]
def by_type(self, queryset, name, value):
if value not in EMPTY_VALUES:
return queryset.by_type(value)
return queryset
def by_subtype(self, queryset, name, value):
if value not in EMPTY_VALUES:
return queryset.by_subtype(value)
return queryset

View File

@ -1,5 +1,6 @@
"""Product app models.""" """Product app models."""
from django.db import models from django.db import models
from django.core.exceptions import ValidationError
from django.contrib.postgres.fields import JSONField from django.contrib.postgres.fields import JSONField
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from utils.models import (BaseAttributes, ProjectBaseMixin, from utils.models import (BaseAttributes, ProjectBaseMixin,
@ -9,9 +10,23 @@ from utils.models import (BaseAttributes, ProjectBaseMixin,
class ProductType(TranslatedFieldsMixin, ProjectBaseMixin): class ProductType(TranslatedFieldsMixin, ProjectBaseMixin):
"""ProductType model.""" """ProductType model."""
STR_FIELD_NAME = 'name'
# INDEX NAME CHOICES
FOOD = 'food'
WINE = 'wine'
LIQUOR = 'liquor'
INDEX_NAME_TYPES = (
(FOOD, _('Food')),
(WINE, _('Wine')),
(LIQUOR, _('Liquor')),
)
name = TJSONField(blank=True, null=True, default=None, name = TJSONField(blank=True, null=True, default=None,
verbose_name=_('Name'), help_text='{"en-GB":"some text"}') verbose_name=_('Name'), help_text='{"en-GB":"some text"}')
index_name = models.CharField(max_length=50, unique=True, db_index=True, index_name = models.CharField(max_length=50, choices=INDEX_NAME_TYPES,
unique=True, db_index=True,
verbose_name=_('Index name')) verbose_name=_('Index name'))
use_subtypes = models.BooleanField(_('Use subtypes'), default=True) use_subtypes = models.BooleanField(_('Use subtypes'), default=True)
@ -25,19 +40,35 @@ class ProductType(TranslatedFieldsMixin, ProjectBaseMixin):
class ProductSubType(TranslatedFieldsMixin, ProjectBaseMixin): class ProductSubType(TranslatedFieldsMixin, ProjectBaseMixin):
"""ProductSubtype model.""" """ProductSubtype model."""
STR_FIELD_NAME = 'name'
# INDEX NAME CHOICES
RUM = 'rum'
OTHER = 'other'
INDEX_NAME_TYPES = (
(RUM, _('Rum')),
(OTHER, _('Other')),
)
product_type = models.ForeignKey(ProductType, on_delete=models.CASCADE, product_type = models.ForeignKey(ProductType, on_delete=models.CASCADE,
related_name='subtypes', related_name='subtypes',
verbose_name=_('Product type')) verbose_name=_('Product type'))
name = TJSONField(blank=True, null=True, default=None, name = TJSONField(blank=True, null=True, default=None,
verbose_name=_('Name'), help_text='{"en-GB":"some text"}') verbose_name=_('Name'), help_text='{"en-GB":"some text"}')
index_name = models.CharField(max_length=50, unique=True, db_index=True, index_name = models.CharField(max_length=50, choices=INDEX_NAME_TYPES,
unique=True, db_index=True,
verbose_name=_('Index name')) verbose_name=_('Index name'))
class Meta: class Meta:
"""Meta class.""" """Meta class."""
verbose_name = _('Product type') verbose_name = _('Product subtype')
verbose_name_plural = _('Product types') verbose_name_plural = _('Product subtypes')
def clean_fields(self, exclude=None):
if not self.product_type.use_subtypes:
raise ValidationError(_('Product type is not use subtypes.'))
class ProductManager(models.Manager): class ProductManager(models.Manager):
@ -47,16 +78,33 @@ class ProductManager(models.Manager):
class ProductQuerySet(models.QuerySet): class ProductQuerySet(models.QuerySet):
"""Product queryset.""" """Product queryset."""
def with_base_related(self):
return self.select_related('country', 'product_type', 'establishment') \
.prefetch_related('product_type__subtypes')
def common(self): def common(self):
return self.filter(category=self.model.COMMON) return self.filter(category=self.model.COMMON)
def online(self): def online(self):
return self.filter(category=self.model.ONLINE) return self.filter(category=self.model.ONLINE)
def wines(self):
return self.filter(type__index_name=ProductType.WINE)
def by_type(self, type: str):
"""Filter by type."""
return self.filter(product_type__index_name=type)
def by_subtype(self, subtype: str):
"""Filter by subtype."""
return self.filter(subtypes__index_name=subtype)
class Product(TranslatedFieldsMixin, BaseAttributes): class Product(TranslatedFieldsMixin, BaseAttributes):
"""Product models.""" """Product models."""
STR_FIELD_NAME = 'name'
COMMON = 0 COMMON = 0
ONLINE = 1 ONLINE = 1
@ -75,10 +123,17 @@ class Product(TranslatedFieldsMixin, BaseAttributes):
country = models.ForeignKey('location.Country', on_delete=models.PROTECT, country = models.ForeignKey('location.Country', on_delete=models.PROTECT,
verbose_name=_('Country')) verbose_name=_('Country'))
available = models.BooleanField(_('Available'), default=True) available = models.BooleanField(_('Available'), default=True)
type = models.ForeignKey(ProductType, on_delete=models.PROTECT, product_type = models.ForeignKey(ProductType, on_delete=models.PROTECT,
related_name='products', verbose_name=_('Type')) related_name='products', verbose_name=_('Type'))
subtypes = models.ManyToManyField(ProductSubType, related_name='products', subtypes = models.ManyToManyField(ProductSubType, blank=True,
related_name='products',
verbose_name=_('Subtypes')) verbose_name=_('Subtypes'))
establishment = models.ForeignKey('establishment.Establishment',
on_delete=models.PROTECT,
related_name='products',
verbose_name=_('establishment'))
public_mark = models.PositiveIntegerField(blank=True, null=True, default=None,
verbose_name=_('public mark'),)
objects = ProductManager.from_queryset(ProductQuerySet)() objects = ProductManager.from_queryset(ProductQuerySet)()
@ -93,7 +148,7 @@ class OnlineProductManager(ProductManager):
"""Extended manger for OnlineProduct model.""" """Extended manger for OnlineProduct model."""
def get_queryset(self): def get_queryset(self):
"""Overrided get_queryset method.""" """Overridden get_queryset method."""
return super().get_queryset().online() return super().get_queryset().online()

View File

@ -0,0 +1,3 @@
from .common import *
from .web import *
from .mobile import *

View File

@ -1 +1,55 @@
"""Product app serializers."""
from rest_framework import serializers from rest_framework import serializers
from utils.serializers import TranslatedField
from product.models import Product, ProductSubType, ProductType
class ProductSubTypeBaseSerializer(serializers.ModelSerializer):
"""ProductSubType base serializer"""
name_translated = TranslatedField()
index_name_display = serializers.CharField(source='get_index_name_display')
class Meta:
model = ProductSubType
fields = [
'id',
'name_translated',
'index_name_display',
]
class ProductTypeBaseSerializer(serializers.ModelSerializer):
"""ProductType base serializer"""
name_translated = TranslatedField()
index_name_display = serializers.CharField(source='get_index_name_display')
class Meta:
model = ProductType
fields = [
'id',
'name_translated',
'index_name_display',
]
class ProductBaseSerializer(serializers.ModelSerializer):
"""Product base serializer."""
name_translated = TranslatedField()
description_translated = TranslatedField()
category_display = serializers.CharField(source='get_category_display')
product_type = ProductTypeBaseSerializer()
subtypes = ProductSubTypeBaseSerializer(many=True)
class Meta:
"""Meta class."""
model = Product
fields = [
'id',
'name_translated',
'category_display',
'description_translated',
'available',
'product_type',
'subtypes',
'public_mark',
]

View File

@ -0,0 +1,10 @@
"""Product url patterns."""
from django.urls import path
from product import views
app_name = 'product'
urlpatterns = [
path('', views.ProductListView.as_view(), name='list')
]

View File

@ -0,0 +1,7 @@
"""Product web url patterns."""
from product.urls.common import urlpatterns as common_urlpatterns
urlpatterns = [
]
urlpatterns.extend(common_urlpatterns)

View File

@ -0,0 +1,4 @@
from .back import *
from .common import *
from .mobile import *
from .web import *

View File

@ -0,0 +1,20 @@
"""Product app views."""
from rest_framework import generics, permissions
from product.models import Product
from product import serializers
from product import filters
class ProductBaseView(generics.GenericAPIView):
"""Product base view"""
def get_queryset(self):
"""Override get_queryset method."""
return Product.objects.with_base_related()
class ProductListView(ProductBaseView, generics.ListAPIView):
"""List view for model Product."""
permission_classes = (permissions.AllowAny, )
serializer_class = serializers.ProductBaseSerializer
filter_class = filters.ProductListFilterSet

View File

@ -75,6 +75,7 @@ PROJECT_APPS = [
'favorites.apps.FavoritesConfig', 'favorites.apps.FavoritesConfig',
'rating.apps.RatingConfig', 'rating.apps.RatingConfig',
'tag.apps.TagConfig', 'tag.apps.TagConfig',
'product.apps.ProductConfig',
] ]
EXTERNAL_APPS = [ EXTERNAL_APPS = [

View File

@ -35,4 +35,5 @@ urlpatterns = [
path('comments/', include('comment.urls.web')), path('comments/', include('comment.urls.web')),
path('favorites/', include('favorites.urls')), path('favorites/', include('favorites.urls')),
path('timetables/', include('timetable.urls.web')), path('timetables/', include('timetable.urls.web')),
path('products/', include('product.urls.web')),
] ]