From 1dbd7a6451483042d1d909cd36eafcb2f361f4b7 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Fri, 15 Nov 2019 13:01:21 +0300 Subject: [PATCH] refactored advertisements --- apps/advertisement/admin.py | 7 ++ .../migrations/0006_auto_20191115_0750.py | 34 ++++++++ .../migrations/0007_auto_20191115_0750.py | 30 +++++++ apps/advertisement/models.py | 45 +++++++++- apps/advertisement/serializers/__init__.py | 3 + apps/advertisement/serializers/common.py | 39 +++++++++ apps/advertisement/serializers/mobile.py | 15 ++++ apps/advertisement/serializers/web.py | 31 +++---- apps/advertisement/urls/common.py | 8 ++ apps/advertisement/urls/mobile.py | 14 +++ apps/advertisement/urls/web.py | 5 +- apps/advertisement/views/__init__.py | 3 + apps/advertisement/views/common.py | 32 +++++++ apps/advertisement/views/mobile.py | 9 ++ apps/advertisement/views/web.py | 22 ++--- apps/main/admin.py | 5 ++ .../migrations/0036_auto_20191115_0750.py | 85 +++++++++++++++++++ apps/main/models.py | 71 ++++++++++++---- apps/main/serializers.py | 36 ++++++-- project/settings/base.py | 1 + project/urls/mobile.py | 2 +- project/urls/web.py | 1 + 22 files changed, 433 insertions(+), 65 deletions(-) create mode 100644 apps/advertisement/migrations/0006_auto_20191115_0750.py create mode 100644 apps/advertisement/migrations/0007_auto_20191115_0750.py create mode 100644 apps/advertisement/serializers/common.py create mode 100644 apps/advertisement/serializers/mobile.py create mode 100644 apps/advertisement/urls/common.py create mode 100644 apps/advertisement/urls/mobile.py create mode 100644 apps/advertisement/views/common.py create mode 100644 apps/advertisement/views/mobile.py create mode 100644 apps/main/migrations/0036_auto_20191115_0750.py diff --git a/apps/advertisement/admin.py b/apps/advertisement/admin.py index 74bc6cdc..3754dca9 100644 --- a/apps/advertisement/admin.py +++ b/apps/advertisement/admin.py @@ -2,8 +2,15 @@ from django.contrib import admin from advertisement import models +from main.models import Page + + +class PageInline(admin.TabularInline): + model = Page + extra = 0 @admin.register(models.Advertisement) class AdvertisementModelAdmin(admin.ModelAdmin): """Admin model for model Advertisement""" + inlines = (PageInline, ) diff --git a/apps/advertisement/migrations/0006_auto_20191115_0750.py b/apps/advertisement/migrations/0006_auto_20191115_0750.py new file mode 100644 index 00000000..a043cab2 --- /dev/null +++ b/apps/advertisement/migrations/0006_auto_20191115_0750.py @@ -0,0 +1,34 @@ +# Generated by Django 2.2.7 on 2019-11-15 07:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('advertisement', '0005_auto_20191108_0923'), + ] + + operations = [ + migrations.RemoveField( + model_name='advertisement', + name='height', + ), + migrations.RemoveField( + model_name='advertisement', + name='image_url', + ), + migrations.RemoveField( + model_name='advertisement', + name='source', + ), + migrations.RemoveField( + model_name='advertisement', + name='width', + ), + migrations.AddField( + model_name='advertisement', + name='end', + field=models.DateTimeField(blank=True, default=None, null=True, verbose_name='end'), + ), + ] diff --git a/apps/advertisement/migrations/0007_auto_20191115_0750.py b/apps/advertisement/migrations/0007_auto_20191115_0750.py new file mode 100644 index 00000000..67617b68 --- /dev/null +++ b/apps/advertisement/migrations/0007_auto_20191115_0750.py @@ -0,0 +1,30 @@ +# Generated by Django 2.2.7 on 2019-11-15 07:50 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('advertisement', '0006_auto_20191115_0750'), + ('main', '0036_auto_20191115_0750'), + ] + + operations = [ + migrations.AddField( + model_name='advertisement', + name='page_type', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='advertisements', to='main.PageType', verbose_name='page type'), + ), + migrations.AddField( + model_name='advertisement', + name='sites', + field=models.ManyToManyField(related_name='advertisements', to='main.SiteSettings', verbose_name='site'), + ), + migrations.AddField( + model_name='advertisement', + name='start', + field=models.DateTimeField(null=True, verbose_name='start'), + ), + ] diff --git a/apps/advertisement/models.py b/apps/advertisement/models.py index 3822522d..574aff56 100644 --- a/apps/advertisement/models.py +++ b/apps/advertisement/models.py @@ -6,18 +6,47 @@ from django.utils.translation import gettext_lazy as _ from translation.models import Language from utils.models import ProjectBaseMixin, ImageMixin, PlatformMixin, URLImageMixin +from main.models import Page -class Advertisement(URLImageMixin, ProjectBaseMixin, PlatformMixin): +class AdvertisementQuerySet(models.QuerySet): + """QuerySet for model Advertisement.""" + + def with_base_related(self): + """Return QuerySet with base related""" + return self.select_related('page_type') \ + .prefetch_related('target_languages', 'sites', 'pages') + + def by_page_type(self, page_type: str): + """Filter Advertisement by page type.""" + return self.filter(page_type__name=page_type) + + def by_locale(self, locale): + """Filter by locale.""" + return self.filter(target_languages__locale=locale) + + +class Advertisement(ProjectBaseMixin): """Advertisement model.""" old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None) uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) url = models.URLField(verbose_name=_('Ad URL')) - width = models.PositiveIntegerField(verbose_name=_('Block width')) # 300 - height = models.PositiveIntegerField(verbose_name=_('Block height')) # 250 block_level = models.CharField(verbose_name=_('Block level'), max_length=10, blank=True, null=True) target_languages = models.ManyToManyField(Language) + start = models.DateTimeField(null=True, + verbose_name=_('start')) + end = models.DateTimeField(blank=True, null=True, default=None, + verbose_name=_('end')) + sites = models.ManyToManyField('main.SiteSettings', + related_name='advertisements', + verbose_name=_('site')) + page_type = models.ForeignKey('main.PageType', on_delete=models.PROTECT, + null=True, + related_name='advertisements', + verbose_name=_('page type')) + + objects = AdvertisementQuerySet.as_manager() class Meta: verbose_name = _('Advertisement') @@ -25,3 +54,13 @@ class Advertisement(URLImageMixin, ProjectBaseMixin, PlatformMixin): def __str__(self): return str(self.url) + + @property + def mobile_page(self): + """Return mobile page""" + return self.pages.by_platform(Page.MOBILE).first() + + @property + def web_page(self): + """Return web page""" + return self.pages.by_platform(Page.WEB).first() diff --git a/apps/advertisement/serializers/__init__.py b/apps/advertisement/serializers/__init__.py index e69de29b..393379a8 100644 --- a/apps/advertisement/serializers/__init__.py +++ b/apps/advertisement/serializers/__init__.py @@ -0,0 +1,3 @@ +from .common import * +from .mobile import * +from .web import * diff --git a/apps/advertisement/serializers/common.py b/apps/advertisement/serializers/common.py new file mode 100644 index 00000000..8b87abad --- /dev/null +++ b/apps/advertisement/serializers/common.py @@ -0,0 +1,39 @@ +"""Serializers for app advertisements""" +from rest_framework import serializers + +from advertisement import models +from translation.serializers import LanguageSerializer +from main.serializers import SiteShortSerializer +from main.serializers import PageBaseSerializer + + +class AdvertisementBaseSerializer(serializers.ModelSerializer): + """Base serializer for model Advertisement.""" + + languages = LanguageSerializer(many=True, read_only=True) + sites = SiteShortSerializer(many=True, read_only=True) + + class Meta: + model = models.Advertisement + fields = [ + 'id', + 'uuid', + 'url', + 'block_level', + 'languages', + 'sites', + 'start', + 'end', + ] + + +class AdvertisementPageTypeCommonListSerializer(AdvertisementBaseSerializer): + """Serializer for AdvertisementPageTypeCommonView.""" + + page = PageBaseSerializer(source='common_page', read_only=True) + + class Meta(AdvertisementBaseSerializer.Meta): + """Meta class.""" + fields = AdvertisementBaseSerializer.Meta.fields + [ + 'page', + ] diff --git a/apps/advertisement/serializers/mobile.py b/apps/advertisement/serializers/mobile.py new file mode 100644 index 00000000..80a19b82 --- /dev/null +++ b/apps/advertisement/serializers/mobile.py @@ -0,0 +1,15 @@ +"""Serializers for mobile app advertisements""" +from advertisement.serializers import AdvertisementBaseSerializer +from main.serializers import PageBaseSerializer + + +class AdvertisementPageTypeMobileListSerializer(AdvertisementBaseSerializer): + """Serializer for AdvertisementPageTypeMobileView.""" + + page = PageBaseSerializer(source='mobile_page', read_only=True) + + class Meta(AdvertisementBaseSerializer.Meta): + """Meta class.""" + fields = AdvertisementBaseSerializer.Meta.fields + [ + 'page', + ] diff --git a/apps/advertisement/serializers/web.py b/apps/advertisement/serializers/web.py index 6d5ebfc0..175f1875 100644 --- a/apps/advertisement/serializers/web.py +++ b/apps/advertisement/serializers/web.py @@ -1,22 +1,15 @@ -"""Serializers for app advertisements""" -from rest_framework import serializers - -from advertisement import models -from translation.serializers import LanguageSerializer +"""Serializers for web app advertisements""" +from advertisement.serializers import AdvertisementBaseSerializer +from main.serializers import PageBaseSerializer -class AdvertisementSerializer(serializers.ModelSerializer): - """Serializer for model Advertisement.""" +class AdvertisementPageTypeWebListSerializer(AdvertisementBaseSerializer): + """Serializer for AdvertisementPageTypeWebView.""" - class Meta: - model = models.Advertisement - fields = ( - 'id', - 'uuid', - 'url', - 'image_url', - 'width', - 'height', - 'block_level', - 'source' - ) + page = PageBaseSerializer(source='web_page', read_only=True) + + class Meta(AdvertisementBaseSerializer.Meta): + """Meta class.""" + fields = AdvertisementBaseSerializer.Meta.fields + [ + 'page', + ] diff --git a/apps/advertisement/urls/common.py b/apps/advertisement/urls/common.py new file mode 100644 index 00000000..323a3b48 --- /dev/null +++ b/apps/advertisement/urls/common.py @@ -0,0 +1,8 @@ +"""Advertisement common urlpaths.""" +from django.urls import path + + +app_name = 'advertisements' + +common_urlpatterns = [ +] diff --git a/apps/advertisement/urls/mobile.py b/apps/advertisement/urls/mobile.py new file mode 100644 index 00000000..f61003da --- /dev/null +++ b/apps/advertisement/urls/mobile.py @@ -0,0 +1,14 @@ +"""Advertisement common urlpaths.""" +from django.urls import path + +from advertisement.views import mobile as views +from .common import common_urlpatterns + + +app_name = 'advertisements' + +urlpatterns = [ + path('/', views.AdvertisementPageTypeMobileListView.as_view(), name='list'), +] + +urlpatterns += common_urlpatterns diff --git a/apps/advertisement/urls/web.py b/apps/advertisement/urls/web.py index 48f9d71a..4d17c831 100644 --- a/apps/advertisement/urls/web.py +++ b/apps/advertisement/urls/web.py @@ -2,9 +2,12 @@ from django.urls import path from advertisement.views import web as views +from .common import common_urlpatterns app_name = 'advertisements' urlpatterns = [ - path('/', views.AdvertisementListView.as_view(), name='list') + path('/', views.AdvertisementPageTypeWebListView.as_view(), name='list'), ] + +urlpatterns += common_urlpatterns diff --git a/apps/advertisement/views/__init__.py b/apps/advertisement/views/__init__.py index e69de29b..393379a8 100644 --- a/apps/advertisement/views/__init__.py +++ b/apps/advertisement/views/__init__.py @@ -0,0 +1,3 @@ +from .common import * +from .mobile import * +from .web import * diff --git a/apps/advertisement/views/common.py b/apps/advertisement/views/common.py new file mode 100644 index 00000000..43c6e965 --- /dev/null +++ b/apps/advertisement/views/common.py @@ -0,0 +1,32 @@ +"""Views for app advertisement""" +from rest_framework import generics +from rest_framework import permissions + +from advertisement.models import Advertisement +from advertisement.serializers import AdvertisementBaseSerializer, \ + AdvertisementPageTypeCommonListSerializer + + +class AdvertisementBaseView(generics.GenericAPIView): + """Advertisement list view.""" + + pagination_class = None + permission_classes = (permissions.AllowAny, ) + serializer_class = AdvertisementBaseSerializer + + def get_queryset(self): + """Overridden get queryset method.""" + return Advertisement.objects.with_base_related() \ + .by_locale(self.request.locale) + + +class AdvertisementPageTypeListView(AdvertisementBaseView, generics.ListAPIView): + """Advertisement list view by page type.""" + + def get_queryset(self): + """Overridden get queryset method.""" + product_type = self.kwargs.get('page_type') + qs = super(AdvertisementPageTypeListView, self).get_queryset() + if product_type: + return qs.by_page_type(product_type) + return qs.none() diff --git a/apps/advertisement/views/mobile.py b/apps/advertisement/views/mobile.py new file mode 100644 index 00000000..bac5a81d --- /dev/null +++ b/apps/advertisement/views/mobile.py @@ -0,0 +1,9 @@ +"""Mobile views for app advertisement""" +from advertisement.serializers import AdvertisementPageTypeMobileListSerializer +from .common import AdvertisementPageTypeListView + + +class AdvertisementPageTypeMobileListView(AdvertisementPageTypeListView): + """Advertisement mobile list view.""" + + serializer_class = AdvertisementPageTypeMobileListSerializer diff --git a/apps/advertisement/views/web.py b/apps/advertisement/views/web.py index 1740022c..db1cfde8 100644 --- a/apps/advertisement/views/web.py +++ b/apps/advertisement/views/web.py @@ -1,19 +1,9 @@ -"""Views for app advertisement""" -from rest_framework import generics -from rest_framework import permissions - -from advertisement import models -from advertisement.serializers import web as serializers +"""Web views for app advertisement""" +from advertisement.serializers import AdvertisementPageTypeWebListSerializer +from .common import AdvertisementPageTypeListView -class AdvertisementListView(generics.ListAPIView): - """List view for model Advertisement""" - pagination_class = None - model = models.Advertisement - permission_classes = (permissions.AllowAny,) - serializer_class = serializers.AdvertisementSerializer +class AdvertisementPageTypeWebListView(AdvertisementPageTypeListView): + """Advertisement mobile list view.""" - def get_queryset(self): - return models.Advertisement.objects\ - .filter(page__page_name__contains=self.kwargs['page'])\ - .filter(target_languages__locale=self.request.locale) + serializer_class = AdvertisementPageTypeWebListSerializer diff --git a/apps/main/admin.py b/apps/main/admin.py index 77ef20ab..057515e8 100644 --- a/apps/main/admin.py +++ b/apps/main/admin.py @@ -35,3 +35,8 @@ class CurrencyContentAdmin(admin.ModelAdmin): @admin.register(models.Carousel) class CarouselAdmin(admin.ModelAdmin): """Carousel admin.""" + + +@admin.register(models.PageType) +class PageTypeAdmin(admin.ModelAdmin): + """PageType admin.""" diff --git a/apps/main/migrations/0036_auto_20191115_0750.py b/apps/main/migrations/0036_auto_20191115_0750.py new file mode 100644 index 00000000..e429d1e2 --- /dev/null +++ b/apps/main/migrations/0036_auto_20191115_0750.py @@ -0,0 +1,85 @@ +# Generated by Django 2.2.7 on 2019-11-15 07:50 + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('advertisement', '0006_auto_20191115_0750'), + ('main', '0035_merge_20191112_1218'), + ] + + operations = [ + migrations.CreateModel( + name='PageType', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date created')), + ('modified', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('name', models.CharField(max_length=255, unique=True, verbose_name='name')), + ], + options={ + 'verbose_name': 'page type', + 'verbose_name_plural': 'page types', + }, + ), + migrations.AlterModelOptions( + name='page', + options={'verbose_name': 'page', 'verbose_name_plural': 'pages'}, + ), + migrations.RemoveField( + model_name='page', + name='advertisements', + ), + migrations.RemoveField( + model_name='page', + name='page_name', + ), + migrations.AddField( + model_name='page', + name='advertisement', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='pages', to='advertisement.Advertisement', verbose_name='advertisement'), + ), + migrations.AddField( + model_name='page', + name='created', + field=models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date created'), + ), + migrations.AddField( + model_name='page', + name='height', + field=models.PositiveIntegerField(null=True, verbose_name='Block height'), + ), + migrations.AddField( + model_name='page', + name='image_url', + field=models.URLField(blank=True, default=None, null=True, verbose_name='Image URL path'), + ), + migrations.AddField( + model_name='page', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='Date updated'), + ), + migrations.AddField( + model_name='page', + name='source', + field=models.PositiveSmallIntegerField(choices=[(0, 'Mobile'), (1, 'Web'), (2, 'All')], default=0, verbose_name='Source'), + ), + migrations.AddField( + model_name='page', + name='width', + field=models.PositiveIntegerField(null=True, verbose_name='Block width'), + ), + migrations.RemoveField( + model_name='feature', + name='route', + ), + migrations.AddField( + model_name='feature', + name='route', + field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='main.PageType'), + ), + ] diff --git a/apps/main/models.py b/apps/main/models.py index 310257f3..1bd39a6d 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -10,7 +10,6 @@ from django.db import models from django.db.models import Q from django.utils.translation import gettext_lazy as _ -from advertisement.models import Advertisement from configuration.models import TranslationSettings from location.models import Country from main import methods @@ -99,27 +98,12 @@ class SiteSettings(ProjectBaseMixin): domain=settings.SITE_DOMAIN_URI) -class Page(models.Model): - """Page model.""" - - page_name = models.CharField(max_length=255, unique=True) - advertisements = models.ManyToManyField(Advertisement) - - class Meta: - """Meta class.""" - verbose_name = _('Page') - verbose_name_plural = _('Pages') - - def __str__(self): - return f'{self.page_name}' - - class Feature(ProjectBaseMixin, PlatformMixin): """Feature model.""" slug = models.SlugField(max_length=255, unique=True) priority = models.IntegerField(unique=True, null=True, default=None) - route = models.ForeignKey(Page, on_delete=models.PROTECT, null=True, default=None) + route = models.ForeignKey('PageType', on_delete=models.PROTECT, null=True, default=None) site_settings = models.ManyToManyField(SiteSettings, through='SiteFeature') class Meta: @@ -310,3 +294,56 @@ class Carousel(models.Model): elif self.link not in EMPTY_VALUES: return 'external' return None + + +class PageQuerySet(models.QuerySet): + """QuerySet for model Page.""" + + def by_platform(self, platform: int): + """Filter by platform.""" + return self.filter(source=platform) + + +class Page(URLImageMixin, PlatformMixin, ProjectBaseMixin): + """Page model.""" + advertisement = models.ForeignKey('advertisement.Advertisement', + on_delete=models.PROTECT, null=True, + related_name='pages', + verbose_name=_('advertisement')) + width = models.PositiveIntegerField(null=True, + verbose_name=_('Block width')) # 300 + height = models.PositiveIntegerField(null=True, + verbose_name=_('Block height')) # 250 + + objects = PageQuerySet.as_manager() + + class Meta: + """Meta class.""" + verbose_name = _('page') + verbose_name_plural = _('pages') + + def __str__(self): + """Overridden dunder method.""" + return self.get_source_display() + + +class PageTypeQuerySet(models.QuerySet): + """QuerySet for model PageType.""" + + +class PageType(ProjectBaseMixin): + """Page type model.""" + + name = models.CharField(max_length=255, unique=True, + verbose_name=_('name')) + + objects = PageTypeQuerySet.as_manager() + + class Meta: + """Meta class.""" + verbose_name = _('page type') + verbose_name_plural = _('page types') + + def __str__(self): + """Overridden dunder method.""" + return self.name diff --git a/apps/main/serializers.py b/apps/main/serializers.py index 71bbd589..519ff9de 100644 --- a/apps/main/serializers.py +++ b/apps/main/serializers.py @@ -1,7 +1,6 @@ """Main app serializers.""" from rest_framework import serializers -from advertisement.serializers.web import AdvertisementSerializer from location.serializers import CountrySerializer from main import models from utils.serializers import ProjectModelSerializer, TranslatedField, RecursiveFieldSerializer @@ -94,11 +93,20 @@ class SiteSerializer(serializers.ModelSerializer): class Meta: """Meta class.""" - model = models.SiteSettings fields = ('subdomain', 'site_url', 'country') +class SiteShortSerializer(serializers.ModelSerializer): + """Short serializer for model SiteSettings.""" + + class Meta(SiteSerializer.Meta): + """Meta class.""" + fields = [ + 'subdomain', + ] + + # class SiteFeatureSerializer(serializers.ModelSerializer): # """Site feature serializer.""" # @@ -167,15 +175,27 @@ class CarouselListSerializer(serializers.ModelSerializer): ] -class PageSerializer(serializers.ModelSerializer): - page_name = serializers.CharField() - advertisements = AdvertisementSerializer(source='advertisements', many=True) +class PageBaseSerializer(serializers.ModelSerializer): + """Serializer for model Page""" class Meta: """Meta class.""" - model = models.Carousel + model = models.Page fields = [ 'id', - 'page_name', - 'advertisements' + 'image_url', + 'width', + 'height', ] + + +class PageTypeBaseSerializer(serializers.ModelSerializer): + """Serializer fro model PageType.""" + + class Meta: + """Meta class.""" + model = models.PageType + fields = [ + 'id', + 'name', + ] \ No newline at end of file diff --git a/project/settings/base.py b/project/settings/base.py index 5e3017c7..65ad0954 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -75,6 +75,7 @@ PROJECT_APPS = [ 'favorites.apps.FavoritesConfig', 'rating.apps.RatingConfig', 'tag.apps.TagConfig', + 'transfer.apps.TransferConfig', ] EXTERNAL_APPS = [ diff --git a/project/urls/mobile.py b/project/urls/mobile.py index 8a1558bc..4fa53ad9 100644 --- a/project/urls/mobile.py +++ b/project/urls/mobile.py @@ -9,7 +9,7 @@ urlpatterns = [ path('tags/', include('tag.urls.mobile')), path('timetables/', include('timetable.urls.mobile')), # path('account/', include('account.urls.web')), - # path('advertisement/', include('advertisement.urls.web')), + path('re_blocks/', include('advertisement.urls.mobile')), # path('collection/', include('collection.urls.web')), # path('establishments/', include('establishment.urls.web')), path('news/', include('news.urls.mobile')), diff --git a/project/urls/web.py b/project/urls/web.py index 86f7eac2..c5a609e2 100644 --- a/project/urls/web.py +++ b/project/urls/web.py @@ -36,4 +36,5 @@ urlpatterns = [ path('favorites/', include('favorites.urls')), path('timetables/', include('timetable.urls.web')), path('products/', include('product.urls.web')), + ]