From d2075a1bf8e555f55272d2b3e5a381f1271a204b Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Fri, 20 Sep 2019 12:13:26 +0300 Subject: [PATCH 1/9] Initial app Product --- apps/{products => product}/__init__.py | 0 apps/product/apps.py | 7 +++++++ apps/{products => product}/migrations/__init__.py | 0 apps/{products => product}/models.py | 0 apps/{products => product}/serializers/__init__.py | 0 apps/{products => product}/serializers/common.py | 0 apps/{products => product}/serializers/mobile.py | 0 apps/{products => product}/serializers/web.py | 0 apps/{products => product}/urls/__init__.py | 0 apps/{products => product}/urls/common.py | 0 apps/{products => product}/urls/mobile.py | 0 apps/{products => product}/urls/web.py | 0 apps/{products => product}/views/__init__.py | 0 apps/{products => product}/views/common.py | 0 apps/{products => product}/views/mobile.py | 0 apps/{products => product}/views/web.py | 0 apps/products/admin.py | 3 --- apps/products/apps.py | 8 -------- apps/products/tests.py | 3 --- apps/products/views/views.py | 1 - project/settings/base.py | 1 + 21 files changed, 8 insertions(+), 15 deletions(-) rename apps/{products => product}/__init__.py (100%) create mode 100644 apps/product/apps.py rename apps/{products => product}/migrations/__init__.py (100%) rename apps/{products => product}/models.py (100%) rename apps/{products => product}/serializers/__init__.py (100%) rename apps/{products => product}/serializers/common.py (100%) rename apps/{products => product}/serializers/mobile.py (100%) rename apps/{products => product}/serializers/web.py (100%) rename apps/{products => product}/urls/__init__.py (100%) rename apps/{products => product}/urls/common.py (100%) rename apps/{products => product}/urls/mobile.py (100%) rename apps/{products => product}/urls/web.py (100%) rename apps/{products => product}/views/__init__.py (100%) rename apps/{products => product}/views/common.py (100%) rename apps/{products => product}/views/mobile.py (100%) rename apps/{products => product}/views/web.py (100%) delete mode 100644 apps/products/admin.py delete mode 100644 apps/products/apps.py delete mode 100644 apps/products/tests.py delete mode 100644 apps/products/views/views.py diff --git a/apps/products/__init__.py b/apps/product/__init__.py similarity index 100% rename from apps/products/__init__.py rename to apps/product/__init__.py diff --git a/apps/product/apps.py b/apps/product/apps.py new file mode 100644 index 00000000..7d1fc554 --- /dev/null +++ b/apps/product/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class ProductConfig(AppConfig): + name = 'product' + verbose_name = _('Product') diff --git a/apps/products/migrations/__init__.py b/apps/product/migrations/__init__.py similarity index 100% rename from apps/products/migrations/__init__.py rename to apps/product/migrations/__init__.py diff --git a/apps/products/models.py b/apps/product/models.py similarity index 100% rename from apps/products/models.py rename to apps/product/models.py diff --git a/apps/products/serializers/__init__.py b/apps/product/serializers/__init__.py similarity index 100% rename from apps/products/serializers/__init__.py rename to apps/product/serializers/__init__.py diff --git a/apps/products/serializers/common.py b/apps/product/serializers/common.py similarity index 100% rename from apps/products/serializers/common.py rename to apps/product/serializers/common.py diff --git a/apps/products/serializers/mobile.py b/apps/product/serializers/mobile.py similarity index 100% rename from apps/products/serializers/mobile.py rename to apps/product/serializers/mobile.py diff --git a/apps/products/serializers/web.py b/apps/product/serializers/web.py similarity index 100% rename from apps/products/serializers/web.py rename to apps/product/serializers/web.py diff --git a/apps/products/urls/__init__.py b/apps/product/urls/__init__.py similarity index 100% rename from apps/products/urls/__init__.py rename to apps/product/urls/__init__.py diff --git a/apps/products/urls/common.py b/apps/product/urls/common.py similarity index 100% rename from apps/products/urls/common.py rename to apps/product/urls/common.py diff --git a/apps/products/urls/mobile.py b/apps/product/urls/mobile.py similarity index 100% rename from apps/products/urls/mobile.py rename to apps/product/urls/mobile.py diff --git a/apps/products/urls/web.py b/apps/product/urls/web.py similarity index 100% rename from apps/products/urls/web.py rename to apps/product/urls/web.py diff --git a/apps/products/views/__init__.py b/apps/product/views/__init__.py similarity index 100% rename from apps/products/views/__init__.py rename to apps/product/views/__init__.py diff --git a/apps/products/views/common.py b/apps/product/views/common.py similarity index 100% rename from apps/products/views/common.py rename to apps/product/views/common.py diff --git a/apps/products/views/mobile.py b/apps/product/views/mobile.py similarity index 100% rename from apps/products/views/mobile.py rename to apps/product/views/mobile.py diff --git a/apps/products/views/web.py b/apps/product/views/web.py similarity index 100% rename from apps/products/views/web.py rename to apps/product/views/web.py diff --git a/apps/products/admin.py b/apps/products/admin.py deleted file mode 100644 index 8c38f3f3..00000000 --- a/apps/products/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/apps/products/apps.py b/apps/products/apps.py deleted file mode 100644 index 17d75292..00000000 --- a/apps/products/apps.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.apps import AppConfig -from django.utils.translation import gettext_lazy as _ - - -class ProductsConfig(AppConfig): - """Products model.""" - name = 'products' - verbose_name = _('products') diff --git a/apps/products/tests.py b/apps/products/tests.py deleted file mode 100644 index 7ce503c2..00000000 --- a/apps/products/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/apps/products/views/views.py b/apps/products/views/views.py deleted file mode 100644 index 60f00ef0..00000000 --- a/apps/products/views/views.py +++ /dev/null @@ -1 +0,0 @@ -# Create your views here. diff --git a/project/settings/base.py b/project/settings/base.py index 4aec6f4c..77701c8f 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -63,6 +63,7 @@ PROJECT_APPS = [ 'news.apps.NewsConfig', 'notification.apps.NotificationConfig', 'partner.apps.PartnerConfig', + 'product.apps.ProductConfig', 'recipe.apps.RecipeConfig', 'search_indexes.apps.SearchIndexesConfig', 'translation.apps.TranslationConfig', From 5562a9e7c981aa825aed03c0e1e58f3bb6e7fc32 Mon Sep 17 00:00:00 2001 From: Semyon Yekhmenin Date: Tue, 15 Oct 2019 12:16:23 +0300 Subject: [PATCH 2/9] Fixed establishments/slug/, added filtering by country code --- apps/establishment/views/web.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/establishment/views/web.py b/apps/establishment/views/web.py index 8f5d2a26..0d2249b1 100644 --- a/apps/establishment/views/web.py +++ b/apps/establishment/views/web.py @@ -45,7 +45,9 @@ class EstablishmentRetrieveView(EstablishmentMixinView, generics.RetrieveAPIView serializer_class = serializers.EstablishmentDetailSerializer def get_queryset(self): - return super().get_queryset().with_extended_related() + return super().get_queryset() \ + .by_country_code(self.request.country_code) \ + .with_extended_related() class EstablishmentRecentReviewListView(EstablishmentListView): From 9c09f000fb894566f02460300751d40b8baeea36 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Tue, 15 Oct 2019 16:32:47 +0300 Subject: [PATCH 3/9] Password changed notification email --- apps/account/models.py | 9 ++++ apps/account/serializers/common.py | 8 ++++ apps/account/serializers/web.py | 11 ++++- apps/account/tasks.py | 45 +++++++++---------- project/settings/base.py | 3 +- .../account/password_change_email.html | 7 +++ 6 files changed, 58 insertions(+), 25 deletions(-) create mode 100644 project/templates/account/password_change_email.html diff --git a/apps/account/models.py b/apps/account/models.py index 5052969e..aa49dc3b 100644 --- a/apps/account/models.py +++ b/apps/account/models.py @@ -202,6 +202,15 @@ class User(AbstractUser): template_name=settings.RESETTING_TOKEN_TEMPLATE, context=context) + def notify_password_changed_template(self, country_code): + """Get notification email template""" + context = {'contry_code': country_code} + context.update(self.base_template) + return render_to_string( + template_name=settings.NOTIFICATION_PASSWORD_TEMPLATE, + context=context, + ) + def confirm_email_template(self, country_code): """Get confirm email template""" context = {'token': self.confirm_email_token, diff --git a/apps/account/serializers/common.py b/apps/account/serializers/common.py index ad232eae..d68cfe56 100644 --- a/apps/account/serializers/common.py +++ b/apps/account/serializers/common.py @@ -127,6 +127,14 @@ class ChangePasswordSerializer(serializers.ModelSerializer): except serializers.ValidationError as e: raise serializers.ValidationError({'detail': e.detail}) else: + if settings.USE_CELERY: + tasks.send_password_changed_email( + user_id=self.instance.id, + country_code=self.context.get('request').country_code) + else: + tasks.send_password_changed_email( + user_id=self.instance.id, + country_code=self.context.get('request').country_code) return attrs def update(self, instance, validated_data): diff --git a/apps/account/serializers/web.py b/apps/account/serializers/web.py index 8be73afa..2f04b31f 100644 --- a/apps/account/serializers/web.py +++ b/apps/account/serializers/web.py @@ -1,8 +1,9 @@ """Serializers for account web""" +from django.conf import settings from django.contrib.auth import password_validation as password_validators from rest_framework import serializers -from account import models +from account import models, tasks from utils import exceptions as utils_exceptions from utils.methods import username_validator @@ -68,4 +69,12 @@ class PasswordResetConfirmSerializer(serializers.ModelSerializer): # Update user password from instance instance.set_password(validated_data.get('password')) instance.save() + if settings.USE_CELERY: + tasks.send_password_changed_email( + user_id=instance.id, + country_code=self.context.get('request').country_code) + else: + tasks.send_password_changed_email( + user_id=instance.id, + country_code=self.context.get('request').country_code) return instance diff --git a/apps/account/tasks.py b/apps/account/tasks.py index 03a231b3..d9fa7bb7 100644 --- a/apps/account/tasks.py +++ b/apps/account/tasks.py @@ -1,47 +1,46 @@ """Account app celery tasks.""" +import inspect import logging from celery import shared_task from django.utils.translation import gettext_lazy as _ -from . import models +from account.models import User logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.INFO) logger = logging.getLogger(__name__) +def send_email(user_id: int, subject: str, message_prop: str, country_code: str): + try: + user = User.objects.get(id=user_id) + user.send_email(subject=_(subject), + message=getattr(user, message_prop, lambda _: '')(country_code)) + except: + cur_frame = inspect.currentframe() + cal_frame = inspect.getouterframes(cur_frame, 2) + logger.error(f'METHOD_NAME: {cal_frame[1][3]}\n' + f'DETAIL: Exception occurred for user: {user_id}') + @shared_task def send_reset_password_email(user_id, country_code): """Send email to user for reset password.""" - try: - user = models.User.objects.get(id=user_id) - 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' - f'DETAIL: Exception occurred for reset password: ' - f'{user_id}') + send_email(user_id, 'Password_resetting', 'reset_password_template', country_code) @shared_task def confirm_new_email_address(user_id, country_code): """Send email to user new email.""" - try: - user = models.User.objects.get(id=user_id) - 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' - f'DETAIL: Exception occurred for user: {user_id}') + send_email(user_id, 'Confirm new email address', 'confirm_email_template', country_code) @shared_task def change_email_address(user_id, country_code): """Send email to user new email.""" - try: - user = models.User.objects.get(id=user_id) - 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' - f'DETAIL: Exception occurred for user: {user_id}') + send_email(user_id, 'Validate new email address', 'change_email_template', country_code) + + +@shared_task +def send_password_changed_email(user_id, country_code): + """Send email which notifies user that his password had changed""" + send_email(user_id, 'Notify password changed', 'notify_password_changed_template', country_code) diff --git a/project/settings/base.py b/project/settings/base.py index 06f83811..8bb470a6 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -406,7 +406,8 @@ PASSWORD_RESET_TIMEOUT_DAYS = 1 RESETTING_TOKEN_TEMPLATE = 'account/password_reset_email.html' CHANGE_EMAIL_TEMPLATE = 'account/change_email.html' CONFIRM_EMAIL_TEMPLATE = 'authorization/confirm_email.html' -NEWS_EMAIL_TEMPLATE = "news/news_email.html" +NEWS_EMAIL_TEMPLATE = 'news/news_email.html' +NOTIFICATION_PASSWORD_TEMPLATE = 'account/password_change_email.html' # COOKIES diff --git a/project/templates/account/password_change_email.html b/project/templates/account/password_change_email.html new file mode 100644 index 00000000..30dd2aac --- /dev/null +++ b/project/templates/account/password_change_email.html @@ -0,0 +1,7 @@ +{% load i18n %}{% autoescape off %} +{% blocktrans %}You're receiving this email because your account's password address at {{ site_name }}.{% endblocktrans %} + +{% trans "Thanks for using our site!" %} + +{% blocktrans %}The {{ site_name }} team{% endblocktrans %} +{% endautoescape %} \ No newline at end of file From 766b6659f1b9a02f8cb232372d6d3f57a6cb2e69 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Wed, 23 Oct 2019 11:31:45 +0300 Subject: [PATCH 4/9] Models created: Product, OnlineProduct (proxy), ProductType, ProductSubtype --- apps/product/models.py | 173 +++++++++++++++++++++++++-------------- project/settings/base.py | 2 +- 2 files changed, 111 insertions(+), 64 deletions(-) diff --git a/apps/product/models.py b/apps/product/models.py index 3c0e6ee8..41f0c7c6 100644 --- a/apps/product/models.py +++ b/apps/product/models.py @@ -1,63 +1,110 @@ -# from django.contrib.postgres.fields import JSONField -# from django.db import models -# from django.utils.translation import gettext_lazy as _ -# -# from utils.models import BaseAttributes -# -# -# class ProductManager(models.Manager): -# """Product manager.""" -# -# -# class ProductQuerySet(models.QuerySet): -# """Product queryset.""" -# -# -# class Product(BaseAttributes): -# """Product models.""" -# name = models.CharField(_('name'), max_length=255) -# country = models.ForeignKey('location.Country', on_delete=models.CASCADE) -# region = models.ForeignKey('location.Region', on_delete=models.CASCADE) -# # ASK: What is the "subregion" -# -# description = JSONField(_('description')) -# characteristics = JSONField(_('characteristics')) -# metadata_values = JSONField(_('metadata_values')) -# # common_relations_id -# # product_region_id -# code = models.CharField(_('code'), max_length=255) -# available = models.BooleanField(_('available')) -# -# # dealer_type -# # target_scope -# # target_type -# # rank -# # excluding_tax_unit_price -# # column_21 -# # currencies_id -# # vintage -# # producer_price -# # producer_description -# # annual_produced_quantity -# # production_method_description -# # unit_name -# # unit -# # unit_values -# # organic_source -# # certificates -# # establishments_id -# # restrictions -# # -# objects = ProductManager.from_queryset(ProductQuerySet)() -# -# class Meta: -# verbose_name = _('product') -# verbose_name_plural = _('products') -# -# -# class ProductType(models.Model): -# """ProductType model.""" -# -# class Meta: -# verbose_name_plural = _('product types') -# verbose_name = _('product type') +"""Product app models.""" +from django.db import models +from django.contrib.postgres.fields import JSONField +from django.utils.translation import gettext_lazy as _ +from utils.models import (BaseAttributes, ProjectBaseMixin, + TranslatedFieldsMixin, TJSONField) + + +class ProductType(TranslatedFieldsMixin, ProjectBaseMixin): + """ProductType model.""" + + name = TJSONField(blank=True, null=True, default=None, + verbose_name=_('Name'), help_text='{"en-GB":"some text"}') + index_name = models.CharField(max_length=50, unique=True, db_index=True, + verbose_name=_('Index name')) + use_subtypes = models.BooleanField(_('Use subtypes'), default=True) + + class Meta: + """Meta class.""" + + verbose_name = _('Product type') + verbose_name_plural = _('Product types') + + +class ProductSubType(TranslatedFieldsMixin, ProjectBaseMixin): + """ProductSubtype model.""" + + product_type = models.ForeignKey(ProductType, on_delete=models.CASCADE, + related_name='subtypes', + verbose_name=_('Product type')) + name = TJSONField(blank=True, null=True, default=None, + verbose_name=_('Name'), help_text='{"en-GB":"some text"}') + index_name = models.CharField(max_length=50, unique=True, db_index=True, + verbose_name=_('Index name')) + + class Meta: + """Meta class.""" + + verbose_name = _('Product type') + verbose_name_plural = _('Product types') + + +class ProductManager(models.Manager): + """Extended manager for Product model.""" + + +class ProductQuerySet(models.QuerySet): + """Product queryset.""" + + def common(self): + return self.filter(category=self.model.COMMON) + + def online(self): + return self.filter(category=self.model.ONLINE) + + +class Product(TranslatedFieldsMixin, BaseAttributes): + """Product models.""" + + COMMON = 0 + ONLINE = 1 + + CATEGORY_CHOICES = ( + (COMMON, _('Common')), + (ONLINE, _('Online')), + ) + + category = models.PositiveIntegerField(choices=CATEGORY_CHOICES, + default=COMMON) + name = TJSONField(_('Name'), 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"}') + characteristics = JSONField(_('Characteristics')) + country = models.ForeignKey('location.Country', on_delete=models.PROTECT, + verbose_name=_('Country')) + available = models.BooleanField(_('Available'), default=True) + type = models.ForeignKey(ProductType, on_delete=models.PROTECT, + related_name='products', verbose_name=_('Type')) + subtypes = models.ManyToManyField(ProductSubType, related_name='products', + verbose_name=_('Subtypes')) + + objects = ProductManager.from_queryset(ProductQuerySet)() + + class Meta: + """Meta class.""" + + verbose_name = _('Product') + verbose_name_plural = _('Products') + + +class OnlineProductManager(ProductManager): + """Extended manger for OnlineProduct model.""" + + def get_queryset(self): + """Overrided get_queryset method.""" + return super().get_queryset().online() + + +class OnlineProduct(Product): + """Online product.""" + + objects = OnlineProductManager.from_queryset(ProductQuerySet)() + + class Meta: + """Meta class.""" + + proxy = True + verbose_name = _('Online product') + verbose_name_plural = _('Online products') diff --git a/project/settings/base.py b/project/settings/base.py index 72fbd05f..448f6318 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -64,7 +64,7 @@ PROJECT_APPS = [ 'news.apps.NewsConfig', 'notification.apps.NotificationConfig', 'partner.apps.PartnerConfig', - 'product.apps.ProductConfig', + # 'product.apps.ProductConfig', Uncomment after refining task and create migrations 'recipe.apps.RecipeConfig', 'search_indexes.apps.SearchIndexesConfig', 'translation.apps.TranslationConfig', From 64692ae4b371b4f61c6a28d3e31b0f7732fc4b7c Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Wed, 23 Oct 2019 12:03:50 +0300 Subject: [PATCH 5/9] max length of slug is 255 --- .../news/migrations/0023_auto_20191023_0903.py | 18 ++++++++++++++++++ apps/news/models.py | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 apps/news/migrations/0023_auto_20191023_0903.py diff --git a/apps/news/migrations/0023_auto_20191023_0903.py b/apps/news/migrations/0023_auto_20191023_0903.py new file mode 100644 index 00000000..e1380b30 --- /dev/null +++ b/apps/news/migrations/0023_auto_20191023_0903.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.4 on 2019-10-23 09:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0022_auto_20191021_1306'), + ] + + operations = [ + migrations.AlterField( + model_name='news', + name='slug', + field=models.SlugField(max_length=255, unique=True, verbose_name='News slug'), + ), + ] diff --git a/apps/news/models.py b/apps/news/models.py index c86187d5..9e2a2926 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -139,7 +139,7 @@ class News(BaseAttributes, TranslatedFieldsMixin): start = models.DateTimeField(verbose_name=_('Start')) end = models.DateTimeField(blank=True, null=True, default=None, verbose_name=_('End')) - slug = models.SlugField(unique=True, max_length=50, + slug = models.SlugField(unique=True, max_length=255, verbose_name=_('News slug')) playlist = models.IntegerField(_('playlist')) state = models.PositiveSmallIntegerField(default=WAITING, choices=STATE_CHOICES, From 9c9c49d9008c5ca190de4bbd7b1c262a849d9bc4 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Wed, 23 Oct 2019 12:25:50 +0300 Subject: [PATCH 6/9] update slug fields --- .../migrations/0041_auto_20191023_0920.py | 18 ++++++++++++++++++ apps/establishment/models.py | 4 ++-- .../main/migrations/0021_auto_20191023_0924.py | 18 ++++++++++++++++++ apps/main/models.py | 2 +- 4 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 apps/establishment/migrations/0041_auto_20191023_0920.py create mode 100644 apps/main/migrations/0021_auto_20191023_0924.py diff --git a/apps/establishment/migrations/0041_auto_20191023_0920.py b/apps/establishment/migrations/0041_auto_20191023_0920.py new file mode 100644 index 00000000..dc5b2e02 --- /dev/null +++ b/apps/establishment/migrations/0041_auto_20191023_0920.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.4 on 2019-10-23 09:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('establishment', '0040_employee_tags'), + ] + + operations = [ + migrations.AlterField( + model_name='establishment', + name='slug', + field=models.SlugField(max_length=255, null=True, unique=True, verbose_name='Establishment slug'), + ), + ] diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 68acb68f..1406e552 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -349,8 +349,8 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): verbose_name=_('Collections')) preview_image_url = models.URLField(verbose_name=_('Preview image URL path'), blank=True, null=True, default=None) - slug = models.SlugField(unique=True, max_length=50, null=True, - verbose_name=_('Establishment slug'), editable=True) + slug = models.SlugField(unique=True, max_length=255, null=True, + verbose_name=_('Establishment slug')) awards = generic.GenericRelation(to='main.Award', related_query_name='establishment') tags = models.ManyToManyField('tag.Tag', related_name='establishments', diff --git a/apps/main/migrations/0021_auto_20191023_0924.py b/apps/main/migrations/0021_auto_20191023_0924.py new file mode 100644 index 00000000..6bd6e1d6 --- /dev/null +++ b/apps/main/migrations/0021_auto_20191023_0924.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.4 on 2019-10-23 09:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0020_merge_20191023_0750'), + ] + + operations = [ + migrations.AlterField( + model_name='feature', + name='slug', + field=models.SlugField(max_length=255, unique=True), + ), + ] diff --git a/apps/main/models.py b/apps/main/models.py index 1bb6ace3..e5d947fd 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -182,7 +182,7 @@ class Page(models.Model): class Feature(ProjectBaseMixin, PlatformMixin): """Feature model.""" - slug = models.CharField(max_length=255, unique=True) + 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) site_settings = models.ManyToManyField(SiteSettings, through='SiteFeature') From 083c8a7eaa258a753397ddc5cd476e7d4aeb8a94 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Wed, 23 Oct 2019 13:00:39 +0300 Subject: [PATCH 7/9] same queryset for Establishment list and detail views --- apps/establishment/views/web.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/apps/establishment/views/web.py b/apps/establishment/views/web.py index 1a0d5f58..0699d9d0 100644 --- a/apps/establishment/views/web.py +++ b/apps/establishment/views/web.py @@ -19,9 +19,12 @@ class EstablishmentMixinView: def get_queryset(self): """Overridden method 'get_queryset'.""" - return models.Establishment.objects.published() \ - .with_base_related() \ - .annotate_in_favorites(self.request.user) + qs = models.Establishment.objects.published() \ + .with_base_related() \ + .annotate_in_favorites(self.request.user) + if self.request.country_code: + qs = qs.by_country_code(self.request.country_code) + return qs class EstablishmentListView(EstablishmentMixinView, generics.ListAPIView): @@ -30,13 +33,6 @@ class EstablishmentListView(EstablishmentMixinView, generics.ListAPIView): filter_class = filters.EstablishmentFilter serializer_class = serializers.EstablishmentBaseSerializer - def get_queryset(self): - """Overridden method 'get_queryset'.""" - qs = super(EstablishmentListView, self).get_queryset() - if self.request.country_code: - qs = qs.by_country_code(self.request.country_code) - return qs - class EstablishmentRetrieveView(EstablishmentMixinView, generics.RetrieveAPIView): """Resource for getting a establishment.""" @@ -45,9 +41,7 @@ class EstablishmentRetrieveView(EstablishmentMixinView, generics.RetrieveAPIView serializer_class = serializers.EstablishmentDetailSerializer def get_queryset(self): - return super().get_queryset() \ - .by_country_code(self.request.country_code) \ - .with_extended_related() + return super().get_queryset().with_extended_related() class EstablishmentRecentReviewListView(EstablishmentListView): From 538ae41d850347a4597c2c2c5407d802cefe6d7b Mon Sep 17 00:00:00 2001 From: Semyon Yekhmenin Date: Wed, 23 Oct 2019 10:06:39 +0000 Subject: [PATCH 8/9] Establishments/slug/ fix --- apps/establishment/views/web.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/apps/establishment/views/web.py b/apps/establishment/views/web.py index d3cc91cb..0699d9d0 100644 --- a/apps/establishment/views/web.py +++ b/apps/establishment/views/web.py @@ -19,9 +19,12 @@ class EstablishmentMixinView: def get_queryset(self): """Overridden method 'get_queryset'.""" - return models.Establishment.objects.published() \ - .with_base_related() \ - .annotate_in_favorites(self.request.user) + qs = models.Establishment.objects.published() \ + .with_base_related() \ + .annotate_in_favorites(self.request.user) + if self.request.country_code: + qs = qs.by_country_code(self.request.country_code) + return qs class EstablishmentListView(EstablishmentMixinView, generics.ListAPIView): @@ -30,13 +33,6 @@ class EstablishmentListView(EstablishmentMixinView, generics.ListAPIView): filter_class = filters.EstablishmentFilter serializer_class = serializers.EstablishmentBaseSerializer - def get_queryset(self): - """Overridden method 'get_queryset'.""" - qs = super(EstablishmentListView, self).get_queryset() - if self.request.country_code: - qs = qs.by_country_code(self.request.country_code) - return qs - class EstablishmentRetrieveView(EstablishmentMixinView, generics.RetrieveAPIView): """Resource for getting a establishment.""" From 81bd7b491230855d42551c7d7e15322445f8ba49 Mon Sep 17 00:00:00 2001 From: Semyon Yekhmenin Date: Wed, 23 Oct 2019 11:58:33 +0000 Subject: [PATCH 9/9] Feature/currency sign --- .../migrations/0022_auto_20191023_1113.py | 37 +++++++++++++++++++ apps/main/models.py | 29 +++++++++------ apps/main/serializers.py | 25 +++++++------ 3 files changed, 68 insertions(+), 23 deletions(-) create mode 100644 apps/main/migrations/0022_auto_20191023_1113.py diff --git a/apps/main/migrations/0022_auto_20191023_1113.py b/apps/main/migrations/0022_auto_20191023_1113.py new file mode 100644 index 00000000..0a190b23 --- /dev/null +++ b/apps/main/migrations/0022_auto_20191023_1113.py @@ -0,0 +1,37 @@ +# Generated by Django 2.2.4 on 2019-10-23 11:13 + +from django.db import migrations, models +import django.db.models.deletion +import utils.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0021_auto_20191023_0924'), + ] + + operations = [ + migrations.AddField( + model_name='currency', + name='sign', + field=models.CharField(default='?', max_length=1, verbose_name='sign'), + preserve_default=False, + ), + migrations.AddField( + model_name='currency', + name='slug', + field=models.SlugField(default='?', max_length=255, unique=True), + preserve_default=False, + ), + migrations.AddField( + model_name='sitesettings', + name='currency', + field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='main.Currency'), + ), + migrations.AlterField( + model_name='currency', + name='name', + field=utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='name'), + ), + ] diff --git a/apps/main/models.py b/apps/main/models.py index e5d947fd..bbdd6386 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -106,6 +106,22 @@ from utils.querysets import ContentTypeQuerySetMixin # +class Currency(models.Model, TranslatedFieldsMixin): + """Currency model.""" + name = TJSONField( + _('name'), null=True, blank=True, + default=None, help_text='{"en-GB":"some text"}') + sign = models.CharField(_('sign'), max_length=1) + slug = models.SlugField(max_length=255, unique=True) + + class Meta: + verbose_name = _('currency') + verbose_name_plural = _('currencies') + + def __str__(self): + return f'{self.name}' + + class SiteSettingsQuerySet(models.QuerySet): """Extended queryset for SiteSettings model.""" @@ -135,6 +151,7 @@ class SiteSettings(ProjectBaseMixin): verbose_name=_('Config')) ad_config = models.TextField(blank=True, null=True, default=None, verbose_name=_('AD config')) + currency = models.ForeignKey(Currency, on_delete=models.PROTECT, null=True, default=None) objects = SiteSettingsQuerySet.as_manager() @@ -257,18 +274,6 @@ class AwardType(models.Model): return self.name -class Currency(models.Model): - """Currency model.""" - name = models.CharField(_('name'), max_length=50) - - class Meta: - verbose_name = _('currency') - verbose_name_plural = _('currencies') - - def __str__(self): - return f'{self.name}' - - class CarouselQuerySet(models.QuerySet): """Carousel QuerySet.""" diff --git a/apps/main/serializers.py b/apps/main/serializers.py index e2523fa9..0e65741e 100644 --- a/apps/main/serializers.py +++ b/apps/main/serializers.py @@ -40,11 +40,24 @@ class SiteFeatureSerializer(serializers.ModelSerializer): ) +class CurrencySerializer(serializers.ModelSerializer): + """Currency serializer""" + + class Meta: + model = models.Currency + fields = [ + 'id', + 'name_translated', + 'sign' + ] + + class SiteSettingsSerializer(serializers.ModelSerializer): """Site settings serializer.""" published_features = SiteFeatureSerializer(source='published_sitefeatures', many=True, allow_null=True) + currency = CurrencySerializer() # todo: remove this country_code = serializers.CharField(source='subdomain', read_only=True) @@ -63,6 +76,7 @@ class SiteSettingsSerializer(serializers.ModelSerializer): 'config', 'ad_config', 'published_features', + 'currency' ) @@ -114,17 +128,6 @@ class AwardSerializer(AwardBaseSerializer): fields = AwardBaseSerializer.Meta.fields + ['award_type', ] -class CurrencySerializer(serializers.ModelSerializer): - """Currency serializer""" - - class Meta: - model = models.Currency - fields = [ - 'id', - 'name' - ] - - class CarouselListSerializer(serializers.ModelSerializer): """Serializer for retrieving list of carousel items.""" model_name = serializers.CharField()