diff --git a/apps/account/migrations/0025_auto_20191210_0623.py b/apps/account/migrations/0025_auto_20191210_0623.py new file mode 100644 index 00000000..464e9b20 --- /dev/null +++ b/apps/account/migrations/0025_auto_20191210_0623.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.7 on 2019-12-10 06:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0024_role_establishment_subtype'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='city', + field=models.TextField(blank=True, default=None, null=True, verbose_name='User last visited from city'), + ), + migrations.AddField( + model_name='user', + name='locale', + field=models.CharField(blank=True, default=None, max_length=10, null=True, verbose_name='User last used locale'), + ), + ] diff --git a/apps/account/models.py b/apps/account/models.py index c5031ed7..280260df 100644 --- a/apps/account/models.py +++ b/apps/account/models.py @@ -110,6 +110,10 @@ class User(AbstractUser): email_confirmed = models.BooleanField(_('email status'), default=False) newsletter = models.NullBooleanField(default=True) old_id = models.IntegerField(null=True, blank=True, default=None) + locale = models.CharField(max_length=10, blank=True, default=None, null=True, + verbose_name=_('User last used locale')) + city = models.TextField(default=None, blank=True, null=True, + verbose_name=_('User last visited from city')) EMAIL_FIELD = 'email' USERNAME_FIELD = 'username' diff --git a/apps/account/serializers/back.py b/apps/account/serializers/back.py index 15e0684e..01889411 100644 --- a/apps/account/serializers/back.py +++ b/apps/account/serializers/back.py @@ -33,12 +33,14 @@ class BackUserSerializer(serializers.ModelSerializer): 'email_confirmed', 'newsletter', 'roles', - 'password' + 'password', + 'city', + 'locale', ) extra_kwargs = { - 'password': {'write_only': True} + 'password': {'write_only': True}, } - read_only_fields = ('old_password', 'last_login', 'date_joined') + read_only_fields = ('old_password', 'last_login', 'date_joined', 'city', 'locale') def create(self, validated_data): user = super().create(validated_data) diff --git a/apps/main/management/commands/add_footers.py b/apps/main/management/commands/add_footers.py new file mode 100644 index 00000000..5982fd43 --- /dev/null +++ b/apps/main/management/commands/add_footers.py @@ -0,0 +1,32 @@ +from django.core.management.base import BaseCommand +from tqdm import tqdm + +from main.models import SiteSettings, Footer +from transfer.models import Footers + + +class Command(BaseCommand): + help = '''Add footers from legacy DB.''' + + def handle(self, *args, **kwargs): + objects = [] + deleted = 0 + footers_list = Footers.objects.all() + + for old_footer in tqdm(footers_list, desc='Add footers'): + site = SiteSettings.objects.filter(old_id=old_footer.site_id).first() + if site: + if site.footers.exists(): + site.footers.all().delete() + deleted += 1 + footer = Footer( + site=site, + about_us=old_footer.about_us, + copyright=old_footer.copyright, + created=old_footer.created_at, + modified=old_footer.updated_at + ) + objects.append(footer) + Footer.objects.bulk_create(objects) + self.stdout.write( + self.style.WARNING(f'Created {len(objects)}/Deleted {deleted} footer objects.')) diff --git a/apps/main/migrations/0040_footer.py b/apps/main/migrations/0040_footer.py new file mode 100644 index 00000000..688e76d1 --- /dev/null +++ b/apps/main/migrations/0040_footer.py @@ -0,0 +1,29 @@ +# Generated by Django 2.2.7 on 2019-12-09 13:21 + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0039_sitefeature_old_id'), + ] + + operations = [ + migrations.CreateModel( + name='Footer', + 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')), + ('about_us', models.TextField(verbose_name='about_us')), + ('copyright', models.TextField(verbose_name='copyright')), + ('site', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='footers', to='main.SiteSettings', verbose_name='footer')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/main/models.py b/apps/main/models.py index f9c1225f..0869169e 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -351,3 +351,12 @@ class PageType(ProjectBaseMixin): def __str__(self): """Overridden dunder method.""" return self.name + + +class Footer(ProjectBaseMixin): + site = models.ForeignKey( + 'main.SiteSettings', related_name='footers', verbose_name=_('footer'), + on_delete=models.PROTECT + ) + about_us = models.TextField(_('about_us')) + copyright = models.TextField(_('copyright')) diff --git a/apps/main/serializers.py b/apps/main/serializers.py index a41a643f..033da976 100644 --- a/apps/main/serializers.py +++ b/apps/main/serializers.py @@ -22,6 +22,7 @@ class FeatureSerializer(serializers.ModelSerializer): 'site_settings', ) + class CurrencySerializer(ProjectModelSerializer): """Currency serializer.""" @@ -36,6 +37,33 @@ class CurrencySerializer(ProjectModelSerializer): ] +class FooterSerializer(serializers.ModelSerializer): + """Footer serializer.""" + + class Meta: + model = models.Footer + fields = [ + 'id', + 'about_us', + 'copyright', + 'created', + 'modified', + ] + + +class FooterBackSerializer(FooterSerializer): + site_id = serializers.PrimaryKeyRelatedField( + queryset=models.SiteSettings.objects.all(), + source='site' + ) + + class Meta: + model = models.Footer + fields = FooterSerializer.Meta.fields + [ + 'site_id' + ] + + class SiteFeatureSerializer(serializers.ModelSerializer): id = serializers.IntegerField(source='feature.id') slug = serializers.CharField(source='feature.slug') @@ -68,6 +96,7 @@ class SiteSettingsSerializer(serializers.ModelSerializer): country_name = serializers.CharField(source='country.name_translated', read_only=True) time_format = serializers.CharField(source='country.time_format', read_only=True) + footers = FooterSerializer(many=True, read_only=True) class Meta: """Meta class.""" @@ -87,6 +116,7 @@ class SiteSettingsSerializer(serializers.ModelSerializer): 'published_features', 'currency', 'country_name', + 'footers', ] diff --git a/apps/main/urls/back.py b/apps/main/urls/back.py index 609e61f7..a9e55311 100644 --- a/apps/main/urls/back.py +++ b/apps/main/urls/back.py @@ -18,6 +18,8 @@ urlpatterns = [ name='site-feature-list-create'), path('site-feature//', views.SiteFeatureRUDBackView.as_view(), name='site-feature-rud'), + path('footer/', views.FooterBackView.as_view(), name='footer-list-create'), + path('footer//', views.FooterRUDBackView.as_view(), name='footer-rud'), ] diff --git a/apps/main/views/back.py b/apps/main/views/back.py index 1faf79a0..3d73f88c 100644 --- a/apps/main/views/back.py +++ b/apps/main/views/back.py @@ -4,7 +4,7 @@ from rest_framework import generics, permissions from main import serializers from main.filters import AwardFilter -from main.models import Award +from main.models import Award, Footer from main.views import SiteSettingsView, SiteListView @@ -67,3 +67,17 @@ class SiteSettingsBackOfficeView(SiteSettingsView): class SiteListBackOfficeView(SiteListView): """Site settings View.""" serializer_class = serializers.SiteSerializer + + +class FooterBackView(generics.ListCreateAPIView): + """Footer back list/create view.""" + permission_classes = (permissions.IsAuthenticatedOrReadOnly,) + serializer_class = serializers.FooterBackSerializer + queryset = Footer.objects.all() + + +class FooterRUDBackView(generics.RetrieveUpdateDestroyAPIView): + """Footer back RUD view.""" + permission_classes = (permissions.IsAuthenticatedOrReadOnly,) + serializer_class = serializers.FooterBackSerializer + queryset = Footer.objects.all() diff --git a/apps/product/serializers/common.py b/apps/product/serializers/common.py index 51ff8e1e..e0617e63 100644 --- a/apps/product/serializers/common.py +++ b/apps/product/serializers/common.py @@ -115,6 +115,7 @@ class ProductBaseSerializer(serializers.ModelSerializer): 'wine_regions', 'wine_colors', 'in_favorites', + 'wine_origins', ] diff --git a/apps/search_indexes/documents/tag_category.py b/apps/search_indexes/documents/tag_category.py index ed69c8b7..757483bf 100644 --- a/apps/search_indexes/documents/tag_category.py +++ b/apps/search_indexes/documents/tag_category.py @@ -2,6 +2,7 @@ from django.conf import settings from django_elasticsearch_dsl import Document, Index, fields from tag import models +from news.models import News TagCategoryIndex = Index(settings.ELASTICSEARCH_INDEX_NAMES.get(__name__, 'tag_category')) TagCategoryIndex.settings(number_of_shards=2, number_of_replicas=2) @@ -26,8 +27,20 @@ class TagCategoryDocument(Document): 'public', 'value_type' ) - related_models = [models.Tag] + related_models = [models.Tag, News] def get_queryset(self): return super().get_queryset().with_base_related() + + def get_instances_from_related(self, related_instance): + """If related_models is set, define how to retrieve the Car instance(s) from the related model. + The related_models option should be used with caution because it can lead in the index + to the updating of a lot of items. + """ + if isinstance(related_instance, News): + tag_categories = [] + for tag in related_instance.tags.all(): + if tag.category not in tag_categories: + tag_categories.append(tag.category) + return tag_categories \ No newline at end of file diff --git a/apps/transfer/models.py b/apps/transfer/models.py index 019f38aa..d8268d6d 100644 --- a/apps/transfer/models.py +++ b/apps/transfer/models.py @@ -1208,3 +1208,17 @@ class NewsletterSubscriber(MigrateMixin): class Meta: managed = False db_table = 'newsletter_subscriptions' + + +class Footers(MigrateMixin): + using = 'legacy' + + about_us = models.TextField(blank=True, null=True) + copyright = models.TextField(blank=True, null=True) + site = models.ForeignKey('Sites', models.DO_NOTHING, blank=True, null=True) + created_at = models.DateTimeField() + updated_at = models.DateTimeField() + + class Meta: + managed = False + db_table = 'footers' diff --git a/apps/utils/middleware.py b/apps/utils/middleware.py index 1a421322..fa9f9950 100644 --- a/apps/utils/middleware.py +++ b/apps/utils/middleware.py @@ -1,8 +1,9 @@ -"""Custom middleware.""" +"""Custom middlewares.""" from django.utils import translation, timezone -from account.models import User +from account.models import User from configuration.models import TranslationSettings +from main.methods import determine_user_city from translation.models import Language @@ -18,7 +19,11 @@ def user_last_visit(get_response): def middleware(request): response = get_response(request) if request.user.is_authenticated: - User.objects.filter(pk=request.user.pk).update(last_login=timezone.now()) + User.objects.filter(pk=request.user.pk).update(**{ + 'last_login': timezone.now(), + 'locale': request.locale, + 'city': determine_user_city(request), + }) return response return middleware diff --git a/make_data_migration.sh b/make_data_migration.sh index 50be7fc4..b16c0b2d 100755 --- a/make_data_migration.sh +++ b/make_data_migration.sh @@ -8,11 +8,15 @@ ./manage.py transfer --fill_city_gallery ./manage.py transfer -l ./manage.py transfer --product +# Утеряна четкая связь между последовательностью миграций для импорта тегов продуктов, +# что может привести к удалению уже импортированных тегов командой выше. ./manage.py transfer --souvenir ./manage.py transfer --establishment_note ./manage.py transfer --product_note +./manage.py transfer --check_serial_number ./manage.py transfer --wine_characteristics ./manage.py transfer --inquiries ./manage.py transfer --assemblage ./manage.py transfer --purchased_plaques -./manage.py rm_empty_images \ No newline at end of file +./manage.py rm_empty_images +./manage.py add_artisan_subtype # добавляет подтипы для заведений артизанов \ No newline at end of file