Merge branch 'feature/gm-192' into feature/gm-148

# Conflicts:
#	apps/news/models.py
#	apps/news/serializers.py
#	apps/news/views.py
This commit is contained in:
Anatoly 2019-10-15 12:08:23 +03:00
commit 2d830b2821
37 changed files with 891 additions and 151 deletions

View File

@ -40,8 +40,7 @@ class PasswordResetConfirmView(JWTGenericViewMixin):
queryset = models.User.objects.active()
def get_object(self):
"""Override get_object method
"""
"""Override get_object method"""
queryset = self.filter_queryset(self.get_queryset())
uidb64 = self.kwargs.get('uidb64')

View File

@ -5,7 +5,7 @@ from django.utils.translation import gettext_lazy as _
from comment.models import Comment
from establishment import models
from main.models import Award, MetaDataContent
from main.models import Award
from review import models as review_models
@ -24,11 +24,6 @@ class AwardInline(GenericTabularInline):
extra = 0
class MetaDataContentInline(GenericTabularInline):
model = MetaDataContent
extra = 0
class ContactPhoneInline(admin.TabularInline):
"""Contact phone inline admin."""
model = models.ContactPhone
@ -56,8 +51,7 @@ class EstablishmentAdmin(admin.ModelAdmin):
"""Establishment admin."""
list_display = ['id', '__str__', 'image_tag', ]
inlines = [
AwardInline, MetaDataContentInline,
ContactPhoneInline, ContactEmailInline,
AwardInline, ContactPhoneInline, ContactEmailInline,
ReviewInline, CommentInline]
@ -84,4 +78,19 @@ class MenuAdmin(admin.ModelAdmin):
"""Get user's short name."""
return obj.category_translated
category_translated.short_description = _('category')
category_translated.short_description = _('category')
@admin.register(models.EstablishmentTypeTagCategory)
class EstablishmentTypeTagCategoryAdmin(admin.ModelAdmin):
"""EstablishmentTypeTagCategory admin."""
@admin.register(models.EstablishmentSubTypeTagCategory)
class EstablishmentSubTypeTagCategoryAdmin(admin.ModelAdmin):
"""EstablishmentTypeTagCategory admin."""
@admin.register(models.EstablishmentTag)
class EstablishmentTagAdmin(admin.ModelAdmin):
"""EstablishmentTag admin."""

View File

@ -26,3 +26,17 @@ class EstablishmentFilter(filters.FilterSet):
if value not in EMPTY_VALUES:
return queryset.search(value, locale=self.request.locale)
return queryset
class EstablishmentTypeTagFilter(filters.FilterSet):
"""Establishment tag filter set."""
type_id = filters.NumberFilter(field_name='id')
class Meta:
"""Meta class."""
model = models.EstablishmentType
fields = (
'type_id',
)

View File

@ -0,0 +1,35 @@
# Generated by Django 2.2.4 on 2019-10-09 07:15
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('establishment', '0031_establishment_slug'),
]
operations = [
migrations.CreateModel(
name='EstablishmentTag',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
],
options={
'verbose_name': 'establishment tag',
'verbose_name_plural': 'establishment tags',
},
),
migrations.CreateModel(
name='EstablishmentTypeTagCategory',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('establishment_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tag_categories', to='establishment.EstablishmentType', verbose_name='establishment type')),
],
options={
'verbose_name': 'establishment type tag categories',
'verbose_name_plural': 'establishment type tag categories',
},
),
]

View File

@ -0,0 +1,30 @@
# Generated by Django 2.2.4 on 2019-10-09 07:15
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('tag', '0001_initial'),
('establishment', '0032_establishmenttag_establishmenttypetagcategory'),
]
operations = [
migrations.AddField(
model_name='establishmenttypetagcategory',
name='tag_category',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='est_type_tag_categories', to='tag.TagCategory', verbose_name='tag category'),
),
migrations.AddField(
model_name='establishmenttag',
name='establishment',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tags', to='establishment.Establishment', verbose_name='establishment'),
),
migrations.AddField(
model_name='establishmenttag',
name='tag',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tags', to='tag.Tag', verbose_name='tag'),
),
]

View File

@ -0,0 +1,14 @@
# Generated by Django 2.2.4 on 2019-10-09 14:57
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('establishment', '0033_auto_20191009_0715'),
('establishment', '0033_auto_20191003_0943_squashed_0034_auto_20191003_1036'),
]
operations = [
]

View File

