diff --git a/apps/account/views/web.py b/apps/account/views/web.py index 897c955e..9f2ebcfd 100644 --- a/apps/account/views/web.py +++ b/apps/account/views/web.py @@ -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') diff --git a/apps/establishment/admin.py b/apps/establishment/admin.py index f95dd5c8..1f200f41 100644 --- a/apps/establishment/admin.py +++ b/apps/establishment/admin.py @@ -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') \ No newline at end of file + 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.""" diff --git a/apps/establishment/filters.py b/apps/establishment/filters.py index 51b207dc..13d951b6 100644 --- a/apps/establishment/filters.py +++ b/apps/establishment/filters.py @@ -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', + ) diff --git a/apps/establishment/migrations/0032_establishmenttag_establishmenttypetagcategory.py b/apps/establishment/migrations/0032_establishmenttag_establishmenttypetagcategory.py new file mode 100644 index 00000000..ec9966d8 --- /dev/null +++ b/apps/establishment/migrations/0032_establishmenttag_establishmenttypetagcategory.py @@ -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', + }, + ), + ] diff --git a/apps/establishment/migrations/0033_auto_20191009_0715.py b/apps/establishment/migrations/0033_auto_20191009_0715.py new file mode 100644 index 00000000..5df367d6 --- /dev/null +++ b/apps/establishment/migrations/0033_auto_20191009_0715.py @@ -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'), + ), + ] diff --git a/apps/establishment/migrations/0034_merge_20191009_1457.py b/apps/establishment/migrations/0034_merge_20191009_1457.py new file mode 100644 index 00000000..945860f7 --- /dev/null +++ b/apps/establishment/migrations/0034_merge_20191009_1457.py @@ -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 = [ + ] diff --git a/apps/establishment/migrations/0035_establishmentsubtypetagcategory.py b/apps/establishment/migrations/0035_establishmentsubtypetagcategory.py new file mode 100644 index 00000000..6a85fca7 --- /dev/null +++ b/apps/establishment/migrations/0035_establishmentsubtypetagcategory.py @@ -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', + }, + ), + ] diff --git a/apps/establishment/migrations/0036_auto_20191011_1356.py b/apps/establishment/migrations/0036_auto_20191011_1356.py new file mode 100644 index 00000000..c2eb2e4e --- /dev/null +++ b/apps/establishment/migrations/0036_auto_20191011_1356.py @@ -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'), + ), + ] diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 4e083634..e494cf94 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -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') diff --git a/apps/establishment/serializers/back.py b/apps/establishment/serializers/back.py index 8bd09e85..21f63e17 100644 --- a/apps/establishment/serializers/back.py +++ b/apps/establishment/serializers/back.py @@ -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', + ] diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index f09c8200..1789da31 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -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', - ] diff --git a/apps/establishment/urls/back.py b/apps/establishment/urls/back.py index dca5fb55..3db81dce 100644 --- a/apps/establishment/urls/back.py +++ b/apps/establishment/urls/back.py @@ -14,6 +14,7 @@ urlpatterns = [ name='schedule-rud'), path('/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//', views.MenuRUDView.as_view(), name='menu-rud'), path('plates/', views.PlateListCreateView.as_view(), name='plates'), @@ -26,4 +27,13 @@ urlpatterns = [ path('emails//', views.EmailRUDView.as_view(), name='emails-rud'), path('employees/', views.EmployeeListCreateView.as_view(), name='employees'), path('employees//', views.EmployeeRUDView.as_view(), name='employees-rud'), -] \ No newline at end of file + path('types/', views.EstablishmentTypeListCreateView.as_view(), name='type-list'), + path('types/tags/', views.EstablishmentTypeTagListView.as_view(), name='type-tag-list'), + path('types//', 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//', views.EstablishmentSubtypeRUDView.as_view(), name='subtype-rud'), + path('subtypes/attach-tag-category/', views.EstablishmentSubTypeAttachTagCategoryView.as_view(), + name='subtype-attach-tag-category'), +] diff --git a/apps/establishment/urls/common.py b/apps/establishment/urls/common.py index 1e9225d6..5d7df146 100644 --- a/apps/establishment/urls/common.py +++ b/apps/establishment/urls/common.py @@ -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//', views.EstablishmentRetrieveView.as_view(), name='detail'), diff --git a/apps/establishment/urls/web.py b/apps/establishment/urls/web.py index b732d171..b4d1942d 100644 --- a/apps/establishment/urls/web.py +++ b/apps/establishment/urls/web.py @@ -4,4 +4,4 @@ from establishment.urls.common import urlpatterns as common_urlpatterns urlpatterns = [] -urlpatterns.extend(common_urlpatterns) \ No newline at end of file +urlpatterns.extend(common_urlpatterns) diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index 5cba8255..543dbd3e 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -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) diff --git a/apps/establishment/views/web.py b/apps/establishment/views/web.py index 8f5d2a26..f164ec73 100644 --- a/apps/establishment/views/web.py +++ b/apps/establishment/views/web.py @@ -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 diff --git a/apps/main/admin.py b/apps/main/admin.py index bdbfe46e..f14a3470 100644 --- a/apps/main/admin.py +++ b/apps/main/admin.py @@ -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""" diff --git a/apps/news/migrations/0021_auto_20191009_1408.py b/apps/news/migrations/0021_auto_20191009_1408.py new file mode 100644 index 00000000..81a4d7fa --- /dev/null +++ b/apps/news/migrations/0021_auto_20191009_1408.py @@ -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'), + ), + ] diff --git a/apps/news/models.py b/apps/news/models.py index 4081a3a2..ed1d3a7a 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -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) diff --git a/apps/news/serializers.py b/apps/news/serializers.py index 6c4db75d..25590f02 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -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: diff --git a/apps/news/urls/web.py b/apps/news/urls/web.py index 80fcf072..0671f5f6 100644 --- a/apps/news/urls/web.py +++ b/apps/news/urls/web.py @@ -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//tags/', views.NewsTypeTagsView.as_view(), + name='type-tags'), path('slug//', views.NewsDetailView.as_view(), name='rud'), ] diff --git a/apps/news/views.py b/apps/news/views.py index aefecd94..601654b6 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -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.""" diff --git a/apps/search_indexes/documents/establishment.py b/apps/search_indexes/documents/establishment.py index 2d43154e..e2147fbc 100644 --- a/apps/search_indexes/documents/establishment.py +++ b/apps/search_indexes/documents/establishment.py @@ -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( diff --git a/apps/tag/__init__.py b/apps/tag/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/tag/admin.py b/apps/tag/admin.py new file mode 100644 index 00000000..ea7f9394 --- /dev/null +++ b/apps/tag/admin.py @@ -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.""" diff --git a/apps/tag/apps.py b/apps/tag/apps.py new file mode 100644 index 00000000..a1cce249 --- /dev/null +++ b/apps/tag/apps.py @@ -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') diff --git a/apps/tag/migrations/0001_initial.py b/apps/tag/migrations/0001_initial.py new file mode 100644 index 00000000..543eb035 --- /dev/null +++ b/apps/tag/migrations/0001_initial.py @@ -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), + ), + ] diff --git a/apps/tag/migrations/0002_auto_20191009_1408.py b/apps/tag/migrations/0002_auto_20191009_1408.py new file mode 100644 index 00000000..472d9596 --- /dev/null +++ b/apps/tag/migrations/0002_auto_20191009_1408.py @@ -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'), + ), + ] diff --git a/apps/tag/migrations/__init__.py b/apps/tag/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/tag/models.py b/apps/tag/models.py new file mode 100644 index 00000000..b552bbcb --- /dev/null +++ b/apps/tag/models.py @@ -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}' diff --git a/apps/tag/serializers.py b/apps/tag/serializers.py new file mode 100644 index 00000000..ecb1b68d --- /dev/null +++ b/apps/tag/serializers.py @@ -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', ) diff --git a/apps/tag/tests.py b/apps/tag/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/apps/tag/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/tag/urls.py b/apps/tag/urls.py new file mode 100644 index 00000000..0d0b86d2 --- /dev/null +++ b/apps/tag/urls.py @@ -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'), +] diff --git a/apps/tag/views.py b/apps/tag/views.py new file mode 100644 index 00000000..5bf4d045 --- /dev/null +++ b/apps/tag/views.py @@ -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 diff --git a/project/settings/base.py b/project/settings/base.py index ae4b0201..033a4917 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -73,6 +73,7 @@ PROJECT_APPS = [ 'comment.apps.CommentConfig', 'favorites.apps.FavoritesConfig', 'rating.apps.RatingConfig', + 'tag.apps.TagConfig', ] EXTERNAL_APPS = [ diff --git a/project/urls/back.py b/project/urls/back.py index 59758c66..7cfcd038 100644 --- a/project/urls/back.py +++ b/project/urls/back.py @@ -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')), -] \ No newline at end of file +] diff --git a/project/urls/web.py b/project/urls/web.py index 59c08a6f..89debab2 100644 --- a/project/urls/web.py +++ b/project/urls/web.py @@ -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')),