diff --git a/apps/collection/migrations/0006_auto_20190827_1205.py b/apps/collection/migrations/0006_auto_20190827_1205.py new file mode 100644 index 00000000..b1e2a687 --- /dev/null +++ b/apps/collection/migrations/0006_auto_20190827_1205.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.4 on 2019-08-27 12:05 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('collection', '0005_auto_20190823_1401'), + ] + + operations = [ + migrations.AlterField( + model_name='collection', + name='image', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='gallery.Image', verbose_name='Collection image'), + ), + ] diff --git a/apps/collection/models.py b/apps/collection/models.py index 4d03a0ae..e6aa8264 100644 --- a/apps/collection/models.py +++ b/apps/collection/models.py @@ -1,5 +1,6 @@ from django.contrib.postgres.fields import JSONField from django.db import models +from django.utils import timezone from django.utils.translation import gettext_lazy as _ from utils.models import ProjectBaseMixin, ImageMixin @@ -37,10 +38,17 @@ class CollectionQuerySet(models.QuerySet): """Returned only published collection""" return self.filter(is_publish=True) + def valid(self): + """Returns valid offers""" + now = timezone.now() + return self.filter(start__lte=now, end__gte=now) -class Collection(ProjectBaseMixin, CollectionNameMixin, - ImageMixin, CollectionDateMixin): + +class Collection(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin): """Collection model.""" + image = models.ForeignKey( + 'gallery.Image', blank=True, default=None, null=True, + verbose_name=_('Collection image'), on_delete=models.CASCADE) is_publish = models.BooleanField( default=False, verbose_name=_('Publish status')) on_top = models.BooleanField( diff --git a/apps/collection/serializers/common.py b/apps/collection/serializers/common.py index 44082406..cd46be7a 100644 --- a/apps/collection/serializers/common.py +++ b/apps/collection/serializers/common.py @@ -1,10 +1,12 @@ from rest_framework import serializers from collection import models +from utils.serializers import ImageSerializerMixin -class CollectionSerializer(serializers.ModelSerializer): +class CollectionSerializer(ImageSerializerMixin, serializers.ModelSerializer): """Collection serializer""" + class Meta: model = models.Collection fields = [ diff --git a/apps/collection/views/common.py b/apps/collection/views/common.py index 02047b88..70eeabdb 100644 --- a/apps/collection/views/common.py +++ b/apps/collection/views/common.py @@ -34,6 +34,7 @@ class CollectionListView(CollectionViewMixin, generics.ListAPIView): def get_queryset(self): """Override get_queryset method""" return models.Collection.objects.published()\ + .valid()\ .by_country_code(code=self.request.country_code)\ .order_by('-on_top', '-created') diff --git a/apps/gallery/admin.py b/apps/gallery/admin.py index 6ea57afd..fc20b0ee 100644 --- a/apps/gallery/admin.py +++ b/apps/gallery/admin.py @@ -1,8 +1,8 @@ from django.contrib import admin -from gallery.models import Gallery +from gallery.models import Image -@admin.register(Gallery) -class GalleryModelAdmin(admin.ModelAdmin): - """Gallery model admin""" +@admin.register(Image) +class ImageModelAdmin(admin.ModelAdmin): + """Image model admin""" diff --git a/apps/gallery/migrations/0001_initial.py b/apps/gallery/migrations/0001_initial.py new file mode 100644 index 00000000..6e70764f --- /dev/null +++ b/apps/gallery/migrations/0001_initial.py @@ -0,0 +1,30 @@ +# Generated by Django 2.2.4 on 2019-08-27 12:04 + +from django.db import migrations, models +import django.utils.timezone +import easy_thumbnails.fields +import utils.methods + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Image', + 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')), + ('image', easy_thumbnails.fields.ThumbnailerImageField(upload_to=utils.methods.image_path, verbose_name='Image')), + ], + options={ + 'verbose_name': 'Image', + 'verbose_name_plural': 'Images', + }, + ), + ] diff --git a/apps/gallery/models.py b/apps/gallery/models.py index eb86ed38..487238a3 100644 --- a/apps/gallery/models.py +++ b/apps/gallery/models.py @@ -1,16 +1,21 @@ from django.utils.translation import gettext_lazy as _ +from easy_thumbnails.fields import ThumbnailerImageField +from utils.methods import image_path from utils.models import ProjectBaseMixin, ImageMixin -class Gallery(ProjectBaseMixin, ImageMixin): - """Gallery model.""" +class Image(ProjectBaseMixin, ImageMixin): + """Image model.""" + + image = ThumbnailerImageField(upload_to=image_path, + verbose_name=_('Image')) class Meta: """Meta class.""" - verbose_name = _('Gallery') - verbose_name_plural = _('Galleries') + verbose_name = _('Image') + verbose_name_plural = _('Images') def __str__(self): - """String representation""" - return str(self.get_image_url) + """String representation of image instance""" + return str(self.id) diff --git a/apps/gallery/serializers.py b/apps/gallery/serializers.py index 9db4e8f6..ef7466de 100644 --- a/apps/gallery/serializers.py +++ b/apps/gallery/serializers.py @@ -1,19 +1,24 @@ +"""Serializers for model Image""" from rest_framework import serializers from . import models -class GallerySerializer(serializers.ModelSerializer): - """Serializer for model Gallery.""" +class ImageSerializer(serializers.ModelSerializer): + """Serializer for model Image.""" + # REQUEST + image = serializers.ImageField(write_only=True) # RESPONSE - url = serializers.URLField(source='get_image_url') + url = serializers.URLField(source='get_image_url', + required=False) class Meta: """Meta class""" - model = models.Gallery + model = models.Image fields = ( + 'id', 'image', - 'url' + 'url', ) read_only_fields = ( 'url', diff --git a/apps/gallery/urls.py b/apps/gallery/urls.py index bf68b85f..b1afe932 100644 --- a/apps/gallery/urls.py +++ b/apps/gallery/urls.py @@ -5,7 +5,7 @@ from . import views app_name = 'gallery' -url_patterns = [ +urlpatterns = [ path('upload/', views.GalleryUploadImage.as_view(), name='upload_image') ] diff --git a/apps/gallery/views.py b/apps/gallery/views.py index 9381c085..63a04df3 100644 --- a/apps/gallery/views.py +++ b/apps/gallery/views.py @@ -5,6 +5,6 @@ from . import models, serializers class GalleryUploadImage(generics.CreateAPIView): """Upload image to gallery""" - model = models.Gallery - queryset = models.Gallery.objects.all() - serializer_class = serializers.GallerySerializer + model = models.Image + queryset = models.Image.objects.all() + serializer_class = serializers.ImageSerializer diff --git a/apps/news/migrations/0006_auto_20190827_1205.py b/apps/news/migrations/0006_auto_20190827_1205.py new file mode 100644 index 00000000..96d764e6 --- /dev/null +++ b/apps/news/migrations/0006_auto_20190827_1205.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.4 on 2019-08-27 12:05 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0005_auto_20190823_1149'), + ] + + operations = [ + migrations.AlterField( + model_name='news', + name='image', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='gallery.Image', verbose_name='News image'), + ), + ] diff --git a/apps/news/models.py b/apps/news/models.py index addb600d..0c1a95bb 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -1,5 +1,6 @@ from django.contrib.postgres.fields import JSONField from django.db import models +from django.utils import timezone from django.utils.translation import gettext_lazy as _ from utils.models import (ProjectBaseMixin, BaseAttributes, @@ -36,8 +37,13 @@ class NewsQuerySet(models.QuerySet): """Return only published news""" return self.filter(is_publish=True) + def valid(self): + """Returns valid offers""" + now = timezone.now() + return self.filter(start__lte=now, end__gte=now) -class News(ImageMixin, BaseAttributes): + +class News(BaseAttributes): """News model.""" news_type = models.ForeignKey( NewsType, verbose_name=_('news type'), on_delete=models.CASCADE) @@ -64,6 +70,9 @@ class News(ImageMixin, BaseAttributes): country = models.ForeignKey( 'location.Country', blank=True, null=True, verbose_name=_('country'), on_delete=models.CASCADE) + image = models.ForeignKey( + 'gallery.Image', blank=True, default=None, null=True, + verbose_name=_('News image'), on_delete=models.CASCADE) is_highlighted = models.BooleanField( default=False, verbose_name=_('Is highlighted')) # TODO: metadata_keys - описание ключей для динамического построения полей метаданных diff --git a/apps/news/serializers/common.py b/apps/news/serializers/common.py index 32e4382d..6ab373c2 100644 --- a/apps/news/serializers/common.py +++ b/apps/news/serializers/common.py @@ -3,6 +3,7 @@ from rest_framework import serializers from location.models import Address from location.serializers import AddressSerializer from news import models +from utils.serializers import ImageSerializerMixin class NewsTypeSerializer(serializers.ModelSerializer): @@ -23,7 +24,7 @@ class NewsLocalizationMixinSerializer(serializers.ModelSerializer): description_trans = serializers.CharField(read_only=True) -class NewsSerializer(NewsLocalizationMixinSerializer): +class NewsSerializer(NewsLocalizationMixinSerializer, ImageSerializerMixin): """News serializer.""" address = AddressSerializer() @@ -50,7 +51,6 @@ class NewsCreateUpdateSerializer(NewsSerializer): title = serializers.JSONField() subtitle = serializers.JSONField() description = serializers.JSONField() - image = serializers.ImageField(required=True) news_type = serializers.PrimaryKeyRelatedField( queryset=models.NewsType.objects.all(), write_only=True) address = serializers.PrimaryKeyRelatedField( diff --git a/apps/news/views/common.py b/apps/news/views/common.py index 4b9c6796..05b14da4 100644 --- a/apps/news/views/common.py +++ b/apps/news/views/common.py @@ -26,6 +26,7 @@ class NewsList(NewsViewMixin, JWTListAPIView): """Override get_queryset method""" return News.objects.annotate_localized_fields(locale=self.request.locale)\ .published()\ + .valid()\ .by_country_code(code=self.request.country_code)\ .order_by('-is_highlighted', '-created') diff --git a/apps/utils/models.py b/apps/utils/models.py index 9498982c..9c03dd64 100644 --- a/apps/utils/models.py +++ b/apps/utils/models.py @@ -1,5 +1,7 @@ """Utils app models.""" +from os.path import exists + from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.contrib.gis.db import models from django.contrib.postgres.fields import JSONField @@ -74,7 +76,14 @@ class ImageMixin(models.Model): def get_image_url(self, key=None): """Get image thumbnail url.""" - return self.get_image(key).url if self.image else None + return self.get_image(key).url if self.image and exists(self.image.path) else None + + def get_full_image_url(self, request, thumbnail_key=None): + """Get full image url""" + if self.image and exists(self.image.path): + return request.build_absolute_uri(self.get_image(thumbnail_key).url) + else: + return None def image_tag(self): """Admin preview tag.""" diff --git a/apps/utils/serializers.py b/apps/utils/serializers.py new file mode 100644 index 00000000..36978683 --- /dev/null +++ b/apps/utils/serializers.py @@ -0,0 +1,15 @@ +"""Serializer mixins""" +from rest_framework import serializers + + +class ImageSerializerMixin(serializers.Serializer): + """ + Image serializer mixin. + Need to return an absolute path of cropped image instead of relative path + """ + # RESPONSE + image = serializers.SerializerMethodField() + + def get_image(self, obj): + """Get full image URL""" + return obj.image.get_full_image_url(self.context.get('request')) diff --git a/project/settings/base.py b/project/settings/base.py index 97a75ff7..8305b637 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -57,6 +57,7 @@ PROJECT_APPS = [ 'authorization.apps.AuthorizationConfig', 'collection.apps.CollectionConfig', 'establishment.apps.EstablishmentConfig', + 'gallery.apps.GalleryConfig', 'location.apps.LocationConfig', 'main.apps.MainConfig', 'news.apps.NewsConfig', diff --git a/project/urls/__init__.py b/project/urls/__init__.py index ef909dcf..5355ca31 100644 --- a/project/urls/__init__.py +++ b/project/urls/__init__.py @@ -54,6 +54,8 @@ urlpatterns_auth = [ ] api_urlpatterns = [ + path('gallery/', include(('gallery.urls', 'gallery'), + namespace='gallery')), path('location/', include(('location.urls', 'location'), namespace='location')), path('main/', include(('main.urls', 'main'),