@ -0,0 +1,27 @@
# Generated by Django 2.2.4 on 2019-10-11 10:47
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('tag', '0002_auto_20191009_1408'),
('establishment', '0034_merge_20191009_1457'),
]
operations = [
migrations.CreateModel(
name='EstablishmentSubTypeTagCategory',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('establishment_subtype', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tag_categories', to='establishment.EstablishmentSubType', verbose_name='establishment subtype')),
('tag_category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='est_subtype_tag_categories', to='tag.TagCategory', verbose_name='tag category')),
],
options={
'verbose_name': 'establishment subtype tag categories',
'verbose_name_plural': 'establishment subtype tag categories',
},
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.4 on 2019-10-11 13:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('establishment', '0035_establishmentsubtypetagcategory'),
]
operations = [
migrations.AlterField(
model_name='establishment',
name='establishment_subtypes',
field=models.ManyToManyField(blank=True, related_name='subtype_establishment', to='establishment.EstablishmentSubType', verbose_name='subtype'),
),
]

View File

@ -16,12 +16,29 @@ from phonenumber_field.modelfields import PhoneNumberField
from collection.models import Collection
from location.models import Address
from main.models import Award, MetaDataContent
from main.models import Award
from tag.models import Tag, TagCategory
from review.models import Review
from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin,
TranslatedFieldsMixin, BaseAttributes)
class EstablishmentTypeQuerySet(models.QuerySet):
"""QuerySet for model EstablishmentType."""
def with_base_related(self):
"""Return QuerySet with base related."""
return self.prefetch_related(
models.Prefetch('tag_categories',
EstablishmentTypeTagCategory.objects.select_related('tag_category')),
models.Prefetch('establishmentsubtype_set',
EstablishmentSubType.objects.prefetch_related(
models.Prefetch(
'tag_categories',
EstablishmentSubTypeTagCategory.objects.select_related('tag_category'))))
)
# todo: establishment type&subtypes check
class EstablishmentType(TranslatedFieldsMixin, ProjectBaseMixin):
"""Establishment type model."""
@ -32,6 +49,8 @@ class EstablishmentType(TranslatedFieldsMixin, ProjectBaseMixin):
help_text='{"en-GB":"some text"}')
use_subtypes = models.BooleanField(_('Use subtypes'), default=True)
objects = EstablishmentTypeQuerySet.as_manager()
class Meta:
"""Meta class."""
@ -76,10 +95,13 @@ class EstablishmentQuerySet(models.QuerySet):
def with_base_related(self):
"""Return qs with related objects."""
return self.select_related('address').prefetch_related(
return self.select_related('address', 'establishment_type').prefetch_related(
models.Prefetch('tags',
MetaDataContent.objects.select_related(
'metadata__category'))
EstablishmentTag.objects.select_related('tag')),
models.Prefetch('establishment_type__tag_categories',
EstablishmentTypeTagCategory.objects.select_related('tag_category')),
models.Prefetch('establishment_type__establishmentsubtype_set',
EstablishmentSubType.objects.prefetch_related('tag_categories')),
)
def with_extended_related(self):
@ -256,6 +278,7 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
on_delete=models.PROTECT,
verbose_name=_('type'))
establishment_subtypes = models.ManyToManyField(EstablishmentSubType,
blank=True,
related_name='subtype_establishment',
verbose_name=_('subtype'))
address = models.ForeignKey(Address, blank=True, null=True, default=None,
@ -478,6 +501,7 @@ class ContactEmail(models.Model):
def __str__(self):
return f'{self.email}'
#
# class Wine(TranslatedFieldsMixin, models.Model):
# """Wine model."""
@ -551,3 +575,82 @@ class SocialNetwork(models.Model):
def __str__(self):
return self.title
class EstablishmentTagQuerySet(models.QuerySet):
"""Establishment tag QuerySet."""
def by_country_code(self, code):
"""Return establishment tags by establishment country code."""
return self.filter(establishment__address__city__country__code=code)
class EstablishmentTag(models.Model):
"""Establishment tag model."""
tag = models.ForeignKey('tag.Tag',
on_delete=models.CASCADE,
related_name='tags',
verbose_name=_('tag'))
establishment = models.ForeignKey('establishment.Establishment',
on_delete=models.CASCADE,
related_name='tags',
verbose_name=_('establishment'))
objects = EstablishmentTagQuerySet.as_manager()
class Meta:
verbose_name = _('establishment tag')
verbose_name_plural = _('establishment tags')
class EstablishmentTypeTagCategoryQuerySet(models.QuerySet):
"""EstablishmentTypeTagCategory QuerySet."""
def by_country_code(self, code):
"""Return establishment tags by country code"""
return self.filter(tag_category__country__code=code)
def with_base_related(self):
"""Return with related relations."""
return self.select_related('establishment_type', 'tag_category')
class EstablishmentTypeTagCategory(models.Model):
"""Tag categories based on establishment type."""
establishment_type = models.ForeignKey(EstablishmentType,
on_delete=models.CASCADE,
related_name='tag_categories',
verbose_name=_('establishment type'))
tag_category = models.ForeignKey('tag.TagCategory',
on_delete=models.CASCADE,
related_name='est_type_tag_categories',
verbose_name=_('tag category'))
objects = EstablishmentTypeTagCategoryQuerySet.as_manager()
class Meta:
verbose_name = _('establishment type tag categories')
verbose_name_plural = _('establishment type tag categories')
class EstablishmentSubTypeTagCategoryQuerySet(models.QuerySet):
"""QuerySet for tag categories based on establishment subtype."""
def with_base_related(self):
"""Return queryset with base related."""
return self.select_related('establishment_subtype', 'tag_category')
class EstablishmentSubTypeTagCategory(models.Model):
"""Tag categories based on establishment subtype."""
establishment_subtype = models.ForeignKey(EstablishmentSubType,
on_delete=models.CASCADE,
related_name='tag_categories',
verbose_name=_('establishment subtype'))
tag_category = models.ForeignKey('tag.TagCategory',
on_delete=models.CASCADE,
related_name='est_subtype_tag_categories',
verbose_name=_('tag category'))
objects = EstablishmentSubTypeTagCategoryQuerySet.as_manager()
class Meta:
verbose_name = _('establishment subtype tag categories')
verbose_name_plural = _('establishment subtype tag categories')

View File

@ -1,13 +1,16 @@
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from establishment import models
from establishment.serializers import (
EstablishmentBaseSerializer, PlateSerializer, ContactEmailsSerializer,
ContactPhonesSerializer, SocialNetworkRelatedSerializers,
EstablishmentTypeSerializer)
from utils.decorators import with_base_attributes
EstablishmentTypeBaseSerializer, EstablishmentSubTypeBaseSerializer,
EstablishmentTypeTagCategoryBaseSerializer)
from main.models import Currency
from tag.serializers import TagBaseSerializer
from utils.decorators import with_base_attributes
from utils.serializers import TranslatedField
class EstablishmentListCreateSerializer(EstablishmentBaseSerializer):
@ -21,7 +24,7 @@ class EstablishmentListCreateSerializer(EstablishmentBaseSerializer):
emails = ContactEmailsSerializer(read_only=True, many=True, )
socials = SocialNetworkRelatedSerializers(read_only=True, many=True, )
slug = serializers.SlugField(required=True, allow_blank=False, max_length=50)
type = EstablishmentTypeSerializer(source='establishment_type', read_only=True)
type = EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True)
class Meta:
model = models.Establishment
@ -55,7 +58,7 @@ class EstablishmentRUDSerializer(EstablishmentBaseSerializer):
phones = ContactPhonesSerializer(read_only=False, many=True, )
emails = ContactEmailsSerializer(read_only=False, many=True, )
socials = SocialNetworkRelatedSerializers(read_only=False, many=True, )
type = EstablishmentTypeSerializer(source='establishment_type')
type = EstablishmentTypeBaseSerializer(source='establishment_type')
class Meta:
model = models.Establishment
@ -142,3 +145,95 @@ class EmployeeBackSerializers(serializers.ModelSerializer):
'name'
]
class EstablishmentTagCreateSerializer(serializers.ModelSerializer):
"""Serializer for model EstablishmentTag."""
class Meta:
model = models.EstablishmentTag
fields = [
'tag',
'establishment'
]
extra_kwargs = {
'tag': {'write_only': True},
'establishment': {'write_only': True},
}
def validate(self, attrs):
"""Validate method."""
establishment = attrs.get('establishment')
tag = attrs.get('tag')
# Check if tag is already added to establishment.
if establishment.tags.filter(tag=tag).exists():
raise serializers.ValidationError(detail={'detail': _('Tag is already added.')})
# Сhecking tag availability for establishment type.
if not establishment.establishment_type.use_subtypes:
qs = establishment.establishment_type.tag_categories.filter(tag_category=tag.category)
else:
# Сhecking tag availability for establishment subtype.
qs = establishment.establishment_type.tag_categories.filter(
establishmentsubtype_set__tag_category=tag.category)
if not qs.exists():
raise serializers.ValidationError(
detail={'detail': _('Tag is not available for this establishment type|subtype.')})
return attrs
class EstablishmentSubTypeTagCategoryBaseSerializer(serializers.ModelSerializer):
"""Serializer for intermediate model EstablishmentSubTypeTagCategories."""
id = serializers.IntegerField(source='tag_category.id', read_only=True)
label_translated = TranslatedField(source='tag_category.label_translated')
tags = TagBaseSerializer(source='tag_category.tags', many=True, read_only=True)
class Meta:
"""Meta class."""
model = models.EstablishmentSubTypeTagCategory
fields = [
'id',
'label_translated',
'tags',
'establishment_subtype',
'tag_category',
]
extra_kwargs = {
'establishment_subtype': {'write_only': True},
'tag_category': {'write_only': True},
}
def validate(self, attrs):
"""Override validate method."""
if models.EstablishmentTypeTagCategory.objects.filter(
establishment_type=attrs.get('establishment_type'),
tag_category=attrs.get('tag_category')).exists():
raise serializers.ValidationError(detail={'detail': _('Objects is already attached.')})
return attrs
class EstablishmentSubTypeSerializer(EstablishmentSubTypeBaseSerializer):
"""Extended serializer for EstablishmentSubType model with tags."""
tag_categories = EstablishmentSubTypeTagCategoryBaseSerializer(many=True, read_only=True)
class Meta(EstablishmentSubTypeBaseSerializer.Meta):
"""Meta class"""
fields = [
'id',
'name_translated',
'tag_categories'
]
class EstablishmentTagsByType(EstablishmentTypeBaseSerializer):
"""Tags by establishment type"""
tag_categories = EstablishmentTypeTagCategoryBaseSerializer(many=True)
subtypes = EstablishmentSubTypeSerializer(many=True, source='establishmentsubtype_set')
class Meta(EstablishmentTypeBaseSerializer.Meta):
"""Meta class."""
fields = [
'id',
'name_translated',
'tag_categories',
'subtypes',
]

