Merge branch 'develop' into feature/gm-148
# Conflicts: # apps/account/tasks.py
This commit is contained in:
commit
7dbf594809
|
|
@ -214,6 +214,15 @@ class User(AbstractUser):
|
||||||
template_name=settings.RESETTING_TOKEN_TEMPLATE,
|
template_name=settings.RESETTING_TOKEN_TEMPLATE,
|
||||||
context=context)
|
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):
|
def confirm_email_template(self, country_code):
|
||||||
"""Get confirm email template"""
|
"""Get confirm email template"""
|
||||||
context = {'token': self.confirm_email_token,
|
context = {'token': self.confirm_email_token,
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,14 @@ class ChangePasswordSerializer(serializers.ModelSerializer):
|
||||||
except serializers.ValidationError as e:
|
except serializers.ValidationError as e:
|
||||||
raise serializers.ValidationError({'detail': e.detail})
|
raise serializers.ValidationError({'detail': e.detail})
|
||||||
else:
|
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
|
return attrs
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
"""Serializers for account web"""
|
"""Serializers for account web"""
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib.auth import password_validation as password_validators
|
from django.contrib.auth import password_validation as password_validators
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from account import models
|
from account import models, tasks
|
||||||
from utils import exceptions as utils_exceptions
|
from utils import exceptions as utils_exceptions
|
||||||
from utils.methods import username_validator
|
from utils.methods import username_validator
|
||||||
|
|
||||||
|
|
@ -68,4 +69,12 @@ class PasswordResetConfirmSerializer(serializers.ModelSerializer):
|
||||||
# Update user password from instance
|
# Update user password from instance
|
||||||
instance.set_password(validated_data.get('password'))
|
instance.set_password(validated_data.get('password'))
|
||||||
instance.save()
|
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
|
return instance
|
||||||
|
|
|
||||||
|
|
@ -1,47 +1,46 @@
|
||||||
"""Account app celery tasks."""
|
"""Account app celery tasks."""
|
||||||
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
from django.utils.translation import gettext_lazy as _
|
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)
|
logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.INFO)
|
||||||
logger = logging.getLogger(__name__)
|
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
|
@shared_task
|
||||||
def send_reset_password_email(user_id, country_code):
|
def send_reset_password_email(user_id, country_code):
|
||||||
"""Send email to user for reset password."""
|
"""Send email to user for reset password."""
|
||||||
try:
|
send_email(user_id, 'Password_resetting', 'reset_password_template', country_code)
|
||||||
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'TASK_NAME: {send_reset_password_email.__name__}\n'
|
|
||||||
f'DETAIL: Exception occurred for reset password: '
|
|
||||||
f'{user_id}')
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def confirm_new_email_address(user_id, country_code):
|
def confirm_new_email_address(user_id, country_code):
|
||||||
"""Send email to user new email."""
|
"""Send email to user new email."""
|
||||||
try:
|
send_email(user_id, 'Confirm new email address', 'confirm_email_template', country_code)
|
||||||
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'TASK_NAME: {confirm_new_email_address.__name__}\n'
|
|
||||||
f'DETAIL: Exception occurred for user: {user_id}')
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def change_email_address(user_id, country_code):
|
def change_email_address(user_id, country_code):
|
||||||
"""Send email to user new email."""
|
"""Send email to user new email."""
|
||||||
try:
|
send_email(user_id, 'Validate new email address', 'change_email_template', country_code)
|
||||||
user = models.User.objects.get(id=user_id)
|
|
||||||
user.send_email(subject=_('Validate new email address'),
|
|
||||||
message=user.change_email_template(country_code))
|
@shared_task
|
||||||
except:
|
def send_password_changed_email(user_id, country_code):
|
||||||
logger.error(f'TASK_NAME: {change_email_address.__name__}\n'
|
"""Send email which notifies user that his password had changed"""
|
||||||
f'DETAIL: Exception occurred for user: {user_id}')
|
send_email(user_id, 'Notify password changed', 'notify_password_changed_template', country_code)
|
||||||
|
|
|
||||||
18
apps/establishment/migrations/0041_auto_20191023_0920.py
Normal file
18
apps/establishment/migrations/0041_auto_20191023_0920.py
Normal file
|
|
@ -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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -352,8 +352,8 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
|
||||||
verbose_name=_('Collections'))
|
verbose_name=_('Collections'))
|
||||||
preview_image_url = models.URLField(verbose_name=_('Preview image URL path'),
|
preview_image_url = models.URLField(verbose_name=_('Preview image URL path'),
|
||||||
blank=True, null=True, default=None)
|
blank=True, null=True, default=None)
|
||||||
slug = models.SlugField(unique=True, max_length=50, null=True,
|
slug = models.SlugField(unique=True, max_length=255, null=True,
|
||||||
verbose_name=_('Establishment slug'), editable=True)
|
verbose_name=_('Establishment slug'))
|
||||||
|
|
||||||
awards = generic.GenericRelation(to='main.Award', related_query_name='establishment')
|
awards = generic.GenericRelation(to='main.Award', related_query_name='establishment')
|
||||||
tags = models.ManyToManyField('tag.Tag', related_name='establishments',
|
tags = models.ManyToManyField('tag.Tag', related_name='establishments',
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,12 @@ class EstablishmentMixinView:
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""Overridden method 'get_queryset'."""
|
"""Overridden method 'get_queryset'."""
|
||||||
return models.Establishment.objects.published() \
|
qs = models.Establishment.objects.published() \
|
||||||
.with_base_related() \
|
.with_base_related() \
|
||||||
.annotate_in_favorites(self.request.user)
|
.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):
|
class EstablishmentListView(EstablishmentMixinView, generics.ListAPIView):
|
||||||
|
|
@ -30,13 +33,6 @@ class EstablishmentListView(EstablishmentMixinView, generics.ListAPIView):
|
||||||
filter_class = filters.EstablishmentFilter
|
filter_class = filters.EstablishmentFilter
|
||||||
serializer_class = serializers.EstablishmentBaseSerializer
|
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):
|
class EstablishmentRetrieveView(EstablishmentMixinView, generics.RetrieveAPIView):
|
||||||
"""Resource for getting a establishment."""
|
"""Resource for getting a establishment."""
|
||||||
|
|
|
||||||
18
apps/main/migrations/0021_auto_20191023_0924.py
Normal file
18
apps/main/migrations/0021_auto_20191023_0924.py
Normal file
|
|
@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
37
apps/main/migrations/0022_auto_20191023_1113.py
Normal file
37
apps/main/migrations/0022_auto_20191023_1113.py
Normal file
|
|
@ -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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -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):
|
class SiteSettingsQuerySet(models.QuerySet):
|
||||||
"""Extended queryset for SiteSettings model."""
|
"""Extended queryset for SiteSettings model."""
|
||||||
|
|
||||||
|
|
@ -136,6 +152,7 @@ class SiteSettings(ProjectBaseMixin):
|
||||||
verbose_name=_('Config'))
|
verbose_name=_('Config'))
|
||||||
ad_config = models.TextField(blank=True, null=True, default=None,
|
ad_config = models.TextField(blank=True, null=True, default=None,
|
||||||
verbose_name=_('AD config'))
|
verbose_name=_('AD config'))
|
||||||
|
currency = models.ForeignKey(Currency, on_delete=models.PROTECT, null=True, default=None)
|
||||||
|
|
||||||
objects = SiteSettingsQuerySet.as_manager()
|
objects = SiteSettingsQuerySet.as_manager()
|
||||||
|
|
||||||
|
|
@ -183,7 +200,7 @@ class Page(models.Model):
|
||||||
class Feature(ProjectBaseMixin, PlatformMixin):
|
class Feature(ProjectBaseMixin, PlatformMixin):
|
||||||
"""Feature model."""
|
"""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)
|
priority = models.IntegerField(unique=True, null=True, default=None)
|
||||||
route = models.ForeignKey(Page, on_delete=models.PROTECT, null=True, default=None)
|
route = models.ForeignKey(Page, on_delete=models.PROTECT, null=True, default=None)
|
||||||
site_settings = models.ManyToManyField(SiteSettings, through='SiteFeature')
|
site_settings = models.ManyToManyField(SiteSettings, through='SiteFeature')
|
||||||
|
|
@ -258,18 +275,6 @@ class AwardType(models.Model):
|
||||||
return self.name
|
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):
|
class CarouselQuerySet(models.QuerySet):
|
||||||
"""Carousel QuerySet."""
|
"""Carousel QuerySet."""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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):
|
class SiteSettingsSerializer(serializers.ModelSerializer):
|
||||||
"""Site settings serializer."""
|
"""Site settings serializer."""
|
||||||
|
|
||||||
published_features = SiteFeatureSerializer(source='published_sitefeatures',
|
published_features = SiteFeatureSerializer(source='published_sitefeatures',
|
||||||
many=True, allow_null=True)
|
many=True, allow_null=True)
|
||||||
|
currency = CurrencySerializer()
|
||||||
# todo: remove this
|
# todo: remove this
|
||||||
country_code = serializers.CharField(source='subdomain', read_only=True)
|
country_code = serializers.CharField(source='subdomain', read_only=True)
|
||||||
|
|
||||||
|
|
@ -63,6 +76,7 @@ class SiteSettingsSerializer(serializers.ModelSerializer):
|
||||||
'config',
|
'config',
|
||||||
'ad_config',
|
'ad_config',
|
||||||
'published_features',
|
'published_features',
|
||||||
|
'currency'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -114,17 +128,6 @@ class AwardSerializer(AwardBaseSerializer):
|
||||||
fields = AwardBaseSerializer.Meta.fields + ['award_type', ]
|
fields = AwardBaseSerializer.Meta.fields + ['award_type', ]
|
||||||
|
|
||||||
|
|
||||||
class CurrencySerializer(serializers.ModelSerializer):
|
|
||||||
"""Currency serializer"""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = models.Currency
|
|
||||||
fields = [
|
|
||||||
'id',
|
|
||||||
'name'
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class CarouselListSerializer(serializers.ModelSerializer):
|
class CarouselListSerializer(serializers.ModelSerializer):
|
||||||
"""Serializer for retrieving list of carousel items."""
|
"""Serializer for retrieving list of carousel items."""
|
||||||
model_name = serializers.CharField()
|
model_name = serializers.CharField()
|
||||||
|
|
|
||||||
18
apps/news/migrations/0023_auto_20191023_0903.py
Normal file
18
apps/news/migrations/0023_auto_20191023_0903.py
Normal file
|
|
@ -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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -139,7 +139,7 @@ class News(BaseAttributes, TranslatedFieldsMixin):
|
||||||
start = models.DateTimeField(verbose_name=_('Start'))
|
start = models.DateTimeField(verbose_name=_('Start'))
|
||||||
end = models.DateTimeField(blank=True, null=True, default=None,
|
end = models.DateTimeField(blank=True, null=True, default=None,
|
||||||
verbose_name=_('End'))
|
verbose_name=_('End'))
|
||||||
slug = models.SlugField(unique=True, max_length=50,
|
slug = models.SlugField(unique=True, max_length=255,
|
||||||
verbose_name=_('News slug'))
|
verbose_name=_('News slug'))
|
||||||
playlist = models.IntegerField(_('playlist'))
|
playlist = models.IntegerField(_('playlist'))
|
||||||
state = models.PositiveSmallIntegerField(default=WAITING, choices=STATE_CHOICES,
|
state = models.PositiveSmallIntegerField(default=WAITING, choices=STATE_CHOICES,
|
||||||
|
|
|
||||||
7
apps/product/apps.py
Normal file
7
apps/product/apps.py
Normal file
|
|
@ -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')
|
||||||
110
apps/product/models.py
Normal file
110
apps/product/models.py
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
"""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')
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
from django.contrib import admin
|
|
||||||
|
|
||||||
# Register your models here.
|
|
||||||
|
|
@ -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')
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
# 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')
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
# Create your views here.
|
|
||||||
|
|
@ -64,6 +64,7 @@ PROJECT_APPS = [
|
||||||
'news.apps.NewsConfig',
|
'news.apps.NewsConfig',
|
||||||
'notification.apps.NotificationConfig',
|
'notification.apps.NotificationConfig',
|
||||||
'partner.apps.PartnerConfig',
|
'partner.apps.PartnerConfig',
|
||||||
|
# 'product.apps.ProductConfig', Uncomment after refining task and create migrations
|
||||||
'recipe.apps.RecipeConfig',
|
'recipe.apps.RecipeConfig',
|
||||||
'search_indexes.apps.SearchIndexesConfig',
|
'search_indexes.apps.SearchIndexesConfig',
|
||||||
'translation.apps.TranslationConfig',
|
'translation.apps.TranslationConfig',
|
||||||
|
|
@ -409,7 +410,8 @@ PASSWORD_RESET_TIMEOUT_DAYS = 1
|
||||||
RESETTING_TOKEN_TEMPLATE = 'account/password_reset_email.html'
|
RESETTING_TOKEN_TEMPLATE = 'account/password_reset_email.html'
|
||||||
CHANGE_EMAIL_TEMPLATE = 'account/change_email.html'
|
CHANGE_EMAIL_TEMPLATE = 'account/change_email.html'
|
||||||
CONFIRM_EMAIL_TEMPLATE = 'authorization/confirm_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
|
# COOKIES
|
||||||
|
|
|
||||||
7
project/templates/account/password_change_email.html
Normal file
7
project/templates/account/password_change_email.html
Normal file
|
|
@ -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 %}
|
||||||
Loading…
Reference in New Issue
Block a user