gm-148: refactored
This commit is contained in:
parent
4796fce784
commit
4890e00b95
|
|
@ -18,7 +18,7 @@ def send_reset_password_email(user_id, country_code):
|
|||
user.send_email(subject=_('Password resetting'),
|
||||
message=user.reset_password_template(country_code))
|
||||
except:
|
||||
logger.error(f'METHOD_NAME: {send_reset_password_email.__name__}\n'
|
||||
logger.error(f'TASK_NAME: {send_reset_password_email.__name__}\n'
|
||||
f'DETAIL: Exception occurred for reset password: '
|
||||
f'{user_id}')
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ def confirm_new_email_address(user_id, country_code):
|
|||
user.send_email(subject=_('Validate new email address'),
|
||||
message=user.confirm_email_template(country_code))
|
||||
except:
|
||||
logger.error(f'METHOD_NAME: {confirm_new_email_address.__name__}\n'
|
||||
logger.error(f'TASK_NAME: {confirm_new_email_address.__name__}\n'
|
||||
f'DETAIL: Exception occurred for user: {user_id}')
|
||||
|
||||
|
||||
|
|
@ -43,5 +43,5 @@ def change_email_address(user_id, country_code):
|
|||
user.send_email(subject=_('Validate new email address'),
|
||||
message=user.change_email_template(country_code))
|
||||
except:
|
||||
logger.error(f'METHOD_NAME: {change_email_address.__name__}\n'
|
||||
logger.error(f'TASK_NAME: {change_email_address.__name__}\n'
|
||||
f'DETAIL: Exception occurred for user: {user_id}')
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
"""Authorization app celery tasks."""
|
||||
import logging
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from celery import shared_task
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from account import models as account_models
|
||||
|
||||
|
|
@ -17,5 +18,5 @@ def send_confirm_email(user_id, country_code):
|
|||
obj.send_email(subject=_('Email confirmation'),
|
||||
message=obj.confirm_email_template(country_code))
|
||||
except:
|
||||
logger.error(f'METHOD_NAME: {send_confirm_email.__name__}\n'
|
||||
logger.error(f'TASK_NAME: {send_confirm_email.__name__}\n'
|
||||
f'DETAIL: Exception occurred for user: {user_id}')
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Generated by Django 2.2.4 on 2019-10-02 14:56
|
||||
# Generated by Django 2.2.4 on 2019-10-03 12:28
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations
|
||||
import sorl.thumbnail.fields
|
||||
import utils.methods
|
||||
|
||||
|
|
@ -13,14 +12,13 @@ class Migration(migrations.Migration):
|
|||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='image',
|
||||
name='parent',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='image',
|
||||
name='image',
|
||||
field=sorl.thumbnail.fields.ImageField(upload_to=utils.methods.image_path, verbose_name='image file'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='image',
|
||||
name='parent',
|
||||
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='children', to='gallery.Image', verbose_name='parent image'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,20 +1,15 @@
|
|||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from sorl.thumbnail import delete
|
||||
from sorl.thumbnail.fields import ImageField as SORLImageField
|
||||
|
||||
from utils.methods import image_path
|
||||
from django.conf import settings
|
||||
from . import tasks
|
||||
from utils.models import ProjectBaseMixin, SORLImageMixin, PlatformMixin
|
||||
|
||||
|
||||
class ImageQuerySet(models.QuerySet):
|
||||
"""QuerySet for model Image."""
|
||||
|
||||
def original_images(self):
|
||||
"""Return QuerySet with original images."""
|
||||
return self.filter(parent__isnull=True)
|
||||
|
||||
|
||||
class Image(ProjectBaseMixin, SORLImageMixin, PlatformMixin):
|
||||
"""Image model."""
|
||||
|
|
@ -28,11 +23,6 @@ class Image(ProjectBaseMixin, SORLImageMixin, PlatformMixin):
|
|||
|
||||
image = SORLImageField(upload_to=image_path,
|
||||
verbose_name=_('image file'))
|
||||
parent = models.ForeignKey('self',
|
||||
blank=True, null=True, default=None,
|
||||
related_name='children',
|
||||
on_delete=models.SET_DEFAULT,
|
||||
verbose_name=_('parent image'))
|
||||
orientation = models.PositiveSmallIntegerField(choices=ORIENTATIONS,
|
||||
blank=True, null=True, default=None,
|
||||
verbose_name=_('image orientation'))
|
||||
|
|
@ -49,9 +39,20 @@ class Image(ProjectBaseMixin, SORLImageMixin, PlatformMixin):
|
|||
"""String representation"""
|
||||
return str(self.id)
|
||||
|
||||
def delete_from_remote_storage(self, delete_original: bool = True):
|
||||
"""Delete from remote storage"""
|
||||
if settings.USE_CELERY:
|
||||
tasks.delete_image_from_remote_storage.delay(self.id, delete_original)
|
||||
else:
|
||||
tasks.delete_image_from_remote_storage(self.id, delete_original)
|
||||
def delete_image(self, completely: bool = True):
|
||||
"""
|
||||
Deletes an instance and crops of instance from media storage.
|
||||
:param completely: if set to False then removed only crop neither original image.
|
||||
"""
|
||||
try:
|
||||
# Delete from remote storage
|
||||
delete(file_=self.image.file, delete_file=completely)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
finally:
|
||||
if completely:
|
||||
# Delete an instance of image
|
||||
super().delete()
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ class ImageSerializer(serializers.ModelSerializer):
|
|||
'id',
|
||||
'file',
|
||||
'url',
|
||||
'parent',
|
||||
'orientation',
|
||||
'orientation_display',
|
||||
'title',
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
import logging
|
||||
|
||||
from celery import shared_task
|
||||
from sorl.thumbnail import delete
|
||||
|
||||
from . import models
|
||||
|
||||
|
|
@ -11,17 +10,14 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
@shared_task
|
||||
def delete_image_from_remote_storage(image_id, delete_original=True):
|
||||
def delete_image(image_id: int, completely: bool = True):
|
||||
"""Delete an image from remote storage."""
|
||||
image_qs = models.Image.objects.filter(id=image_id)
|
||||
if image_qs.exists():
|
||||
try:
|
||||
image = image_qs.first()
|
||||
# Delete from remote storage
|
||||
delete(file_=image.image.file, delete_file=delete_original)
|
||||
# Delete an instance of image
|
||||
image.delete()
|
||||
image.delete_image(completely=completely)
|
||||
except:
|
||||
logger.error(f'METHOD_NAME: delete_image_from_remote_storage\n'
|
||||
logger.error(f'TASK_NAME: delete_image\n'
|
||||
f'DETAIL: Exception occurred while deleting an image '
|
||||
f'from remote storage: image_id - {image_id}')
|
||||
f'and related crops from remote storage: image_id - {image_id}')
|
||||
|
|
|
|||
|
|
@ -6,5 +6,6 @@ from . import views
|
|||
app_name = 'gallery'
|
||||
|
||||
urlpatterns = [
|
||||
path('upload/', views.ImageBaseView.as_view(), name='upload-image'),
|
||||
path('', views.ImageListCreateView.as_view(), name='list-create-image'),
|
||||
path('<int:pk>/', views.ImageRetrieveDestroyView.as_view(), name='retrieve-destroy-image'),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,19 +1,30 @@
|
|||
from rest_framework import generics
|
||||
from django.conf import settings
|
||||
from django.db.transaction import on_commit
|
||||
from rest_framework import generics, status
|
||||
from rest_framework.response import Response
|
||||
|
||||
from . import models, serializers
|
||||
from . import tasks, models, serializers
|
||||
|
||||
|
||||
class ImageBaseView(generics.CreateAPIView):
|
||||
"""Upload image to gallery."""
|
||||
class ImageBaseView(generics.GenericAPIView):
|
||||
"""Base Image view."""
|
||||
model = models.Image
|
||||
queryset = models.Image.objects.all()
|
||||
serializer_class = serializers.ImageSerializer
|
||||
|
||||
|
||||
class NewsImageListView(ImageBaseView, generics.ListAPIView):
|
||||
"""Return list of uploaded images for news object."""
|
||||
class ImageListCreateView(ImageBaseView, generics.ListCreateAPIView):
|
||||
"""List/Create Image view."""
|
||||
|
||||
def get_queryset(self):
|
||||
"""Override get_queryset method."""
|
||||
qs = super(NewsImageListView, self).get_queryset()
|
||||
return qs.filter(news_gallery__news=self.kwargs.get('news_id'))
|
||||
|
||||
class ImageRetrieveDestroyView(ImageBaseView, generics.RetrieveDestroyAPIView):
|
||||
"""Destroy view for model Image"""
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
"""Override destroy view"""
|
||||
instance = self.get_object()
|
||||
if settings.USE_CELERY:
|
||||
on_commit(lambda: tasks.delete_image.delay(image_id=instance.id))
|
||||
else:
|
||||
on_commit(lambda: tasks.delete_image(image_id=instance.id))
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
"""News app models."""
|
||||
from django.db import models
|
||||
from django.contrib.contenttypes import fields as generic
|
||||
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
|
||||
from random import sample as random_sample
|
||||
|
||||
|
||||
class NewsType(models.Model):
|
||||
|
|
@ -149,10 +149,6 @@ class News(BaseAttributes, TranslatedFieldsMixin):
|
|||
def web_url(self):
|
||||
return reverse('web:news:rud', kwargs={'slug': self.slug})
|
||||
|
||||
@property
|
||||
def original_images(self):
|
||||
return self.gallery.original_images()
|
||||
|
||||
|
||||
class NewsGalleryQuerySet(models.QuerySet):
|
||||
"""QuerySet for model News"""
|
||||
|
|
@ -169,8 +165,6 @@ class NewsGallery(models.Model):
|
|||
on_delete=models.SET_NULL,
|
||||
verbose_name=_('gallery'))
|
||||
|
||||
objects = NewsGalleryQuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
"""NewsGallery meta class."""
|
||||
verbose_name = _('news gallery')
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ from rest_framework import serializers
|
|||
|
||||
from account.serializers.common import UserBaseSerializer
|
||||
from gallery.models import Image
|
||||
from gallery.serializers import ImageSerializer
|
||||
from location import models as location_models
|
||||
from location.serializers import CountrySimpleSerializer
|
||||
from main.serializers import MetaDataContentSerializer
|
||||
|
|
@ -12,12 +11,61 @@ from news import models
|
|||
from utils.serializers import TranslatedField, ProjectModelSerializer
|
||||
|
||||
|
||||
class NewsCropImageSerializer(ImageSerializer):
|
||||
class CropImageSerializer(serializers.Serializer):
|
||||
"""Serializer for crop images for News object."""
|
||||
preview_url = serializers.SerializerMethodField()
|
||||
promo_horizontal_web_url = serializers.SerializerMethodField()
|
||||
promo_horizontal_mobile_url = serializers.SerializerMethodField()
|
||||
tile_horizontal_web_url = serializers.SerializerMethodField()
|
||||
tile_horizontal_mobile_url = serializers.SerializerMethodField()
|
||||
tile_vertical_web_url = serializers.SerializerMethodField()
|
||||
highlight_vertical_web_url = serializers.SerializerMethodField()
|
||||
editor_web_url = serializers.SerializerMethodField()
|
||||
editor_mobile_url = serializers.SerializerMethodField()
|
||||
|
||||
def get_preview_url(self, obj):
|
||||
"""Get crop preview."""
|
||||
return obj.instance.get_image_url('news_preview')
|
||||
|
||||
def get_promo_horizontal_web_url(self, obj):
|
||||
"""Get crop promo_horizontal_web."""
|
||||
return obj.instance.get_image_url('news_promo_horizontal_web')
|
||||
|
||||
def get_promo_horizontal_mobile_url(self, obj):
|
||||
"""Get crop promo_horizontal_mobile."""
|
||||
return obj.instance.get_image_url('news_promo_horizontal_mobile')
|
||||
|
||||
def get_tile_horizontal_web_url(self, obj):
|
||||
"""Get crop tile_horizontal_web."""
|
||||
return obj.instance.get_image_url('news_tile_horizontal_web')
|
||||
|
||||
def get_tile_horizontal_mobile_url(self, obj):
|
||||
"""Get crop tile_horizontal_mobile."""
|
||||
return obj.instance.get_image_url('news_tile_horizontal_mobile')
|
||||
|
||||
def get_tile_vertical_web_url(self, obj):
|
||||
"""Get crop tile_vertical_web."""
|
||||
return obj.instance.get_image_url('news_tile_vertical_web')
|
||||
|
||||
def get_highlight_vertical_web_url(self, obj):
|
||||
"""Get crop highlight_vertical_web."""
|
||||
return obj.instance.get_image_url('news_highlight_vertical_web')
|
||||
|
||||
def get_editor_web_url(self, obj):
|
||||
"""Get crop editor_web."""
|
||||
return obj.instance.get_image_url('news_editor_web')
|
||||
|
||||
def get_editor_mobile_url(self, obj):
|
||||
"""Get crop editor_mobile."""
|
||||
return obj.instance.get_image_url('news_editor_mobile')
|
||||
|
||||
|
||||
class NewsImageSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for returning crop images of news image."""
|
||||
orientation_display = serializers.CharField(source='get_orientation_display',
|
||||
read_only=True)
|
||||
web_url = serializers.SerializerMethodField()
|
||||
mobile_url = serializers.SerializerMethodField()
|
||||
original_url = serializers.URLField(source='image.url')
|
||||
auto_crop_images = CropImageSerializer(source='image', allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = Image
|
||||
|
|
@ -25,34 +73,8 @@ class NewsCropImageSerializer(ImageSerializer):
|
|||
'id',
|
||||
'title',
|
||||
'orientation_display',
|
||||
'web_url',
|
||||
'mobile_url',
|
||||
]
|
||||
extra_kwargs = {
|
||||
'orientation': {'write_only': True}
|
||||
}
|
||||
|
||||
def get_web_url(self, obj):
|
||||
"""Return URL of cropped image by thumbnail."""
|
||||
return obj.get_image_url('news_promo_horizontal_web')
|
||||
|
||||
def get_mobile_url(self, obj):
|
||||
"""Return URL of cropped image by thumbnail."""
|
||||
return obj.get_image_url('news_promo_horizontal_mobile')
|
||||
|
||||
|
||||
class NewsImageSerializer(ImageSerializer):
|
||||
"""News images"""
|
||||
url = serializers.URLField(source='image.url', read_only=True)
|
||||
crops = NewsCropImageSerializer(source='children', allow_null=True, many=True)
|
||||
|
||||
class Meta:
|
||||
model = Image
|
||||
fields = [
|
||||
'id',
|
||||
'title',
|
||||
'url',
|
||||
'crops',
|
||||
'original_url',
|
||||
'auto_crop_images',
|
||||
]
|
||||
extra_kwargs = {
|
||||
'orientation': {'write_only': True}
|
||||
|
|
@ -79,7 +101,7 @@ class NewsBaseSerializer(ProjectModelSerializer):
|
|||
# related fields
|
||||
news_type = NewsTypeSerializer(read_only=True)
|
||||
tags = MetaDataContentSerializer(read_only=True, many=True)
|
||||
gallery = NewsImageSerializer(source='original_images', read_only=True, many=True)
|
||||
gallery = NewsImageSerializer(read_only=True, many=True)
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
"""News app views."""
|
||||
from django.conf import settings
|
||||
from django.db.transaction import on_commit
|
||||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework import generics, permissions, status
|
||||
from rest_framework.response import Response
|
||||
|
||||
from gallery.tasks import delete_image
|
||||
from news import filters, models, serializers
|
||||
from gallery.serializers import ImageSerializer
|
||||
|
||||
|
||||
class NewsMixinView:
|
||||
|
|
@ -102,13 +104,14 @@ class NewsBackOfficeGalleryCreateDestroyView(NewsBackOfficeMixinView,
|
|||
def destroy(self, request, *args, **kwargs):
|
||||
"""Override destroy method."""
|
||||
gallery_obj = self.get_object()
|
||||
image_obj = gallery_obj.image
|
||||
|
||||
if settings.USE_CELERY:
|
||||
on_commit(lambda: delete_image.delay(image_id=gallery_obj.image.id,
|
||||
completely=False))
|
||||
else:
|
||||
on_commit(lambda: delete_image(image_id=gallery_obj.image.id,
|
||||
completely=False))
|
||||
# Delete an instances of NewsGallery model
|
||||
gallery_obj.delete()
|
||||
# Delete an instance of Image model
|
||||
image_obj.delete_from_remote_storage()
|
||||
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
|
|
@ -128,7 +131,7 @@ class NewsBackOfficeGalleryListView(NewsBackOfficeMixinView, generics.ListAPIVie
|
|||
|
||||
def get_queryset(self):
|
||||
"""Override get_queryset method."""
|
||||
return self.get_object().gallery.original_images()
|
||||
return self.get_object().gallery.all()
|
||||
|
||||
|
||||
class NewsBackOfficeRUDView(NewsBackOfficeMixinView,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
"""Utils app models."""
|
||||
import logging
|
||||
from os.path import exists
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.tokens import PasswordResetTokenGenerator
|
||||
from django.contrib.gis.db import models
|
||||
from django.contrib.postgres.fields import JSONField
|
||||
|
|
@ -9,12 +11,11 @@ from django.utils import timezone
|
|||
from django.utils.html import mark_safe
|
||||
from django.utils.translation import ugettext_lazy as _, get_language
|
||||
from easy_thumbnails.fields import ThumbnailerImageField
|
||||
from sorl.thumbnail import get_thumbnail
|
||||
from sorl.thumbnail.fields import ImageField as SORLImageField
|
||||
|
||||
from utils.methods import image_path, svg_image_path
|
||||
from utils.validators import svg_image_validator
|
||||
from sorl.thumbnail.fields import ImageField as SORLImageField
|
||||
from sorl.thumbnail import get_thumbnail
|
||||
from django.conf import settings
|
||||
from utils.methods import image_url_valid
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -123,7 +124,7 @@ class OAuthProjectMixin:
|
|||
|
||||
def get_source(self):
|
||||
"""Method to get of platform"""
|
||||
return NotImplemented
|
||||
return NotImplementedError
|
||||
|
||||
|
||||
class BaseAttributes(ProjectBaseMixin):
|
||||
|
|
@ -193,15 +194,18 @@ class SORLImageMixin(models.Model):
|
|||
"""Meta class."""
|
||||
abstract = True
|
||||
|
||||
def get_image(self, thumbnail_key=None):
|
||||
def get_image(self, thumbnail_key: str):
|
||||
"""Get thumbnail image file."""
|
||||
if thumbnail_key in settings.SORL_THUMBNAIL_ALIASES:
|
||||
return get_thumbnail(file_=self.image,
|
||||
**settings.SORL_THUMBNAIL_ALIASES[thumbnail_key])
|
||||
return get_thumbnail(
|
||||
file_=self.image,
|
||||
**settings.SORL_THUMBNAIL_ALIASES[thumbnail_key])
|
||||
|
||||
def get_image_url(self, thumbnail_key=None):
|
||||
def get_image_url(self, thumbnail_key: str):
|
||||
"""Get image thumbnail url."""
|
||||
return self.get_image(thumbnail_key).url
|
||||
crop_image = self.get_image(thumbnail_key)
|
||||
if hasattr(crop_image, 'url'):
|
||||
return self.get_image(thumbnail_key).url
|
||||
|
||||
def image_tag(self):
|
||||
"""Admin preview tag."""
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
"""Utils QuerySet Mixins"""
|
||||
from django.db import models
|
||||
from django.db.models import Q, Sum, F
|
||||
from functools import reduce
|
||||
from operator import add
|
||||
|
||||
from django.db import models
|
||||
from django.db.models import Q, F
|
||||
|
||||
from utils.methods import get_contenttype
|
||||
|
||||
|
||||
|
|
@ -50,7 +52,7 @@ class RelatedObjectsCountMixin(models.QuerySet):
|
|||
|
||||
def filter_all_related_gt(self, count):
|
||||
"""Queryset filter by all related objects count"""
|
||||
exp =reduce(add, [F(f"{related_object}_count") for related_object in self._get_related_objects_names()])
|
||||
exp = reduce(add, [F(f"{related_object}_count") for related_object in self._get_related_objects_names()])
|
||||
return self._annotate_related_objects_count()\
|
||||
.annotate(all_related_count=exp)\
|
||||
.filter(all_related_count__gt=count)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"""Local settings."""
|
||||
from .base import *
|
||||
import sys
|
||||
from .amazon_s3 import *
|
||||
|
||||
ALLOWED_HOSTS = ['*', ]
|
||||
|
||||
|
|
@ -23,8 +24,8 @@ CELERY_BROKER_URL = BROKER_URL
|
|||
|
||||
|
||||
# MEDIA
|
||||
MEDIA_URL = f'{SCHEMA_URI}://{DOMAIN_URI}/{MEDIA_LOCATION}/'
|
||||
MEDIA_ROOT = os.path.join(PUBLIC_ROOT, MEDIA_LOCATION)
|
||||
# MEDIA_URL = f'{SCHEMA_URI}://{DOMAIN_URI}/{MEDIA_LOCATION}/'
|
||||
# MEDIA_ROOT = os.path.join(PUBLIC_ROOT, MEDIA_LOCATION)
|
||||
|
||||
|
||||
# LOGGING
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user