View File

@ -1,17 +1,19 @@
"""Establishment serializers."""
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from comment import models as comment_models
from comment.serializers import common as comment_serializers
from establishment import models
from favorites.models import Favorites
from location.serializers import AddressBaseSerializer
from main.models import MetaDataContent
from main.serializers import MetaDataContentSerializer, AwardSerializer, CurrencySerializer
from main.serializers import AwardSerializer, CurrencySerializer
from review import models as review_models
from tag.serializers import TagBaseSerializer
from timetable.serialziers import ScheduleRUDSerializer
from utils import exceptions as utils_exceptions
from utils.serializers import TranslatedField, ProjectModelSerializer
from utils.serializers import ProjectModelSerializer
from utils.serializers import TranslatedField
class ContactPhonesSerializer(serializers.ModelSerializer):
@ -86,30 +88,6 @@ class MenuRUDSerializers(ProjectModelSerializer):
]
class EstablishmentTypeSerializer(serializers.ModelSerializer):
"""Serializer for EstablishmentType model."""
name_translated = serializers.CharField(allow_null=True)
class Meta:
"""Meta class."""
model = models.EstablishmentType
fields = ('id', 'name_translated')
class EstablishmentSubTypeSerializer(serializers.ModelSerializer):
"""Serializer for EstablishmentSubType models."""
name_translated = serializers.CharField(allow_null=True)
class Meta:
"""Meta class."""
model = models.EstablishmentSubType
fields = ('id', 'name_translated')
class ReviewSerializer(serializers.ModelSerializer):
"""Serializer for model Review."""
text_translated = serializers.CharField(read_only=True)
@ -122,6 +100,75 @@ class ReviewSerializer(serializers.ModelSerializer):
)
class EstablishmentTypeBaseSerializer(serializers.ModelSerializer):
"""Serializer for EstablishmentType model."""
name_translated = TranslatedField()
class Meta:
"""Meta class."""
model = models.EstablishmentType
fields = [
'id',
'name',
'name_translated',
'use_subtypes'
]
extra_kwargs = {
'name': {'write_only': True},
'use_subtypes': {'write_only': True},
}
class EstablishmentSubTypeBaseSerializer(serializers.ModelSerializer):
"""Serializer for EstablishmentSubType models."""
name_translated = TranslatedField()
class Meta:
"""Meta class."""
model = models.EstablishmentSubType
fields = [
'id',
'name',
'name_translated',
'establishment_type'
]
extra_kwargs = {
'name': {'write_only': True},
'establishment_type': {'write_only': True}
}
class EstablishmentTypeTagCategoryBaseSerializer(serializers.ModelSerializer):
"""Serializer for intermediate model EstablishmentTypeTagCategories."""
id = serializers.IntegerField(source='tag_category.id', read_only=True)
label_translated = TranslatedField(source='tag_category.label_translated')
tags = TagBaseSerializer(source='tag_category.tags', many=True, read_only=True)
class Meta:
"""Meta class."""
model = models.EstablishmentTypeTagCategory
fields = [
'id',
'label_translated',
'tags',
'establishment_type',
'tag_category',
]
extra_kwargs = {
'establishment_type': {'write_only': True},
'tag_category': {'write_only': True},
}
def validate(self, attrs):
"""Override validate method."""
if models.EstablishmentTypeTagCategory.objects.filter(
establishment_type=attrs.get('establishment_type'),
tag_category=attrs.get('tag_category')).exists():
raise serializers.ValidationError(detail={'detail': _('Objects is already attached.')})
return attrs
class EstablishmentEmployeeSerializer(serializers.ModelSerializer):
"""Serializer for actual employees."""
@ -138,14 +185,28 @@ class EstablishmentEmployeeSerializer(serializers.ModelSerializer):
fields = ('id', 'name', 'position_translated', 'awards', 'priority')
class EstablishmentTagSerializer(serializers.ModelSerializer):
"""Serializer for intermediate model EstablishmentTag."""
id = serializers.IntegerField(source='tag.id')
label_translated = serializers.CharField(source='tag.label_translated')
class Meta:
"""Meta class."""
model = models.EstablishmentTag
fields = [
'id',
'label_translated'
]
class EstablishmentBaseSerializer(ProjectModelSerializer):
"""Base serializer for Establishment model."""
preview_image = serializers.URLField(source='preview_image_url')
slug = serializers.SlugField(allow_blank=False, required=True, max_length=50)
address = AddressBaseSerializer()
tags = MetaDataContentSerializer(many=True)
in_favorites = serializers.BooleanField(allow_null=True)
tags = EstablishmentTagSerializer(many=True)
class Meta:
"""Meta class."""
@ -171,8 +232,8 @@ class EstablishmentDetailSerializer(EstablishmentBaseSerializer):
description_translated = TranslatedField()
image = serializers.URLField(source='image_url')
type = EstablishmentTypeSerializer(source='establishment_type', read_only=True)
subtypes = EstablishmentSubTypeSerializer(many=True, source='establishment_subtypes')
type = EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True)
subtypes = EstablishmentSubTypeBaseSerializer(many=True, source='establishment_subtypes')
awards = AwardSerializer(many=True)
schedule = ScheduleRUDSerializer(many=True, allow_null=True)
phones = ContactPhonesSerializer(read_only=True, many=True)
@ -306,17 +367,3 @@ class EstablishmentFavoritesCreateSerializer(serializers.ModelSerializer):
})
return super().create(validated_data)
class EstablishmentTagListSerializer(serializers.ModelSerializer):
"""List establishment tag serializer."""
id = serializers.IntegerField(source='metadata.id')
label_translated = serializers.CharField(
source='metadata.label_translated', read_only=True, allow_null=True)
class Meta:
"""Meta class."""
model = MetaDataContent
fields = [
'id',
'label_translated',
]

