Refactor news
This commit is contained in:
parent
07d17d5bf1
commit
dc4b63d8b2
|
|
@ -1,7 +1,9 @@
|
|||
"""Location app common serializers."""
|
||||
from django.contrib.gis.geos import Point
|
||||
from rest_framework import serializers
|
||||
|
||||
from location import models
|
||||
from utils.serializers import TranslatedField
|
||||
|
||||
|
||||
class CountrySerializer(serializers.ModelSerializer):
|
||||
|
|
@ -20,6 +22,18 @@ class CountrySerializer(serializers.ModelSerializer):
|
|||
]
|
||||
|
||||
|
||||
class CountrySimpleSerializer(serializers.ModelSerializer):
|
||||
"""Simple country serializer."""
|
||||
|
||||
name_translated = TranslatedField()
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
||||
model = models.Country
|
||||
fields = ('id', 'code', 'name_translated')
|
||||
|
||||
|
||||
class RegionSerializer(serializers.ModelSerializer):
|
||||
"""Region serializer"""
|
||||
|
||||
|
|
|
|||
54
apps/news/migrations/0010_auto_20190923_1131.py
Normal file
54
apps/news/migrations/0010_auto_20190923_1131.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# Generated by Django 2.2.4 on 2019-09-23 11:31
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('news', '0009_auto_20190901_1032'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='news',
|
||||
name='author',
|
||||
field=models.CharField(blank=True, default=None, max_length=255, null=True, verbose_name='Author'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='news',
|
||||
name='image_url',
|
||||
field=models.URLField(blank=True, default=None, null=True, verbose_name='Image URL path'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='news',
|
||||
name='preview_image_url',
|
||||
field=models.URLField(blank=True, default=None, null=True, verbose_name='Preview image URL path'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='news',
|
||||
name='address',
|
||||
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='location.Address', verbose_name='address'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='news',
|
||||
name='country',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='location.Country', verbose_name='country'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='news',
|
||||
name='end',
|
||||
field=models.DateTimeField(verbose_name='End'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='news',
|
||||
name='news_type',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='news.NewsType', verbose_name='news type'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='news',
|
||||
name='start',
|
||||
field=models.DateTimeField(verbose_name='Start'),
|
||||
),
|
||||
]
|
||||
27
apps/news/migrations/0011_auto_20190923_1134.py
Normal file
27
apps/news/migrations/0011_auto_20190923_1134.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# Generated by Django 2.2.4 on 2019-09-23 11:34
|
||||
|
||||
from django.db import migrations
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
def copy_image_links(apps, schemaeditor):
|
||||
News = apps.get_model('news', 'News')
|
||||
for news in News.objects.all():
|
||||
if news.image:
|
||||
news.image_url = f'{settings.SCHEMA_URI}://{settings.DOMAIN_URI}{news.image.image.url}'
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('news', '0010_auto_20190923_1131'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(copy_image_links, migrations.RunPython.noop),
|
||||
|
||||
migrations.RemoveField(
|
||||
model_name='news',
|
||||
name='image',
|
||||
),
|
||||
]
|
||||
|
|
@ -1,25 +1,30 @@
|
|||
"""News app models."""
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from utils.models import BaseAttributes, TJSONField, TranslatedFieldsMixin
|
||||
|
||||
|
||||
class NewsType(models.Model):
|
||||
"""NewsType model."""
|
||||
|
||||
name = models.CharField(_('name'), max_length=250)
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
||||
verbose_name_plural = _('news types')
|
||||
verbose_name = _('news type')
|
||||
|
||||
def __str__(self):
|
||||
"""Overrided __str__ method."""
|
||||
return self.name
|
||||
|
||||
|
||||
class NewsQuerySet(models.QuerySet):
|
||||
"""QuerySet for model News"""
|
||||
|
||||
def by_type(self, news_type):
|
||||
"""Filter News by type"""
|
||||
return self.filter(news_type__name=news_type)
|
||||
|
|
@ -30,48 +35,55 @@ class NewsQuerySet(models.QuerySet):
|
|||
|
||||
def published(self):
|
||||
"""Return only published news"""
|
||||
return self.filter(is_publish=True)
|
||||
now = timezone.now()
|
||||
return self.filter(is_publish=True, start__lte=now, end__gte=now)
|
||||
|
||||
def with_related(self):
|
||||
"""Return qs with related objects."""
|
||||
return self.select_related('news_type')
|
||||
|
||||
|
||||
class News(BaseAttributes, TranslatedFieldsMixin):
|
||||
"""News model."""
|
||||
|
||||
image = models.ForeignKey(
|
||||
'gallery.Image', null=True, blank=True, default=None,
|
||||
verbose_name=_('News image'), on_delete=models.CASCADE)
|
||||
news_type = models.ForeignKey(
|
||||
NewsType, verbose_name=_('news type'), on_delete=models.CASCADE)
|
||||
|
||||
title = TJSONField(
|
||||
_('title'), null=True, blank=True,
|
||||
default=None, help_text='{"en-GB":"some text"}')
|
||||
subtitle = TJSONField(
|
||||
_('subtitle'), null=True, blank=True,
|
||||
default=None, help_text='{"en-GB":"some text"}'
|
||||
)
|
||||
description = TJSONField(
|
||||
_('description'), null=True, blank=True,
|
||||
default=None, help_text='{"en-GB":"some text"}'
|
||||
)
|
||||
start = models.DateTimeField(_('start'))
|
||||
end = models.DateTimeField(_('end'))
|
||||
news_type = models.ForeignKey(NewsType, on_delete=models.PROTECT,
|
||||
verbose_name=_('news type'))
|
||||
title = TJSONField(blank=True, null=True, default=None,
|
||||
verbose_name=_('title'),
|
||||
help_text='{"en-GB":"some text"}')
|
||||
subtitle = TJSONField(blank=True, null=True, default=None,
|
||||
verbose_name=_('subtitle'),
|
||||
help_text='{"en-GB":"some text"}')
|
||||
description = TJSONField(blank=True, null=True, default=None,
|
||||
verbose_name=_('description'),
|
||||
help_text='{"en-GB":"some text"}')
|
||||
start = models.DateTimeField(verbose_name=_('Start'))
|
||||
end = models.DateTimeField(verbose_name=_('End'))
|
||||
playlist = models.IntegerField(_('playlist'))
|
||||
address = models.ForeignKey(
|
||||
'location.Address', verbose_name=_('address'), blank=True,
|
||||
null=True, default=None, on_delete=models.CASCADE)
|
||||
is_publish = models.BooleanField(
|
||||
default=False, verbose_name=_('Publish status'))
|
||||
country = models.ForeignKey(
|
||||
'location.Country', blank=True, null=True,
|
||||
verbose_name=_('country'), on_delete=models.CASCADE)
|
||||
is_highlighted = models.BooleanField(
|
||||
default=False, verbose_name=_('Is highlighted'))
|
||||
is_publish = models.BooleanField(default=False,
|
||||
verbose_name=_('Publish status'))
|
||||
author = models.CharField(max_length=255, blank=True, null=True,
|
||||
default=None,verbose_name=_('Author'))
|
||||
is_highlighted = models.BooleanField(default=False,
|
||||
verbose_name=_('Is highlighted'))
|
||||
# TODO: metadata_keys - описание ключей для динамического построения полей метаданных
|
||||
# TODO: metadata_values - Описание значений для динамических полей из MetadataKeys
|
||||
image_url = models.URLField(blank=True, null=True, default=None,
|
||||
verbose_name=_('Image URL path'))
|
||||
preview_image_url = models.URLField(blank=True, null=True, default=None,
|
||||
verbose_name=_('Preview image URL path'))
|
||||
address = models.ForeignKey('location.Address', blank=True, null=True,
|
||||
default=None, verbose_name=_('address'),
|
||||
on_delete=models.SET_NULL)
|
||||
country = models.ForeignKey('location.Country', blank=True, null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name=_('country'))
|
||||
|
||||
objects = NewsQuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
||||
verbose_name = _('news')
|
||||
verbose_name_plural = _('news')
|
||||
|
||||
|
|
|
|||
97
apps/news/serializers.py
Normal file
97
apps/news/serializers.py
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
"""News app common serializers."""
|
||||
from rest_framework import serializers
|
||||
from location import models as location_models
|
||||
from location.serializers import CountrySimpleSerializer
|
||||
from news import models
|
||||
from utils.serializers import TranslatedField
|
||||
|
||||
|
||||
class NewsTypeSerializer(serializers.ModelSerializer):
|
||||
"""News type serializer."""
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
||||
model = models.NewsType
|
||||
fields = ('id', 'name')
|
||||
|
||||
|
||||
class NewsBaseSerializer(serializers.ModelSerializer):
|
||||
"""Base serializer for News model."""
|
||||
|
||||
# read only fields
|
||||
title_translated = TranslatedField()
|
||||
subtitle_translated = TranslatedField()
|
||||
|
||||
# related fields
|
||||
news_type = NewsTypeSerializer(read_only=True)
|
||||
country = CountrySimpleSerializer(read_only=True)
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
||||
model = models.News
|
||||
fields = (
|
||||
'id',
|
||||
'title_translated',
|
||||
'subtitle_translated',
|
||||
'image_url',
|
||||
'image_url',
|
||||
'preview_image_url',
|
||||
'news_type',
|
||||
'country',
|
||||
)
|
||||
|
||||
|
||||
class NewsDetailSerializer(NewsBaseSerializer):
|
||||
"""News detail serializer."""
|
||||
|
||||
description_translated = TranslatedField()
|
||||
|
||||
class Meta(NewsBaseSerializer.Meta):
|
||||
"""Meta class."""
|
||||
|
||||
fields = NewsBaseSerializer.Meta.fields + (
|
||||
'description_translated',
|
||||
'start',
|
||||
'end',
|
||||
'playlist',
|
||||
'is_highlighted',
|
||||
'is_publish',
|
||||
)
|
||||
|
||||
|
||||
class NewsBackOfficeBaseSerializer(NewsBaseSerializer):
|
||||
"""News back office base serializer."""
|
||||
|
||||
class Meta(NewsBaseSerializer.Meta):
|
||||
"""Meta class."""
|
||||
|
||||
fields = NewsBaseSerializer.Meta.fields + (
|
||||
'title',
|
||||
'subtitle',
|
||||
)
|
||||
|
||||
|
||||
class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer,
|
||||
NewsDetailSerializer):
|
||||
"""News detail serializer for back-office users."""
|
||||
|
||||
news_type_id = serializers.PrimaryKeyRelatedField(
|
||||
source='news_type', write_only=True,
|
||||
queryset=models.NewsType.objects.all())
|
||||
|
||||
country_id = serializers.PrimaryKeyRelatedField(
|
||||
source='country', write_only=True,
|
||||
queryset=location_models.Country.objects.all())
|
||||
|
||||
class Meta(NewsBackOfficeBaseSerializer.Meta, NewsDetailSerializer.Meta):
|
||||
"""Meta class."""
|
||||
|
||||
fields = NewsBackOfficeBaseSerializer.Meta.fields + \
|
||||
NewsDetailSerializer.Meta.fields + (
|
||||
'description',
|
||||
'news_type_id',
|
||||
'country_id',
|
||||
)
|
||||
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
"""News app common serializers."""
|
||||
from rest_framework import serializers
|
||||
from gallery import models as gallery_models
|
||||
from location.models import Address
|
||||
from location.serializers import AddressSerializer
|
||||
from news import models
|
||||
|
||||
|
||||
class NewsTypeSerializer(serializers.ModelSerializer):
|
||||
"""News type serializer."""
|
||||
class Meta:
|
||||
model = models.NewsType
|
||||
fields = [
|
||||
'id',
|
||||
'name'
|
||||
]
|
||||
|
||||
|
||||
class NewsSerializer(serializers.ModelSerializer):
|
||||
"""News serializer."""
|
||||
|
||||
address = AddressSerializer()
|
||||
title_translated = serializers.CharField(read_only=True, allow_null=True)
|
||||
subtitle_translated = serializers.CharField(read_only=True, allow_null=True)
|
||||
description_translated = serializers.CharField(read_only=True, allow_null=True)
|
||||
image_url = serializers.ImageField(source='image.image', allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = models.News
|
||||
fields = [
|
||||
'id',
|
||||
'news_type',
|
||||
'start',
|
||||
'end',
|
||||
'playlist',
|
||||
'address',
|
||||
'is_highlighted',
|
||||
'image_url',
|
||||
# Localized fields
|
||||
'title_translated',
|
||||
'subtitle_translated',
|
||||
'description_translated',
|
||||
]
|
||||
|
||||
|
||||
class NewsCreateUpdateSerializer(NewsSerializer):
|
||||
"""News update serializer."""
|
||||
title = serializers.JSONField()
|
||||
subtitle = serializers.JSONField()
|
||||
description = serializers.JSONField()
|
||||
image = serializers.PrimaryKeyRelatedField(
|
||||
queryset=gallery_models.Image.objects.all(), required=True,)
|
||||
news_type = serializers.PrimaryKeyRelatedField(
|
||||
queryset=models.NewsType.objects.all(), write_only=True)
|
||||
address = serializers.PrimaryKeyRelatedField(
|
||||
queryset=Address.objects.all(), write_only=True)
|
||||
|
||||
class Meta:
|
||||
model = models.News
|
||||
read_only_fields = [
|
||||
'id'
|
||||
]
|
||||
fields = [
|
||||
'id',
|
||||
'news_type',
|
||||
'title',
|
||||
'subtitle',
|
||||
'description',
|
||||
'start',
|
||||
'end',
|
||||
'playlist',
|
||||
'address',
|
||||
'image',
|
||||
'is_publish',
|
||||
'country'
|
||||
]
|
||||
11
apps/news/urls/back.py
Normal file
11
apps/news/urls/back.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
"""News app urlpatterns for backoffice"""
|
||||
from django.urls import path
|
||||
from news import views
|
||||
|
||||
app_name = 'news'
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.NewsBackOfficeLCView.as_view(), name='list-create'),
|
||||
path('<int:pk>/', views.NewsBackOfficeRUDView.as_view(),
|
||||
name='retrieve-update-destroy'),
|
||||
]
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
"""Location app urlconf."""
|
||||
"""News app urlconf."""
|
||||
from django.urls import path
|
||||
|
||||
from news.views import common
|
||||
from news import views
|
||||
|
||||
app_name = 'news'
|
||||
|
||||
urlpatterns = [
|
||||
path('', common.NewsListView.as_view(), name='list'),
|
||||
path('<int:pk>/', common.NewsDetailView.as_view(), name='rud'),
|
||||
path('type/', common.NewsTypeListView.as_view(), name='type'),
|
||||
path('', views.NewsListView.as_view(), name='list'),
|
||||
path('<int:pk>/', views.NewsDetailView.as_view(), name='rud'),
|
||||
path('types/', views.NewsTypeListView.as_view(), name='type'),
|
||||
]
|
||||
|
|
|
|||
68
apps/news/views.py
Normal file
68
apps/news/views.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
"""News app views."""
|
||||
from rest_framework import generics, permissions
|
||||
from news import filters, models, serializers
|
||||
|
||||
|
||||
class NewsMixinView:
|
||||
"""News mixin."""
|
||||
|
||||
permission_classes = (permissions.AllowAny, )
|
||||
serializer_class = serializers.NewsBaseSerializer
|
||||
|
||||
def get_queryset(self, *args, **kwargs):
|
||||
"""Override get_queryset method."""
|
||||
qs = models.News.objects.with_related().published()\
|
||||
.order_by('-is_highlighted', '-created')
|
||||
if self.request.country_code:
|
||||
qs = qs.by_country_code(self.request.country_code)
|
||||
return qs
|
||||
|
||||
|
||||
class NewsListView(NewsMixinView, generics.ListAPIView):
|
||||
"""News list view."""
|
||||
|
||||
filter_class = filters.NewsListFilterSet
|
||||
|
||||
|
||||
class NewsDetailView(NewsMixinView, generics.RetrieveAPIView):
|
||||
"""News detail view."""
|
||||
|
||||
serializer_class = serializers.NewsDetailSerializer
|
||||
|
||||
|
||||
class NewsTypeListView(generics.ListAPIView):
|
||||
"""NewsType list view."""
|
||||
|
||||
pagination_class = None
|
||||
permission_classes = (permissions.AllowAny, )
|
||||
queryset = models.NewsType.objects.all()
|
||||
serializer_class = serializers.NewsTypeSerializer
|
||||
|
||||
|
||||
class NewsBackOfficeMixinView:
|
||||
"""News back office mixin view."""
|
||||
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
queryset = models.News.objects.with_related() \
|
||||
.order_by('-is_highlighted', '-created')
|
||||
|
||||
|
||||
class NewsBackOfficeLCView(NewsBackOfficeMixinView,
|
||||
generics.ListCreateAPIView):
|
||||
"""Resource for a list of news for back-office users."""
|
||||
|
||||
serializer_class = serializers.NewsBackOfficeBaseSerializer
|
||||
create_serializers_class = serializers.NewsBackOfficeDetailSerializer
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.request.method == 'POST':
|
||||
return self.create_serializers_class
|
||||
return super().get_serializer_class()
|
||||
|
||||
|
||||
class NewsBackOfficeRUDView(NewsBackOfficeMixinView,
|
||||
generics.RetrieveUpdateDestroyAPIView):
|
||||
"""Resource for detailed information about news for back-office users."""
|
||||
|
||||
serializer_class = serializers.NewsBackOfficeDetailSerializer
|
||||
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
"""News app common app."""
|
||||
from rest_framework import generics, permissions
|
||||
|
||||
from news import filters, models
|
||||
from news.serializers import common as serializers
|
||||
from utils.views import JWTGenericViewMixin
|
||||
|
||||
|
||||
class NewsMixin:
|
||||
"""News mixin."""
|
||||
|
||||
permission_classes = (permissions.AllowAny, )
|
||||
serializer_class = serializers.NewsSerializer
|
||||
|
||||
def get_queryset(self, *args, **kwargs):
|
||||
"""Override get_queryset method"""
|
||||
return models.News.objects.published() \
|
||||
.by_country_code(code=self.request.country_code) \
|
||||
.order_by('-is_highlighted', '-created')
|
||||
|
||||
|
||||
class NewsListView(NewsMixin, generics.ListAPIView):
|
||||
"""News list view."""
|
||||
|
||||
filter_class = filters.NewsListFilterSet
|
||||
|
||||
|
||||
class NewsDetailView(NewsMixin, JWTGenericViewMixin, generics.RetrieveAPIView):
|
||||
"""News detail view."""
|
||||
|
||||
|
||||
class NewsTypeListView(generics.ListAPIView):
|
||||
"""NewsType list view."""
|
||||
|
||||
serializer_class = serializers.NewsTypeSerializer
|
||||
permission_classes = (permissions.AllowAny, )
|
||||
pagination_class = None
|
||||
queryset = models.NewsType.objects.all()
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
"""Utils app serializer."""
|
||||
from rest_framework import serializers
|
||||
|
||||
from utils.models import PlatformMixin
|
||||
|
||||
|
||||
|
|
@ -13,3 +12,12 @@ class SourceSerializerMixin(serializers.Serializer):
|
|||
source = serializers.ChoiceField(choices=PlatformMixin.SOURCES,
|
||||
default=PlatformMixin.WEB,
|
||||
write_only=True)
|
||||
|
||||
|
||||
class TranslatedField(serializers.CharField):
|
||||
"""Translated field."""
|
||||
|
||||
def __init__(self, allow_null=True, required=False, read_only=True,
|
||||
**kwargs):
|
||||
super().__init__(allow_null=allow_null, required=required,
|
||||
read_only=read_only, **kwargs)
|
||||
|
|
|
|||
|
|
@ -7,4 +7,5 @@ urlpatterns = [
|
|||
namespace='gallery')),
|
||||
path('establishments/', include('establishment.urls.back')),
|
||||
path('location/', include('location.urls.back')),
|
||||
path('news/', include('news.urls.back'))
|
||||
]
|
||||
Loading…
Reference in New Issue
Block a user