From 074a5ec5ea7be3450ec89b79c7b278c8a6a09464 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Wed, 9 Oct 2019 18:25:40 +0300 Subject: [PATCH] refactor news tags --- .../migrations/0021_auto_20191009_1408.py | 24 ++++++++++ apps/news/models.py | 7 +-- apps/news/serializers.py | 4 +- apps/news/urls/web.py | 2 + apps/news/views.py | 16 +++++++ .../tag/migrations/0002_auto_20191009_1408.py | 27 +++++++++++ apps/tag/models.py | 47 ++++++++++++------- apps/tag/serializers.py | 21 +++++++-- apps/tag/views.py | 5 +- project/urls/back.py | 6 +-- project/urls/web.py | 3 +- 11 files changed, 131 insertions(+), 31 deletions(-) create mode 100644 apps/news/migrations/0021_auto_20191009_1408.py create mode 100644 apps/tag/migrations/0002_auto_20191009_1408.py 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 6e91b912..8a6a89f4 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')) ratings = generic.GenericRelation(Rating) objects = NewsQuerySet.as_manager() @@ -163,4 +165,3 @@ class News(BaseAttributes, TranslatedFieldsMixin): @property def same_theme(self): return self.__class__.objects.same_theme(self)[:3] - diff --git a/apps/news/serializers.py b/apps/news/serializers.py index c473be1d..6f0b73b6 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -3,8 +3,8 @@ from rest_framework import serializers from account.serializers.common import UserBaseSerializer 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 @@ -27,7 +27,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) class Meta: """Meta class.""" 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 61a57251..1a845a7b 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -1,7 +1,10 @@ """News app views.""" +from django.shortcuts import get_object_or_404 from rest_framework import generics, permissions from news import filters, models, serializers from rating.tasks import add_rating +from tag.serializers import TagCategoryDetailSerializer + class NewsMixinView: """News mixin.""" @@ -34,6 +37,7 @@ class NewsDetailView(NewsMixinView, generics.RetrieveAPIView): """Override get_queryset method.""" return super().get_queryset().with_extended_related() + class NewsTypeListView(generics.ListAPIView): """NewsType list view.""" @@ -43,6 +47,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/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/models.py b/apps/tag/models.py index c269d64e..b552bbcb 100644 --- a/apps/tag/models.py +++ b/apps/tag/models.py @@ -1,24 +1,25 @@ """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') + + 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: - verbose_name = _('tag') - verbose_name_plural = _('tags') + """Meta class.""" + + verbose_name = _('Tag') + verbose_name_plural = _('Tags') def __str__(self): label = 'None' @@ -28,20 +29,34 @@ class Tag(TranslatedFieldsMixin, models.Model): 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( - _('label'), null=True, blank=True, - default=None, help_text='{"en-GB":"some text"}') + + 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: - verbose_name = _('tag category') - verbose_name_plural = _('tag categories') + """Meta class.""" + + verbose_name = _('Tag category') + verbose_name_plural = _('Tag categories') def __str__(self): label = 'None' diff --git a/apps/tag/serializers.py b/apps/tag/serializers.py index cf783910..ecb1b68d 100644 --- a/apps/tag/serializers.py +++ b/apps/tag/serializers.py @@ -1,14 +1,17 @@ """Tag serializers.""" from rest_framework import serializers -from . import models +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', @@ -24,21 +27,33 @@ class TagBaseSerializer(serializers.ModelSerializer): 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 = [ + 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/views.py b/apps/tag/views.py index ab944ba7..5bf4d045 100644 --- a/apps/tag/views.py +++ b/apps/tag/views.py @@ -1,11 +1,11 @@ """Tag views.""" from rest_framework import generics - -from . import serializers, models +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 @@ -13,6 +13,7 @@ class TagListCreateView(generics.ListCreateAPIView): 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/urls/back.py b/project/urls/back.py index 2a9657db..77e573d0 100644 --- a/project/urls/back.py +++ b/project/urls/back.py @@ -3,11 +3,9 @@ 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('tags/', include(('tag.urls', 'tag'), namespace='tag')) ] diff --git a/project/urls/web.py b/project/urls/web.py index 5bf538f3..a4e68317 100644 --- a/project/urls/web.py +++ b/project/urls/web.py @@ -23,7 +23,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')),