View File

@ -14,6 +14,7 @@ urlpatterns = [
name='schedule-rud'),
path('<int:pk>/schedule/', views.EstablishmentScheduleCreateView.as_view(),
name='schedule-create'),
path('attach-tag/', views.EstablishmentTagCreateView.as_view(), name='attach-tag'),
path('menus/', views.MenuListCreateView.as_view(), name='menu-list'),
path('menus/<int:pk>/', views.MenuRUDView.as_view(), name='menu-rud'),
path('plates/', views.PlateListCreateView.as_view(), name='plates'),
@ -26,4 +27,13 @@ urlpatterns = [
path('emails/<int:pk>/', views.EmailRUDView.as_view(), name='emails-rud'),
path('employees/', views.EmployeeListCreateView.as_view(), name='employees'),
path('employees/<int:pk>/', views.EmployeeRUDView.as_view(), name='employees-rud'),
]
path('types/', views.EstablishmentTypeListCreateView.as_view(), name='type-list'),
path('types/tags/', views.EstablishmentTypeTagListView.as_view(), name='type-tag-list'),
path('types/<int:pk>/', views.EstablishmentTypeRUDView.as_view(), name='type-rud'),
path('types/attach-tag-category/', views.EstablishmentTypeAttachTagCategoryView.as_view(),
name='type-attach-tag-category'),
path('subtypes/', views.EstablishmentSubtypeListCreateView.as_view(), name='subtype-list'),
path('subtypes/<int:pk>/', views.EstablishmentSubtypeRUDView.as_view(), name='subtype-rud'),
path('subtypes/attach-tag-category/', views.EstablishmentSubTypeAttachTagCategoryView.as_view(),
name='subtype-attach-tag-category'),
]

