From d321866dc21b45eb2205930cd4a01098f7f3e132 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Tue, 8 Oct 2019 15:38:35 +0300 Subject: [PATCH] gm-192: in progress --- apps/account/views/web.py | 3 +- apps/establishment/admin.py | 22 +++-- ...ishmenttag_establishmenttypetagcategory.py | 39 ++++++++ apps/establishment/models.py | 62 +++++++++++- apps/establishment/serializers/back.py | 17 ++++ apps/establishment/serializers/common.py | 64 ++++++++----- apps/establishment/urls/back.py | 6 ++ apps/establishment/urls/web.py | 2 +- apps/establishment/views/back.py | 94 ++++++++++++++++++- apps/establishment/views/web.py | 36 +------ apps/main/admin.py | 16 ---- apps/main/models.py | 1 - .../search_indexes/documents/establishment.py | 23 ++--- apps/tag/__init__.py | 0 apps/tag/admin.py | 12 +++ apps/tag/apps.py | 7 ++ apps/tag/migrations/0001_initial.py | 44 +++++++++ apps/tag/migrations/__init__.py | 0 apps/tag/models.py | 51 ++++++++++ apps/tag/serializers.py | 44 +++++++++ apps/tag/tests.py | 3 + apps/tag/urls.py | 10 ++ apps/tag/views.py | 18 ++++ project/settings/base.py | 1 + project/urls/back.py | 6 +- 25 files changed, 477 insertions(+), 104 deletions(-) create mode 100644 apps/establishment/migrations/0032_establishmenttag_establishmenttypetagcategory.py create mode 100644 apps/tag/__init__.py create mode 100644 apps/tag/admin.py create mode 100644 apps/tag/apps.py create mode 100644 apps/tag/migrations/0001_initial.py create mode 100644 apps/tag/migrations/__init__.py create mode 100644 apps/tag/models.py create mode 100644 apps/tag/serializers.py create mode 100644 apps/tag/tests.py create mode 100644 apps/tag/urls.py create mode 100644 apps/tag/views.py 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..a40475b0 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,14 @@ 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 EstablishmentTypeTagCategory(admin.ModelAdmin): + """EstablishmentTypeTagCategory admin.""" + + +@admin.register(models.EstablishmentTag) +class EstablishmentTag(admin.ModelAdmin): + """EstablishmentTag admin.""" diff --git a/apps/establishment/migrations/0032_establishmenttag_establishmenttypetagcategory.py b/apps/establishment/migrations/0032_establishmenttag_establishmenttypetagcategory.py new file mode 100644 index 00000000..6efaa57a --- /dev/null +++ b/apps/establishment/migrations/0032_establishmenttag_establishmenttypetagcategory.py @@ -0,0 +1,39 @@ +# Generated by Django 2.2.4 on 2019-10-08 07:47 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('tag', '0001_initial'), + ('establishment', '0031_establishment_slug'), + ] + + operations = [ + migrations.CreateModel( + name='EstablishmentTypeTagCategory', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('establishment_type', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tag_categories', to='establishment.EstablishmentType', verbose_name='establishment type')), + ('tag_category', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='est_type_tag_categories', to='tag.TagCategory', verbose_name='tag category')), + ], + options={ + 'verbose_name': 'establishment type tag categories', + 'verbose_name_plural': 'establishment type tag categories', + }, + ), + migrations.CreateModel( + name='EstablishmentTag', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('establishment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tags', to='establishment.Establishment', verbose_name='establishment')), + ('tag', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tags', to='tag.Tag', verbose_name='tag')), + ], + options={ + 'verbose_name': 'establishment tag', + 'verbose_name_plural': 'establishment tags', + }, + ), + ] diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 5c2a0ff0..9ce4eebc 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -76,11 +76,13 @@ class EstablishmentQuerySet(models.QuerySet): def with_base_related(self): """Return qs with related objects.""" - return self.select_related('address').prefetch_related( - models.Prefetch('tags', - MetaDataContent.objects.select_related( - 'metadata__category')) - ) + return self.select_related('address') + # todo: fix this + # return self.select_related('address').prefetch_related( + # models.Prefetch('tags', + # MetaDataContent.objects.select_related( + # 'metadata__category')) + # ) def with_extended_related(self): return self.select_related('establishment_type').\ @@ -546,3 +548,53 @@ 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.SET_NULL, null=True, + related_name='tags', + verbose_name=_('tag')) + establishment = models.ForeignKey('establishment.Establishment', + on_delete=models.SET_NULL, null=True, + 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) + + +class EstablishmentTypeTagCategory(models.Model): + """Tag categories based on establishment type.""" + establishment_type = models.ForeignKey(EstablishmentType, + on_delete=models.SET_NULL, null=True, + related_name='tag_categories', + verbose_name=_('establishment type')) + tag_category = models.ForeignKey('tag.TagCategory', + on_delete=models.SET_NULL, null=True, + 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') diff --git a/apps/establishment/serializers/back.py b/apps/establishment/serializers/back.py index d0c70b2f..b15ff51f 100644 --- a/apps/establishment/serializers/back.py +++ b/apps/establishment/serializers/back.py @@ -1,4 +1,5 @@ from rest_framework import serializers +from tag.serializers import TagBaseSerializer from establishment import models from establishment.serializers import ( EstablishmentBaseSerializer, PlateSerializer, ContactEmailsSerializer, @@ -6,6 +7,7 @@ from establishment.serializers import ( EstablishmentTypeSerializer) from utils.decorators import with_base_attributes +from utils.serializers import TranslatedField from main.models import Currency @@ -74,6 +76,21 @@ class EstablishmentRUDSerializer(EstablishmentBaseSerializer): ] +class EstablishmentTagCategoryListSerializer(serializers.ModelSerializer): + """Serializer for intermediate model EstablishmentTypeTagCategories.""" + label_translated = TranslatedField(source='tag_category.label_translated') + tags = TagBaseSerializer(source='tag_category.tags', many=True) + + class Meta: + """Meta class.""" + model = models.EstablishmentTypeTagCategory + fields = [ + 'id', + 'label_translated', + 'tags', + ] + + class SocialNetworkSerializers(serializers.ModelSerializer): """Social network serializers.""" class Meta: diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index f09c8200..6c12360b 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -6,8 +6,8 @@ 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 tag import models as tag_models from review import models as review_models from timetable.serialziers import ScheduleRUDSerializer from utils import exceptions as utils_exceptions @@ -89,25 +89,32 @@ class MenuRUDSerializers(ProjectModelSerializer): class EstablishmentTypeSerializer(serializers.ModelSerializer): """Serializer for EstablishmentType model.""" - name_translated = serializers.CharField(allow_null=True) + name_translated = TranslatedField() class Meta: """Meta class.""" model = models.EstablishmentType - fields = ('id', 'name_translated') + fields = ('id', 'name', 'name_translated') + extra_kwargs = { + 'name': {'write_only': True} + } class EstablishmentSubTypeSerializer(serializers.ModelSerializer): """Serializer for EstablishmentSubType models.""" - name_translated = serializers.CharField(allow_null=True) + name_translated = TranslatedField() class Meta: """Meta class.""" model = models.EstablishmentSubType - fields = ('id', 'name_translated') + fields = ('id', 'name', 'name_translated', 'establishment_type') + extra_kwargs = { + 'name': {'write_only': True}, + 'establishment_type': {'write_only': True} + } class ReviewSerializer(serializers.ModelSerializer): @@ -138,14 +145,42 @@ class EstablishmentEmployeeSerializer(serializers.ModelSerializer): fields = ('id', 'name', 'position_translated', 'awards', 'priority') +class EstablishmentCategoryTagListSerializer(serializers.ModelSerializer): + """Serializer for establishment category tags.""" + label_translated = TranslatedField() + + class Meta: + """Meta class.""" + model = tag_models.TagCategory + fields = [ + 'id', + 'label_translated', + ] + + +class EstablishmentTagListSerializer(serializers.ModelSerializer): + """Serializer for establishment tags.""" + label_translated = TranslatedField(source='tag.label_translated') + category = EstablishmentCategoryTagListSerializer(source='tag.category') + + class Meta: + """Meta class.""" + model = tag_models.Tag + fields = [ + 'id', + 'label_translated', + 'category', + ] + + 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 = EstablishmentTagListSerializer(many=True) class Meta: """Meta class.""" @@ -305,18 +340,3 @@ class EstablishmentFavoritesCreateSerializer(serializers.ModelSerializer): 'content_object': validated_data.pop('establishment') }) 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..b04a6843 100644 --- a/apps/establishment/urls/back.py +++ b/apps/establishment/urls/back.py @@ -14,6 +14,8 @@ urlpatterns = [ name='schedule-rud'), path('/schedule/', views.EstablishmentScheduleCreateView.as_view(), name='schedule-create'), + path('/tags/categories/', views.EstablishmentTagCategoryListView.as_view(), + name='tag-category-list'), 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 +28,8 @@ 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'), + path('types/', views.EstablishmentTypeListCreateView.as_view(), name='type-list'), + path('types//', views.EstablishmentTypeRUDView.as_view(), name='type-rud'), + path('subtypes/', views.EstablishmentSubtypeListCreateView.as_view(), name='subtype-list'), + path('subtypes//', views.EstablishmentSubtypeRUDView.as_view(), name='subtype-rud'), ] \ No newline at end of file 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..64887fb3 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -1,9 +1,9 @@ """Establishment app views.""" - +from django.shortcuts import get_object_or_404 from rest_framework import generics -from establishment import models -from establishment import serializers +from establishment import models, serializers +from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer class EstablishmentMixinViews: @@ -25,6 +25,62 @@ 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 EstablishmentTagCategoryListView(EstablishmentMixinViews, generics.ListAPIView): + """View for establishment tag categories.""" + serializer_class = serializers.EstablishmentTagCategoryListSerializer + pagination_class = None + + def get_object(self): + """ + Returns the object the view is displaying. + """ + queryset = super(EstablishmentTagCategoryListView, self).get_queryset() + + # Perform the lookup filtering. + lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field + + filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]} + obj = get_object_or_404(queryset, **filter_kwargs) + + # May raise a permission denied + self.check_object_permissions(self.request, obj) + + return obj + + def get_queryset(self): + """Overridden get_queryset method.""" + establishment = self.get_object() + return establishment.establishment_type.tag_categories.all() + + class MenuListCreateView(generics.ListCreateAPIView): """Menu list create view.""" serializer_class = serializers.MenuSerializers @@ -100,3 +156,35 @@ 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.EstablishmentTypeSerializer + queryset = models.EstablishmentType.objects.all() + pagination_class = None + + +class EstablishmentTypeRUDView(generics.RetrieveUpdateDestroyAPIView): + """Establishment type retrieve/update/destroy view.""" + serializer_class = serializers.EstablishmentTypeSerializer + queryset = models.EstablishmentType.objects.all() + + +class EstablishmentSubtypeListCreateView(generics.ListCreateAPIView): + """Establishment subtype list/create view.""" + serializer_class = serializers.EstablishmentSubTypeSerializer + queryset = models.EstablishmentSubType.objects.all() + pagination_class = None + + +class EstablishmentSubtypeRUDView(generics.RetrieveUpdateDestroyAPIView): + """Establishment subtype retrieve/update/destroy view.""" + serializer_class = serializers.EstablishmentSubTypeSerializer + queryset = models.EstablishmentSubType.objects.all() + + +class EstablishmentTagListCreateView(generics.ListCreateAPIView): + """Establishment tag list/create view.""" + serializer_class = serializers.EstablishmentTagListSerializer + queryset = models.EstablishmentTag.objects.all() diff --git a/apps/establishment/views/web.py b/apps/establishment/views/web.py index 8f5d2a26..d65ede11 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): @@ -187,31 +187,3 @@ class EstablishmentTagListView(generics.ListAPIView): 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/main/models.py b/apps/main/models.py index fa6cf7d1..df04fd8a 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -353,7 +353,6 @@ class Carousel(models.Model): return self.content_object.establishment_type.name_translated - class Page(models.Model): """Page model.""" diff --git a/apps/search_indexes/documents/establishment.py b/apps/search_indexes/documents/establishment.py index 2d43154e..3a06d4ed 100644 --- a/apps/search_indexes/documents/establishment.py +++ b/apps/search_indexes/documents/establishment.py @@ -30,17 +30,18 @@ class EstablishmentDocument(Document): properties=OBJECT_FIELD_PROPERTIES) }, 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(), - }) - }, - multi=True) + # todo: need to fix + # 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(), + # }) + # }, + # multi=True) address = fields.ObjectField( properties={ 'id': fields.IntegerField(), 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..1c146570 --- /dev/null +++ b/apps/tag/migrations/0001_initial.py @@ -0,0 +1,44 @@ +# Generated by Django 2.2.4 on 2019-10-08 07:47 + +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/__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..c269d64e --- /dev/null +++ b/apps/tag/models.py @@ -0,0 +1,51 @@ +"""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( + _('label'), null=True, blank=True, + default=None, help_text='{"en-GB":"some text"}') + category = models.ForeignKey('TagCategory', + on_delete=models.SET_NULL, null=True, + related_name='tags', + verbose_name='category') + + class Meta: + 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 TagCategory(TranslatedFieldsMixin, models.Model): + """Tag base category model.""" + label = TJSONField( + _('label'), null=True, blank=True, + default=None, 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) + + class Meta: + 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..cf783910 --- /dev/null +++ b/apps/tag/serializers.py @@ -0,0 +1,44 @@ +"""Tag serializers.""" +from rest_framework import serializers +from . import models +from utils.serializers import TranslatedField + + +class TagBaseSerializer(serializers.ModelSerializer): + """Serializer for model Tag.""" + label_translated = TranslatedField() + + class Meta: + 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}, + } 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..ab944ba7 --- /dev/null +++ b/apps/tag/views.py @@ -0,0 +1,18 @@ +"""Tag views.""" +from rest_framework import generics + +from . 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 cec6a4d1..8f02f52d 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -72,6 +72,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 7b4146eb..2a9657db 100644 --- a/project/urls/back.py +++ b/project/urls/back.py @@ -7,5 +7,7 @@ urlpatterns = [ namespace='gallery')), path('establishments/', include('establishment.urls.back')), path('location/', include('location.urls.back')), - path('news/', include('news.urls.back')) -] \ No newline at end of file + path('news/', include('news.urls.back')), + path('tags/', include(('tag.urls', 'tag'), + namespace='tag')) +]