View File

@ -7,7 +7,6 @@ app_name = 'establishment'
urlpatterns = [
path('', views.EstablishmentListView.as_view(), name='list'),
path('tags/', views.EstablishmentTagListView.as_view(), name='tags'),
path('recent-reviews/', views.EstablishmentRecentReviewListView.as_view(),
name='recent-reviews'),
path('slug/<slug:slug>/', views.EstablishmentRetrieveView.as_view(), name='detail'),

View File

@ -4,4 +4,4 @@ from establishment.urls.common import urlpatterns as common_urlpatterns
urlpatterns = []
urlpatterns.extend(common_urlpatterns)
urlpatterns.extend(common_urlpatterns)

View File

@ -1,9 +1,11 @@
"""Establishment app views."""
from django.shortcuts import get_object_or_404
from rest_framework import generics, status, permissions
from rest_framework import generics
from establishment import models
from establishment import serializers
from establishment import models, serializers
from rest_framework.response import Response
from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer
from establishment.filters import EstablishmentTypeTagFilter
class EstablishmentMixinViews:
@ -25,6 +27,53 @@ class EstablishmentRUDView(generics.RetrieveUpdateDestroyAPIView):
serializer_class = serializers.EstablishmentRUDSerializer
class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView):
"""Establishment schedule RUD view"""
serializer_class = ScheduleRUDSerializer
def get_object(self):
"""
Returns the object the view is displaying.
"""
establishment_pk = self.kwargs['pk']
schedule_id = self.kwargs['schedule_id']
establishment = get_object_or_404(klass=models.Establishment.objects.all(),
pk=establishment_pk)
schedule = get_object_or_404(klass=establishment.schedule,
id=schedule_id)
# May raise a permission denied
self.check_object_permissions(self.request, establishment)
self.check_object_permissions(self.request, schedule)
return schedule
class EstablishmentScheduleCreateView(generics.CreateAPIView):
"""Establishment schedule Create view"""
serializer_class = ScheduleCreateSerializer
class EstablishmentTypeAttachTagCategoryView(generics.CreateAPIView):
"""Attach tag category to establishment type."""
serializer_class = serializers.EstablishmentTypeTagCategoryBaseSerializer
def get_queryset(self):
"""Override get_queryset method."""
return models.EstablishmentTypeTagCategory.objects.with_base_related()
def post(self, request, *args, **kwargs):
"""Overridden post-method."""
super(EstablishmentTypeAttachTagCategoryView, self).post(request)
return Response(status=status.HTTP_200_OK)
class EstablishmentTagCreateView(EstablishmentMixinViews, generics.CreateAPIView):
"""Attach tag to establishment."""
serializer_class = serializers.EstablishmentTagCreateSerializer
class MenuListCreateView(generics.ListCreateAPIView):
"""Menu list create view."""
serializer_class = serializers.MenuSerializers
@ -100,3 +149,52 @@ class EmployeeRUDView(generics.RetrieveUpdateDestroyAPIView):
"""Social RUD view."""
serializer_class = serializers.EmployeeBackSerializers
queryset = models.Employee.objects.all()
class EstablishmentTypeListCreateView(generics.ListCreateAPIView):
"""Establishment type list/create view."""
serializer_class = serializers.EstablishmentTypeBaseSerializer
queryset = models.EstablishmentType.objects.all()
pagination_class = None
class EstablishmentTypeRUDView(generics.RetrieveUpdateDestroyAPIView):
"""Establishment type retrieve/update/destroy view."""
serializer_class = serializers.EstablishmentTypeBaseSerializer
queryset = models.EstablishmentType.objects.all()
class EstablishmentTypeTagListView(generics.ListAPIView):
"""List of tags with categories by establishment type."""
serializer_class = serializers.EstablishmentTagsByType
queryset = models.EstablishmentType.objects.with_base_related()
filter_class = EstablishmentTypeTagFilter
permission_classes = (permissions.AllowAny, )
pagination_class = None
class EstablishmentSubtypeListCreateView(generics.ListCreateAPIView):
"""Establishment subtype list/create view."""
serializer_class = serializers.EstablishmentSubTypeBaseSerializer
queryset = models.EstablishmentSubType.objects.all()
pagination_class = None
class EstablishmentSubtypeRUDView(generics.RetrieveUpdateDestroyAPIView):
"""Establishment subtype retrieve/update/destroy view."""
serializer_class = serializers.EstablishmentSubTypeBaseSerializer
queryset = models.EstablishmentSubType.objects.all()
class EstablishmentSubTypeAttachTagCategoryView(generics.CreateAPIView):
"""Attach tag category to establishment subtype."""
serializer_class = serializers.EstablishmentSubTypeTagCategoryBaseSerializer
def get_queryset(self):
"""Override get_queryset method."""
return models.EstablishmentSubTypeTagCategory.objects.with_base_related()
def post(self, request, *args, **kwargs):
"""Overridden post-method."""
super(EstablishmentSubTypeAttachTagCategoryView, self).post(request)
return Response(status=status.HTTP_200_OK)

View File

@ -9,7 +9,6 @@ from establishment import filters
from establishment import models, serializers
from main import methods
from main.models import MetaDataContent
from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer
from utils.pagination import EstablishmentPortionPagination
@ -19,9 +18,10 @@ class EstablishmentMixinView:
permission_classes = (permissions.AllowAny,)
def get_queryset(self):
"""Overrided method 'get_queryset'."""
return models.Establishment.objects.published().with_base_related().\
annotate_in_favorites(self.request.user)
"""Overridden method 'get_queryset'."""
return models.Establishment.objects.published() \
.with_base_related() \
.annotate_in_favorites(self.request.user)
class EstablishmentListView(EstablishmentMixinView, generics.ListAPIView):
@ -84,7 +84,7 @@ class EstablishmentTypeListView(generics.ListAPIView):
"""Resource for getting a list of establishment types."""
permission_classes = (permissions.AllowAny,)
serializer_class = serializers.EstablishmentTypeSerializer
serializer_class = serializers.EstablishmentTypeBaseSerializer
queryset = models.EstablishmentType.objects.all()
@ -174,44 +174,3 @@ class EstablishmentNearestRetrieveView(EstablishmentListView, generics.ListAPIVi
return qs.by_distance_from_point(**{k: v for k, v in filter_kwargs.items()
if v is not None})
return qs
class EstablishmentTagListView(generics.ListAPIView):
"""List view for establishment tags."""
serializer_class = serializers.EstablishmentTagListSerializer
permission_classes = (permissions.AllowAny,)
pagination_class = None
def get_queryset(self):
"""Override get_queryset method"""
return MetaDataContent.objects.by_content_type(app_label='establishment',
model='establishment')\
.distinct('metadata__label')
class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView):
"""Establishment schedule RUD view"""
serializer_class = ScheduleRUDSerializer
def get_object(self):
"""
Returns the object the view is displaying.
"""
establishment_pk = self.kwargs['pk']
schedule_id = self.kwargs['schedule_id']
establishment = get_object_or_404(klass=models.Establishment.objects.all(),
pk=establishment_pk)
schedule = get_object_or_404(klass=establishment.schedule,
id=schedule_id)
# May raise a permission denied
self.check_object_permissions(self.request, establishment)
self.check_object_permissions(self.request, schedule)
return schedule
class EstablishmentScheduleCreateView(generics.CreateAPIView):
"""Establishment schedule Create view"""
serializer_class = ScheduleCreateSerializer

View File

@ -25,22 +25,6 @@ class AwardAdmin(admin.ModelAdmin):
# list_display_links = ['id', '__str__']
@admin.register(models.MetaData)
class MetaDataAdmin(admin.ModelAdmin):
"""MetaData admin."""
@admin.register(models.MetaDataCategory)
class MetaDataCategoryAdmin(admin.ModelAdmin):
"""MetaData admin."""
list_display = ['id', 'country', 'content_type']
@admin.register(models.MetaDataContent)
class MetaDataContentAdmin(admin.ModelAdmin):
"""MetaDataContent admin"""
@admin.register(models.Currency)
class CurrencContentAdmin(admin.ModelAdmin):
"""CurrencContent admin"""

View File

@ -0,0 +1,24 @@
# Generated by Django 2.2.4 on 2019-10-09 14:08
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tag', '0002_auto_20191009_1408'),
('news', '0020_remove_news_author'),
]
operations = [
migrations.AddField(
model_name='news',
name='tags',
field=models.ManyToManyField(related_name='news', to='tag.Tag', verbose_name='Tags'),
),
migrations.AddField(
model_name='newstype',
name='tag_categories',
field=models.ManyToManyField(related_name='news_types', to='tag.TagCategory'),
),
]

View File

@ -12,6 +12,8 @@ class NewsType(models.Model):
"""NewsType model."""
name = models.CharField(_('name'), max_length=250)
tag_categories = models.ManyToManyField('tag.TagCategory',
related_name='news_types')
class Meta:
"""Meta class."""
@ -133,8 +135,8 @@ class News(BaseAttributes, TranslatedFieldsMixin):
country = models.ForeignKey('location.Country', blank=True, null=True,
on_delete=models.SET_NULL,
verbose_name=_('country'))
tags = generic.GenericRelation(to='main.MetaDataContent')
tags = models.ManyToManyField('tag.Tag', related_name='news',
verbose_name=_('Tags'))
gallery = models.ManyToManyField('gallery.Image', through='news.NewsGallery')
ratings = generic.GenericRelation(Rating)

View File

@ -6,8 +6,8 @@ from account.serializers.common import UserBaseSerializer
from gallery.models import Image
from location import models as location_models
from location.serializers import CountrySimpleSerializer
from main.serializers import MetaDataContentSerializer
from news import models
from tag.serializers import TagBaseSerializer
from utils.serializers import TranslatedField, ProjectModelSerializer
@ -100,7 +100,7 @@ class NewsBaseSerializer(ProjectModelSerializer):
# related fields
news_type = NewsTypeSerializer(read_only=True)
tags = MetaDataContentSerializer(read_only=True, many=True)
tags = TagBaseSerializer(read_only=True, many=True)
gallery = NewsImageSerializer(read_only=True, many=True)
class Meta:

View File

@ -7,5 +7,7 @@ app_name = 'news'
urlpatterns = [
path('', views.NewsListView.as_view(), name='list'),
path('types/', views.NewsTypeListView.as_view(), name='type'),
path('types/<int:pk>/tags/', views.NewsTypeTagsView.as_view(),
name='type-tags'),
path('slug/<slug:slug>/', views.NewsDetailView.as_view(), name='rud'),
]

View File

@ -1,4 +1,6 @@
"""News app views."""
from django.shortcuts import get_object_or_404
from rest_framework import generics, permissions
from django.conf import settings
from django.db.transaction import on_commit
from django.shortcuts import get_object_or_404
@ -8,6 +10,8 @@ from rest_framework.response import Response
from gallery.tasks import delete_image
from news import filters, models, serializers
from rating.tasks import add_rating
from tag.serializers import TagCategoryDetailSerializer
class NewsMixinView:
"""News mixin."""
@ -40,6 +44,7 @@ class NewsDetailView(NewsMixinView, generics.RetrieveAPIView):
"""Override get_queryset method."""
return super().get_queryset().with_extended_related()
class NewsTypeListView(generics.ListAPIView):
"""NewsType list view."""
@ -49,6 +54,18 @@ class NewsTypeListView(generics.ListAPIView):
serializer_class = serializers.NewsTypeSerializer
class NewsTypeTagsView(generics.ListAPIView):
"""Resource to get a list of tags for a news type."""
pagination_class = None
permission_classes = (permissions.AllowAny, )
serializer_class = TagCategoryDetailSerializer
def get_queryset(self):
news_type = get_object_or_404(models.NewsType, pk=self.kwargs.get('pk'))
return news_type.tag_categories.with_related()
class NewsBackOfficeMixinView:
"""News back office mixin view."""

View File

@ -21,24 +21,29 @@ class EstablishmentDocument(Document):
properties={
'id': fields.IntegerField(),
'name': fields.ObjectField(attr='name_indexing',
properties=OBJECT_FIELD_PROPERTIES)
properties=OBJECT_FIELD_PROPERTIES),
'tag_categories': fields.ObjectField(properties={
'tag_category': fields.ObjectField(
properties={
'id': fields.IntegerField()
}
)
}),
})
establishment_subtypes = fields.ObjectField(
properties={
'id': fields.IntegerField(),
'name': fields.ObjectField(attr='name_indexing',
properties=OBJECT_FIELD_PROPERTIES)
properties={
'id': fields.IntegerField(),
}),
},
multi=True)
tags = fields.ObjectField(
properties={
'id': fields.IntegerField(attr='metadata.id'),
'label': fields.ObjectField(attr='metadata.label_indexing',
properties=OBJECT_FIELD_PROPERTIES),
'category': fields.ObjectField(attr='metadata.category',
properties={
'id': fields.IntegerField(),
})
'tag': fields.ObjectField(properties={
'id': fields.IntegerField(),
}),
},
multi=True)
address = fields.ObjectField(

0
apps/tag/__init__.py Normal file
View File

12
apps/tag/admin.py Normal file
View File

@ -0,0 +1,12 @@
from django.contrib import admin
from .models import Tag, TagCategory
@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
"""Admin model for model Tag."""
@admin.register(TagCategory)
class TagCategoryAdmin(admin.ModelAdmin):
"""Admin model for model TagCategory."""

7
apps/tag/apps.py Normal file
View File

@ -0,0 +1,7 @@
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class TagConfig(AppConfig):
name = 'tag'
verbose_name = _('tag')

View File

@ -0,0 +1,44 @@
# Generated by Django 2.2.4 on 2019-10-09 07:15
from django.db import migrations, models
import django.db.models.deletion
import utils.models
class Migration(migrations.Migration):
initial = True
dependencies = [
('location', '0010_auto_20190904_0711'),
]
operations = [
migrations.CreateModel(
name='TagCategory',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('label', utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='label')),
('public', models.BooleanField(default=False)),
('country', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='location.Country')),
],
options={
'verbose_name': 'tag category',
'verbose_name_plural': 'tag categories',
},
bases=(utils.models.TranslatedFieldsMixin, models.Model),
),
migrations.CreateModel(
name='Tag',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('label', utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='label')),
('category', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tags', to='tag.TagCategory', verbose_name='category')),
],
options={
'verbose_name': 'tag',
'verbose_name_plural': 'tags',
},
bases=(utils.models.TranslatedFieldsMixin, models.Model),
),
]

View File

@ -0,0 +1,27 @@
# Generated by Django 2.2.4 on 2019-10-09 14:08
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('tag', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='tag',
options={'verbose_name': 'Tag', 'verbose_name_plural': 'Tags'},
),
migrations.AlterModelOptions(
name='tagcategory',
options={'verbose_name': 'Tag category', 'verbose_name_plural': 'Tag categories'},
),
migrations.AlterField(
model_name='tag',
name='category',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='tags', to='tag.TagCategory', verbose_name='Category'),
),
]

View File

66
apps/tag/models.py Normal file
View File

@ -0,0 +1,66 @@
"""Tag app models."""
from django.db import models
from django.utils.translation import gettext_lazy as _
from configuration.models import TranslationSettings
from utils.models import TJSONField, TranslatedFieldsMixin
class Tag(TranslatedFieldsMixin, models.Model):
"""Tag model."""
label = TJSONField(blank=True, null=True, default=None,
verbose_name=_('label'),
help_text='{"en-GB":"some text"}')
category = models.ForeignKey('TagCategory', on_delete=models.PROTECT,
null=True, related_name='tags',
verbose_name=_('Category'))
class Meta:
"""Meta class."""
verbose_name = _('Tag')
verbose_name_plural = _('Tags')
def __str__(self):
label = 'None'
lang = TranslationSettings.get_solo().default_language
if self.label and lang in self.label:
label = self.label[lang]
return f'id:{self.id}-{label}'
class TagCategoryQuerySet(models.QuerySet):
"""Extended queryset for TagCategory model."""
def by_news_type(self, news_type):
return self.filter(news_types=news_type)
def with_related(self):
return self.select_related('country').prefetch_related('tags')
class TagCategory(TranslatedFieldsMixin, models.Model):
"""Tag base category model."""
label = TJSONField(blank=True, null=True, default=None,
verbose_name=_('label'),
help_text='{"en-GB":"some text"}')
country = models.ForeignKey('location.Country',
on_delete=models.SET_NULL, null=True,
default=None)
public = models.BooleanField(default=False)
objects = TagCategoryQuerySet.as_manager()
class Meta:
"""Meta class."""
verbose_name = _('Tag category')
verbose_name_plural = _('Tag categories')
def __str__(self):
label = 'None'
lang = TranslationSettings.get_solo().default_language
if self.label and lang in self.label:
label = self.label[lang]
return f'id:{self.id}-{label}'

59
apps/tag/serializers.py Normal file
View File

@ -0,0 +1,59 @@
"""Tag serializers."""
from rest_framework import serializers
from tag import models
from utils.serializers import TranslatedField
class TagBaseSerializer(serializers.ModelSerializer):
"""Serializer for model Tag."""
label_translated = TranslatedField()
class Meta:
"""Meta class."""
model = models.Tag
fields = [
'id',
'label',
'label_translated',
'category'
]
extra_kwargs = {
'label': {'write_only': True},
'category': {'write_only': True}
}
class TagCategoryBaseSerializer(serializers.ModelSerializer):
"""Serializer for model TagCategory."""
label_translated = TranslatedField()
country_translated = TranslatedField(source='country.name_translated')
class Meta:
"""Meta class."""
model = models.TagCategory
fields = (
'id',
'label',
'label_translated',
'country',
'country_translated',
'public',
)
extra_kwargs = {
'label': {'write_only': True},
'country': {'write_only': True},
}
class TagCategoryDetailSerializer(TagCategoryBaseSerializer):
tags = TagBaseSerializer(many=True)
class Meta(TagCategoryBaseSerializer.Meta):
"""Meta class."""
fields = TagCategoryBaseSerializer.Meta.fields + ('tags', )

3
apps/tag/tests.py Normal file
View File

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

10
apps/tag/urls.py Normal file
View File

@ -0,0 +1,10 @@
"""Urlconf for app tag."""
from django.urls import path
from . import views
app_name = 'tag'
urlpatterns = [
path('', views.TagListCreateView.as_view(), name='list-create'),
path('category/', views.TagCategoryListCreateView.as_view(), name='category-list-create'),
]

19
apps/tag/views.py Normal file
View File

@ -0,0 +1,19 @@
"""Tag views."""
from rest_framework import generics
from tag import serializers, models
class TagListCreateView(generics.ListCreateAPIView):
"""List/create tag view."""
queryset = models.Tag.objects.all()
serializer_class = serializers.TagBaseSerializer
pagination_class = None
class TagCategoryListCreateView(generics.ListCreateAPIView):
"""List/create tag category view."""
queryset = models.TagCategory.objects.all()
serializer_class = serializers.TagCategoryBaseSerializer
pagination_class = None

View File

@ -73,6 +73,7 @@ PROJECT_APPS = [
'comment.apps.CommentConfig',
'favorites.apps.FavoritesConfig',
'rating.apps.RatingConfig',
'tag.apps.TagConfig',
]
EXTERNAL_APPS = [

View File

@ -3,11 +3,11 @@ from django.urls import path, include
app_name = 'back'
urlpatterns = [
path('gallery/', include(('gallery.urls', 'gallery'),
namespace='gallery')),
path('gallery/', include(('gallery.urls', 'gallery'), namespace='gallery')),
path('establishments/', include('establishment.urls.back')),
path('location/', include('location.urls.back')),
path('news/', include('news.urls.back')),
path('tags/', include(('tag.urls', 'tag'), namespace='tag'))
path('account/', include('account.urls.back')),
path('comment/', include('comment.urls.back')),
]
]

View File

@ -24,7 +24,8 @@ urlpatterns = [
path('collections/', include('collection.urls.web')),
path('establishments/', include('establishment.urls.web')),
path('news/', include('news.urls.web')),
path('notifications/', include(('notification.urls.web', "notification"), namespace='notification')),
path('notifications/', include(('notification.urls.web', "notification"),
namespace='notification')),
path('partner/', include('partner.urls.web')),
path('location/', include('location.urls.web')),
path('main/', include('main.urls')),