Fix conflict

This commit is contained in:
Виктор Гладких 2019-10-21 14:43:14 +03:00
commit 264e7bcb2a
52 changed files with 4230 additions and 183 deletions

View File

@ -40,8 +40,7 @@ class PasswordResetConfirmView(JWTGenericViewMixin):
queryset = models.User.objects.active()
def get_object(self):
"""Override get_object method
"""
"""Override get_object method"""
queryset = self.filter_queryset(self.get_queryset())
uidb64 = self.kwargs.get('uidb64')

View File

@ -1,10 +1,10 @@
"""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
from smtplib import SMTPException
logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.INFO)
logger = logging.getLogger(__name__)

View File

@ -5,7 +5,7 @@ from django.utils.translation import gettext_lazy as _
from comment.models import Comment
from establishment import models
from main.models import Award, MetaDataContent
from main.models import Award
from review import models as review_models
@ -24,11 +24,6 @@ class AwardInline(GenericTabularInline):
extra = 0
class MetaDataContentInline(GenericTabularInline):
model = MetaDataContent
extra = 0
class ContactPhoneInline(admin.TabularInline):
"""Contact phone inline admin."""
model = models.ContactPhone
@ -56,8 +51,7 @@ class EstablishmentAdmin(admin.ModelAdmin):
"""Establishment admin."""
list_display = ['id', '__str__', 'image_tag', ]
inlines = [
AwardInline, MetaDataContentInline,
ContactPhoneInline, ContactEmailInline,
AwardInline, ContactPhoneInline, ContactEmailInline,
ReviewInline, CommentInline]
@ -84,4 +78,4 @@ class MenuAdmin(admin.ModelAdmin):
"""Get user's short name."""
return obj.category_translated
category_translated.short_description = _('category')
category_translated.short_description = _('category')

View File

@ -10,6 +10,10 @@ class EstablishmentFilter(filters.FilterSet):
tag_id = filters.NumberFilter(field_name='tags__metadata__id',)
award_id = filters.NumberFilter(field_name='awards__id',)
search = filters.CharFilter(method='search_text')
est_type = filters.ChoiceFilter(choices=models.EstablishmentType.INDEX_NAME_TYPES,
method='by_type')
est_subtype = filters.ChoiceFilter(choices=models.EstablishmentSubType.INDEX_NAME_TYPES,
method='by_subtype')
class Meta:
"""Meta class."""
@ -19,6 +23,8 @@ class EstablishmentFilter(filters.FilterSet):
'tag_id',
'award_id',
'search',
'est_type',
'est_subtype',
)
def search_text(self, queryset, name, value):
@ -26,3 +32,23 @@ class EstablishmentFilter(filters.FilterSet):
if value not in EMPTY_VALUES:
return queryset.search(value, locale=self.request.locale)
return queryset
def by_type(self, queryset, name, value):
return queryset.by_type(value)
def by_subtype(self, queryset, name, value):
return queryset.by_subtype(value)
class EstablishmentTypeTagFilter(filters.FilterSet):
"""Establishment tag filter set."""
type_id = filters.NumberFilter(field_name='id')
class Meta:
"""Meta class."""
model = models.EstablishmentType
fields = (
'type_id',
)

View File

@ -0,0 +1,35 @@
# Generated by Django 2.2.4 on 2019-10-09 07:15
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('establishment', '0031_establishment_slug'),
]
operations = [
migrations.CreateModel(
name='EstablishmentTag',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
],
options={
'verbose_name': 'establishment tag',
'verbose_name_plural': 'establishment tags',
},
),
migrations.CreateModel(
name='EstablishmentTypeTagCategory',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('establishment_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tag_categories', to='establishment.EstablishmentType', verbose_name='establishment type')),
],
options={
'verbose_name': 'establishment type tag categories',
'verbose_name_plural': 'establishment type tag categories',
},
),
]

View File

@ -0,0 +1,30 @@
# Generated by Django 2.2.4 on 2019-10-09 07:15
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('tag', '0001_initial'),
('establishment', '0032_establishmenttag_establishmenttypetagcategory'),
]
operations = [
migrations.AddField(
model_name='establishmenttypetagcategory',
name='tag_category',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='est_type_tag_categories', to='tag.TagCategory', verbose_name='tag category'),
),
migrations.AddField(
model_name='establishmenttag',
name='establishment',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tags', to='establishment.Establishment', verbose_name='establishment'),
),
migrations.AddField(
model_name='establishmenttag',
name='tag',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tags', to='tag.Tag', verbose_name='tag'),
),
]

View File

@ -0,0 +1,14 @@
# Generated by Django 2.2.4 on 2019-10-09 14:57
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('establishment', '0033_auto_20191009_0715'),
('establishment', '0033_auto_20191003_0943_squashed_0034_auto_20191003_1036'),
]
operations = [
]

View File

@ -0,0 +1,27 @@
# Generated by Django 2.2.4 on 2019-10-11 10:47
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('tag', '0002_auto_20191009_1408'),
('establishment', '0034_merge_20191009_1457'),
]
operations = [
migrations.CreateModel(
name='EstablishmentSubTypeTagCategory',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('establishment_subtype', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tag_categories', to='establishment.EstablishmentSubType', verbose_name='establishment subtype')),
('tag_category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='est_subtype_tag_categories', to='tag.TagCategory', verbose_name='tag category')),
],
options={
'verbose_name': 'establishment subtype tag categories',
'verbose_name_plural': 'establishment subtype tag categories',
},
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.4 on 2019-10-11 13:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('establishment', '0035_establishmentsubtypetagcategory'),
]
operations = [
migrations.AlterField(
model_name='establishment',
name='establishment_subtypes',
field=models.ManyToManyField(blank=True, related_name='subtype_establishment', to='establishment.EstablishmentSubType', verbose_name='subtype'),
),
]

View File

@ -0,0 +1,54 @@
# Generated by Django 2.2.4 on 2019-10-15 14:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tag', '0002_auto_20191009_1408'),
('establishment', '0036_auto_20191011_1356'),
]
operations = [
migrations.RemoveField(
model_name='establishmenttag',
name='establishment',
),
migrations.RemoveField(
model_name='establishmenttag',
name='tag',
),
migrations.RemoveField(
model_name='establishmenttypetagcategory',
name='establishment_type',
),
migrations.RemoveField(
model_name='establishmenttypetagcategory',
name='tag_category',
),
migrations.AddField(
model_name='establishment',
name='tags',
field=models.ManyToManyField(related_name='establishments', to='tag.Tag', verbose_name='Tag'),
),
migrations.AddField(
model_name='establishmentsubtype',
name='tag_categories',
field=models.ManyToManyField(related_name='establishment_subtypes', to='tag.TagCategory', verbose_name='Tag'),
),
migrations.AddField(
model_name='establishmenttype',
name='tag_categories',
field=models.ManyToManyField(related_name='establishment_types', to='tag.TagCategory', verbose_name='Tag'),
),
migrations.DeleteModel(
name='EstablishmentSubTypeTagCategory',
),
migrations.DeleteModel(
name='EstablishmentTag',
),
migrations.DeleteModel(
name='EstablishmentTypeTagCategory',
),
]

View File

@ -0,0 +1,35 @@
# Generated by Django 2.2.4 on 2019-10-16 11:33
from django.db import migrations, models
def fill_establishment_type(apps, schema_editor):
# We can't import the Person model directly as it may be a newer
# version than this migration expects. We use the historical version.
EstablishmentType = apps.get_model('establishment', 'EstablishmentType')
for n, et in enumerate(EstablishmentType.objects.all()):
et.index_name = f'Type {n}'
et.save()
class Migration(migrations.Migration):
dependencies = [
('establishment', '0037_auto_20191015_1404'),
]
operations = [
migrations.AddField(
model_name='establishmenttype',
name='index_name',
field=models.CharField(blank=True, db_index=True, max_length=50, null=True, unique=True, default=None, verbose_name='Index name'),
),
migrations.RunPython(fill_establishment_type, migrations.RunPython.noop),
migrations.AlterField(
model_name='establishmenttype',
name='index_name',
field=models.CharField(choices=[('restaurant', 'Restaurant'), ('artisan', 'Artisan'),
('producer', 'Producer')], db_index=True, max_length=50,
unique=True, verbose_name='Index name'),
),
]

View File

@ -0,0 +1,35 @@
# Generated by Django 2.2.4 on 2019-10-18 13:47
from django.db import migrations, models
def fill_establishment_subtype(apps, schema_editor):
# We can't import the Person model directly as it may be a newer
# version than this migration expects. We use the historical version.
EstablishmentSubType = apps.get_model('establishment', 'EstablishmentSubType')
for n, et in enumerate(EstablishmentSubType.objects.all()):
et.index_name = f'Type {n}'
et.save()
class Migration(migrations.Migration):
dependencies = [
('establishment', '0038_establishmenttype_index_name'),
]
operations = [
migrations.AddField(
model_name='establishmentsubtype',
name='index_name',
field=models.CharField(blank=True, db_index=True, max_length=50, null=True, unique=True, default=None, verbose_name='Index name'),
),
migrations.RunPython(fill_establishment_subtype),
migrations.AlterField(
model_name='establishmentsubtype',
name='index_name',
field=models.CharField(choices=[('winery', 'Winery'), ], db_index=True, max_length=50,
unique=True, verbose_name='Index name'),
),
]

View File

@ -15,7 +15,7 @@ from phonenumber_field.modelfields import PhoneNumberField
from collection.models import Collection
from location.models import Address
from main.models import Award, MetaDataContent
from main.models import Award
from review.models import Review
from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin,
TranslatedFieldsMixin, BaseAttributes)
@ -27,9 +27,26 @@ class EstablishmentType(TranslatedFieldsMixin, ProjectBaseMixin):
STR_FIELD_NAME = 'name'
# INDEX NAME CHOICES
RESTAURANT = 'restaurant'
ARTISAN = 'artisan'
PRODUCER = 'producer'
INDEX_NAME_TYPES = (
(RESTAURANT, _('Restaurant')),
(ARTISAN, _('Artisan')),
(PRODUCER, _('Producer')),
)
name = TJSONField(blank=True, null=True, default=None, verbose_name=_('Description'),
help_text='{"en-GB":"some text"}')
index_name = models.CharField(max_length=50, choices=INDEX_NAME_TYPES,
unique=True, db_index=True,
verbose_name=_('Index name'))
use_subtypes = models.BooleanField(_('Use subtypes'), default=True)
tag_categories = models.ManyToManyField('tag.TagCategory',
related_name='establishment_types',
verbose_name=_('Tag'))
class Meta:
"""Meta class."""
@ -51,11 +68,24 @@ class EstablishmentSubTypeManager(models.Manager):
class EstablishmentSubType(ProjectBaseMixin, TranslatedFieldsMixin):
"""Establishment type model."""
# INDEX NAME CHOICES
WINERY = 'winery'
INDEX_NAME_TYPES = (
(WINERY, _('Winery')),
)
name = TJSONField(blank=True, null=True, default=None, verbose_name=_('Description'),
help_text='{"en-GB":"some text"}')
index_name = models.CharField(max_length=50, choices=INDEX_NAME_TYPES,
unique=True, db_index=True,
verbose_name=_('Index name'))
establishment_type = models.ForeignKey(EstablishmentType,
on_delete=models.CASCADE,
verbose_name=_('Type'))
tag_categories = models.ManyToManyField('tag.TagCategory',
related_name='establishment_subtypes',
verbose_name=_('Tag'))
objects = EstablishmentSubTypeManager()
@ -75,11 +105,8 @@ class EstablishmentQuerySet(models.QuerySet):
def with_base_related(self):
"""Return qs with related objects."""
return self.select_related('address').prefetch_related(
models.Prefetch('tags',
MetaDataContent.objects.select_related(
'metadata__category'))
)
return self.select_related('address', 'establishment_type').\
prefetch_related('tags')
def with_extended_related(self):
return self.select_related('establishment_type').\
@ -87,6 +114,9 @@ class EstablishmentQuerySet(models.QuerySet):
'phones').\
prefetch_actual_employees()
def with_type_related(self):
return self.prefetch_related('establishment_subtypes')
def search(self, value, locale=None):
"""Search text in JSON fields."""
if locale is not None:
@ -234,6 +264,31 @@ class EstablishmentQuerySet(models.QuerySet):
kwargs = {unit: radius}
return self.filter(address__coordinates__distance_lte=(center, DistanceMeasure(**kwargs)))
def artisans(self):
"""Return artisans."""
return self.filter(establishment_type__index_name=EstablishmentType.ARTISAN)
def producers(self):
"""Return producers."""
return self.filter(establishment_type__index_name=EstablishmentType.PRODUCER)
def restaurants(self):
"""Return restaurants."""
return self.filter(establishment_type__index_name=EstablishmentType.RESTAURANT)
def wineries(self):
"""Return wineries."""
return self.producers().filter(
establishment_subtypes__index_name=EstablishmentSubType.WINERY)
def by_type(self, value):
"""Return QuerySet with type by value."""
return self.filter(establishment_type__index_name=value)
def by_subtype(self, value):
"""Return QuerySet with subtype by value."""
return self.filter(establishment_subtypes__index_name=value)
class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
"""Establishment model."""
@ -255,6 +310,7 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
on_delete=models.PROTECT,
verbose_name=_('type'))
establishment_subtypes = models.ManyToManyField(EstablishmentSubType,
blank=True,
related_name='subtype_establishment',
verbose_name=_('subtype'))
address = models.ForeignKey(Address, blank=True, null=True, default=None,
@ -297,7 +353,11 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
verbose_name=_('Establishment slug'), editable=True)
awards = generic.GenericRelation(to='main.Award', related_query_name='establishment')
tags = generic.GenericRelation(to='main.MetaDataContent')
# todo: remove after data merge
# tags = generic.GenericRelation(to='main.MetaDataContent')
tags = models.ManyToManyField('tag.Tag', related_name='establishments',
verbose_name=_('Tag'))
old_tags = generic.GenericRelation(to='main.MetaDataContent')
reviews = generic.GenericRelation(to='review.Review')
comments = generic.GenericRelation(to='comment.Comment')
favorites = generic.GenericRelation(to='favorites.Favorites')
@ -491,6 +551,7 @@ class ContactEmail(models.Model):
def __str__(self):
return f'{self.email}'
#
# class Wine(TranslatedFieldsMixin, models.Model):
# """Wine model."""
@ -568,3 +629,4 @@ class SocialNetwork(models.Model):
def __str__(self):
return self.title

View File

@ -1,13 +1,12 @@
from rest_framework import serializers
from establishment import models
from establishment.serializers import (
EstablishmentBaseSerializer, PlateSerializer, ContactEmailsSerializer,
ContactPhonesSerializer, SocialNetworkRelatedSerializers,
EstablishmentTypeSerializer)
from utils.decorators import with_base_attributes
EstablishmentTypeBaseSerializer)
from main.models import Currency
from utils.decorators import with_base_attributes
class EstablishmentListCreateSerializer(EstablishmentBaseSerializer):
@ -21,7 +20,7 @@ class EstablishmentListCreateSerializer(EstablishmentBaseSerializer):
emails = ContactEmailsSerializer(read_only=True, many=True, )
socials = SocialNetworkRelatedSerializers(read_only=True, many=True, )
slug = serializers.SlugField(required=True, allow_blank=False, max_length=50)
type = EstablishmentTypeSerializer(source='establishment_type', read_only=True)
type = EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True)
class Meta:
model = models.Establishment
@ -55,7 +54,7 @@ class EstablishmentRUDSerializer(EstablishmentBaseSerializer):
phones = ContactPhonesSerializer(read_only=False, many=True, )
emails = ContactEmailsSerializer(read_only=False, many=True, )
socials = SocialNetworkRelatedSerializers(read_only=False, many=True, )
type = EstablishmentTypeSerializer(source='establishment_type')
type = EstablishmentTypeBaseSerializer(source='establishment_type')
class Meta:
model = models.Establishment
@ -141,4 +140,3 @@ class EmployeeBackSerializers(serializers.ModelSerializer):
'user',
'name'
]

View File

@ -1,17 +1,19 @@
"""Establishment serializers."""
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from comment import models as comment_models
from comment.serializers import common as comment_serializers
from establishment import models
from favorites.models import Favorites
from location.serializers import AddressBaseSerializer
from main.models import MetaDataContent
from main.serializers import MetaDataContentSerializer, AwardSerializer, CurrencySerializer
from main.serializers import AwardSerializer, CurrencySerializer
from review import models as review_models
from tag.serializers import TagBaseSerializer
from timetable.serialziers import ScheduleRUDSerializer
from utils import exceptions as utils_exceptions
from utils.serializers import TranslatedField, ProjectModelSerializer
from utils.serializers import ProjectModelSerializer
from utils.serializers import TranslatedField
class ContactPhonesSerializer(serializers.ModelSerializer):
@ -86,30 +88,6 @@ class MenuRUDSerializers(ProjectModelSerializer):
]
class EstablishmentTypeSerializer(serializers.ModelSerializer):
"""Serializer for EstablishmentType model."""
name_translated = serializers.CharField(allow_null=True)
class Meta:
"""Meta class."""
model = models.EstablishmentType
fields = ('id', 'name_translated')
class EstablishmentSubTypeSerializer(serializers.ModelSerializer):
"""Serializer for EstablishmentSubType models."""
name_translated = serializers.CharField(allow_null=True)
class Meta:
"""Meta class."""
model = models.EstablishmentSubType
fields = ('id', 'name_translated')
class ReviewSerializer(serializers.ModelSerializer):
"""Serializer for model Review."""
text_translated = serializers.CharField(read_only=True)
@ -122,6 +100,45 @@ class ReviewSerializer(serializers.ModelSerializer):
)
class EstablishmentTypeBaseSerializer(serializers.ModelSerializer):
"""Serializer for EstablishmentType model."""
name_translated = TranslatedField()
class Meta:
"""Meta class."""
model = models.EstablishmentType
fields = [
'id',
'name',
'name_translated',
'use_subtypes'
]
extra_kwargs = {
'name': {'write_only': True},
'use_subtypes': {'write_only': True},
}
class EstablishmentSubTypeBaseSerializer(serializers.ModelSerializer):
"""Serializer for EstablishmentSubType models."""
name_translated = TranslatedField()
class Meta:
"""Meta class."""
model = models.EstablishmentSubType
fields = [
'id',
'name',
'name_translated',
'establishment_type'
]
extra_kwargs = {
'name': {'write_only': True},
'establishment_type': {'write_only': True}
}
class EstablishmentEmployeeSerializer(serializers.ModelSerializer):
"""Serializer for actual employees."""
@ -144,8 +161,8 @@ class EstablishmentBaseSerializer(ProjectModelSerializer):
preview_image = serializers.URLField(source='preview_image_url')
slug = serializers.SlugField(allow_blank=False, required=True, max_length=50)
address = AddressBaseSerializer()
tags = MetaDataContentSerializer(many=True)
in_favorites = serializers.BooleanField(allow_null=True)
tags = TagBaseSerializer(read_only=True, many=True)
class Meta:
"""Meta class."""
@ -171,8 +188,8 @@ class EstablishmentDetailSerializer(EstablishmentBaseSerializer):
description_translated = TranslatedField()
image = serializers.URLField(source='image_url')
type = EstablishmentTypeSerializer(source='establishment_type', read_only=True)
subtypes = EstablishmentSubTypeSerializer(many=True, source='establishment_subtypes')
type = EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True)
subtypes = EstablishmentSubTypeBaseSerializer(many=True, source='establishment_subtypes')
awards = AwardSerializer(many=True)
schedule = ScheduleRUDSerializer(many=True, allow_null=True)
phones = ContactPhonesSerializer(read_only=True, many=True)
@ -306,17 +323,3 @@ class EstablishmentFavoritesCreateSerializer(serializers.ModelSerializer):
})
return super().create(validated_data)
class EstablishmentTagListSerializer(serializers.ModelSerializer):
"""List establishment tag serializer."""
id = serializers.IntegerField(source='metadata.id')
label_translated = serializers.CharField(
source='metadata.label_translated', read_only=True, allow_null=True)
class Meta:
"""Meta class."""
model = MetaDataContent
fields = [
'id',
'label_translated',
]

View File

@ -26,4 +26,8 @@ urlpatterns = [
path('emails/<int:pk>/', views.EmailRUDView.as_view(), name='emails-rud'),
path('employees/', views.EmployeeListCreateView.as_view(), name='employees'),
path('employees/<int:pk>/', views.EmployeeRUDView.as_view(), name='employees-rud'),
]
path('types/', views.EstablishmentTypeListCreateView.as_view(), name='type-list'),
path('types/<int:pk>/', views.EstablishmentTypeRUDView.as_view(), name='type-rud'),
path('subtypes/', views.EstablishmentSubtypeListCreateView.as_view(), name='subtype-list'),
path('subtypes/<int:pk>/', views.EstablishmentSubtypeRUDView.as_view(), name='subtype-rud'),
]

View File

@ -7,9 +7,9 @@ app_name = 'establishment'
urlpatterns = [
path('', views.EstablishmentListView.as_view(), name='list'),
path('tags/', views.EstablishmentTagListView.as_view(), name='tags'),
path('recent-reviews/', views.EstablishmentRecentReviewListView.as_view(),
name='recent-reviews'),
# path('wineries/', views.WineriesListView.as_view(), name='wineries-list'),
path('slug/<slug:slug>/', views.EstablishmentRetrieveView.as_view(), name='detail'),
path('slug/<slug:slug>/similar/', views.EstablishmentSimilarListView.as_view(), name='similar'),
path('slug/<slug:slug>/comments/', views.EstablishmentCommentListView.as_view(), name='list-comments'),

View File

@ -4,4 +4,4 @@ from establishment.urls.common import urlpatterns as common_urlpatterns
urlpatterns = []
urlpatterns.extend(common_urlpatterns)
urlpatterns.extend(common_urlpatterns)

View File

@ -1,10 +1,10 @@
"""Establishment app views."""
from django.shortcuts import get_object_or_404
from rest_framework import generics
from establishment import models
from establishment import serializers
from utils.permissions import IsCountryAdmin, IsEstablishmentManager
from establishment import models, serializers
from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer
class EstablishmentMixinViews:
@ -28,6 +28,34 @@ class EstablishmentRUDView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsCountryAdmin|IsEstablishmentManager]
class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView):
"""Establishment schedule RUD view"""
serializer_class = ScheduleRUDSerializer
def get_object(self):
"""
Returns the object the view is displaying.
"""
establishment_pk = self.kwargs['pk']
schedule_id = self.kwargs['schedule_id']
establishment = get_object_or_404(klass=models.Establishment.objects.all(),
pk=establishment_pk)
schedule = get_object_or_404(klass=establishment.schedule,
id=schedule_id)
# May raise a permission denied
self.check_object_permissions(self.request, establishment)
self.check_object_permissions(self.request, schedule)
return schedule
class EstablishmentScheduleCreateView(generics.CreateAPIView):
"""Establishment schedule Create view"""
serializer_class = ScheduleCreateSerializer
class MenuListCreateView(generics.ListCreateAPIView):
"""Menu list create view."""
serializer_class = serializers.MenuSerializers
@ -113,3 +141,29 @@ class EmployeeRUDView(generics.RetrieveUpdateDestroyAPIView):
"""Social RUD view."""
serializer_class = serializers.EmployeeBackSerializers
queryset = models.Employee.objects.all()
class EstablishmentTypeListCreateView(generics.ListCreateAPIView):
"""Establishment type list/create view."""
serializer_class = serializers.EstablishmentTypeBaseSerializer
queryset = models.EstablishmentType.objects.all()
pagination_class = None
class EstablishmentTypeRUDView(generics.RetrieveUpdateDestroyAPIView):
"""Establishment type retrieve/update/destroy view."""
serializer_class = serializers.EstablishmentTypeBaseSerializer
queryset = models.EstablishmentType.objects.all()
class EstablishmentSubtypeListCreateView(generics.ListCreateAPIView):
"""Establishment subtype list/create view."""
serializer_class = serializers.EstablishmentSubTypeBaseSerializer
queryset = models.EstablishmentSubType.objects.all()
pagination_class = None
class EstablishmentSubtypeRUDView(generics.RetrieveUpdateDestroyAPIView):
"""Establishment subtype retrieve/update/destroy view."""
serializer_class = serializers.EstablishmentSubTypeBaseSerializer
queryset = models.EstablishmentSubType.objects.all()

View File

@ -9,7 +9,6 @@ from establishment import filters
from establishment import models, serializers
from main import methods
from main.models import MetaDataContent
from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer
from utils.pagination import EstablishmentPortionPagination
from utils.permissions import IsCountryAdmin
@ -19,9 +18,10 @@ class EstablishmentMixinView:
permission_classes = (permissions.AllowAny,)
def get_queryset(self):
"""Overrided method 'get_queryset'."""
return models.Establishment.objects.published().with_base_related().\
annotate_in_favorites(self.request.user)
"""Overridden method 'get_queryset'."""
return models.Establishment.objects.published() \
.with_base_related() \
.annotate_in_favorites(self.request.user)
class EstablishmentListView(EstablishmentMixinView, generics.ListAPIView):
@ -84,7 +84,7 @@ class EstablishmentTypeListView(generics.ListAPIView):
"""Resource for getting a list of establishment types."""
permission_classes = (permissions.AllowAny,)
serializer_class = serializers.EstablishmentTypeSerializer
serializer_class = serializers.EstablishmentTypeBaseSerializer
queryset = models.EstablishmentType.objects.all()
@ -176,42 +176,12 @@ class EstablishmentNearestRetrieveView(EstablishmentListView, generics.ListAPIVi
return qs
class EstablishmentTagListView(generics.ListAPIView):
"""List view for establishment tags."""
serializer_class = serializers.EstablishmentTagListSerializer
permission_classes = (permissions.AllowAny,)
pagination_class = None
def get_queryset(self):
"""Override get_queryset method"""
return MetaDataContent.objects.by_content_type(app_label='establishment',
model='establishment')\
.distinct('metadata__label')
class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView):
"""Establishment schedule RUD view"""
serializer_class = ScheduleRUDSerializer
def get_object(self):
"""
Returns the object the view is displaying.
"""
establishment_pk = self.kwargs['pk']
schedule_id = self.kwargs['schedule_id']
establishment = get_object_or_404(klass=models.Establishment.objects.all(),
pk=establishment_pk)
schedule = get_object_or_404(klass=establishment.schedule,
id=schedule_id)
# May raise a permission denied
self.check_object_permissions(self.request, establishment)
self.check_object_permissions(self.request, schedule)
return schedule
class EstablishmentScheduleCreateView(generics.CreateAPIView):
"""Establishment schedule Create view"""
serializer_class = ScheduleCreateSerializer
# Wineries
# todo: find out about difference between subtypes data
# class WineriesListView(EstablishmentListView):
# """Return list establishments with type Wineries"""
#
# def get_queryset(self):
# """Overridden get_queryset method."""
# qs = super(WineriesListView, self).get_queryset()
# return qs.with_type_related().wineries()

View File

@ -25,22 +25,6 @@ class AwardAdmin(admin.ModelAdmin):
# list_display_links = ['id', '__str__']
@admin.register(models.MetaData)
class MetaDataAdmin(admin.ModelAdmin):
"""MetaData admin."""
@admin.register(models.MetaDataCategory)
class MetaDataCategoryAdmin(admin.ModelAdmin):
"""MetaData admin."""
list_display = ['id', 'country', 'content_type']
@admin.register(models.MetaDataContent)
class MetaDataContentAdmin(admin.ModelAdmin):
"""MetaDataContent admin"""
@admin.register(models.Currency)
class CurrencContentAdmin(admin.ModelAdmin):
"""CurrencContent admin"""

View File

@ -1,8 +1,8 @@
from django.contrib import admin
from news import models
from .tasks import send_email_with_news
@admin.register(models.NewsType)
class NewsTypeAdmin(admin.ModelAdmin):
"""News type admin."""

View File

@ -0,0 +1,24 @@
# Generated by Django 2.2.4 on 2019-10-09 14:08
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tag', '0002_auto_20191009_1408'),
('news', '0020_remove_news_author'),
]
operations = [
migrations.AddField(
model_name='news',
name='tags',
field=models.ManyToManyField(related_name='news', to='tag.Tag', verbose_name='Tags'),
),
migrations.AddField(
model_name='newstype',
name='tag_categories',
field=models.ManyToManyField(related_name='news_types', to='tag.TagCategory'),
),
]

View File

@ -12,6 +12,8 @@ class NewsType(models.Model):
"""NewsType model."""
name = models.CharField(_('name'), max_length=250)
tag_categories = models.ManyToManyField('tag.TagCategory',
related_name='news_types')
class Meta:
"""Meta class."""
@ -133,8 +135,8 @@ class News(BaseAttributes, TranslatedFieldsMixin):
country = models.ForeignKey('location.Country', blank=True, null=True,
on_delete=models.SET_NULL,
verbose_name=_('country'))
tags = generic.GenericRelation(to='main.MetaDataContent')
tags = models.ManyToManyField('tag.Tag', related_name='news',
verbose_name=_('Tags'))
ratings = generic.GenericRelation(Rating)
objects = NewsQuerySet.as_manager()
@ -163,4 +165,3 @@ class News(BaseAttributes, TranslatedFieldsMixin):
@property
def same_theme(self):
return self.__class__.objects.same_theme(self)[:3]

View File

@ -3,8 +3,8 @@ from rest_framework import serializers
from account.serializers.common import UserBaseSerializer
from location import models as location_models
from location.serializers import CountrySimpleSerializer
from main.serializers import MetaDataContentSerializer
from news import models
from tag.serializers import TagBaseSerializer
from utils.serializers import TranslatedField, ProjectModelSerializer
@ -27,7 +27,7 @@ class NewsBaseSerializer(ProjectModelSerializer):
# related fields
news_type = NewsTypeSerializer(read_only=True)
tags = MetaDataContentSerializer(read_only=True, many=True)
tags = TagBaseSerializer(read_only=True, many=True)
class Meta:
"""Meta class."""

View File

@ -1,9 +1,11 @@
"""News app views."""
from django.shortcuts import get_object_or_404
from rest_framework import generics, permissions
from news import filters, models, serializers
from rating.tasks import add_rating
from utils.permissions import IsCountryAdmin, IsContentPageManager
class NewsMixinView:
"""News mixin."""

View File

@ -21,24 +21,22 @@ class EstablishmentDocument(Document):
properties={
'id': fields.IntegerField(),
'name': fields.ObjectField(attr='name_indexing',
properties=OBJECT_FIELD_PROPERTIES)
properties=OBJECT_FIELD_PROPERTIES),
})
establishment_subtypes = fields.ObjectField(
properties={
'id': fields.IntegerField(),
'name': fields.ObjectField(attr='name_indexing',
properties=OBJECT_FIELD_PROPERTIES)
properties={
'id': fields.IntegerField(),
}),
},
multi=True)
tags = fields.ObjectField(
properties={
'id': fields.IntegerField(attr='metadata.id'),
'label': fields.ObjectField(attr='metadata.label_indexing',
'id': fields.IntegerField(attr='id'),
'label': fields.ObjectField(attr='label_indexing',
properties=OBJECT_FIELD_PROPERTIES),
'category': fields.ObjectField(attr='metadata.category',
properties={
'id': fields.IntegerField(),
})
},
multi=True)
address = fields.ObjectField(

View File

@ -26,11 +26,9 @@ class NewsDocument(Document):
web_url = fields.KeywordField(attr='web_url')
tags = fields.ObjectField(
properties={
'id': fields.IntegerField(attr='metadata.id'),
'label': fields.ObjectField(attr='metadata.label_indexing',
'id': fields.IntegerField(attr='id'),
'label': fields.ObjectField(attr='label_indexing',
properties=OBJECT_FIELD_PROPERTIES),
'category': fields.ObjectField(attr='metadata.category',
properties={'id': fields.IntegerField()})
},
multi=True)

View File

@ -40,12 +40,8 @@ def update_document(sender, **kwargs):
for establishment in establishments:
registry.update(establishment)
if app_label == 'main':
if model_name == 'metadata':
establishments = Establishment.objects.filter(tags__metadata=instance)
for establishment in establishments:
registry.update(establishment)
if model_name == 'metadatacontent':
if app_label == 'tag':
if model_name == 'tag':
establishments = Establishment.objects.filter(tags=instance)
for establishment in establishments:
registry.update(establishment)
@ -70,12 +66,8 @@ def update_news(sender, **kwargs):
for news in qs:
registry.update(news)
if app_label == 'main':
if model_name == 'metadata':
qs = News.objects.filter(tags__metadata=instance)
for news in qs:
registry.update(news)
if model_name == 'metadatacontent':
if app_label == 'tag':
if model_name == 'tag':
qs = News.objects.filter(tags=instance)
for news in qs:
registry.update(news)

0
apps/tag/__init__.py Normal file
View File

12
apps/tag/admin.py Normal file
View File

@ -0,0 +1,12 @@
from django.contrib import admin
from .models import Tag, TagCategory
@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
"""Admin model for model Tag."""
@admin.register(TagCategory)
class TagCategoryAdmin(admin.ModelAdmin):
"""Admin model for model TagCategory."""

7
apps/tag/apps.py Normal file
View File

@ -0,0 +1,7 @@
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class TagConfig(AppConfig):
name = 'tag'
verbose_name = _('tag')

42
apps/tag/filters.py Normal file
View File

@ -0,0 +1,42 @@
"""Tag app filters."""
from django_filters import rest_framework as filters
from establishment.models import EstablishmentType
from tag import models
class TagCategoryFilterSet(filters.FilterSet):
"""TagCategory filterset."""
# Object type choices
NEWS = 'news'
ESTABLISHMENT = 'establishment'
TYPE_CHOICES = (
(NEWS, 'News'),
(ESTABLISHMENT, 'Establishment'),
)
type = filters.MultipleChoiceFilter(choices=TYPE_CHOICES,
method='filter_by_type')
establishment_type = filters.ChoiceFilter(
choices=EstablishmentType.INDEX_NAME_TYPES,
method='by_establishment_type')
class Meta:
"""Meta class."""
model = models.TagCategory
fields = ('type',
'establishment_type', )
def filter_by_type(self, queryset, name, value):
if self.NEWS in value:
queryset = queryset.for_news()
if self.ESTABLISHMENT in value:
queryset = queryset.for_establishments()
return queryset
# todo: filter by establishment type
def by_establishment_type(self, queryset, name, value):
return queryset.by_establishment_type(value)

View File

@ -0,0 +1,44 @@
# Generated by Django 2.2.4 on 2019-10-09 07:15
from django.db import migrations, models
import django.db.models.deletion
import utils.models
class Migration(migrations.Migration):
initial = True
dependencies = [
('location', '0010_auto_20190904_0711'),
]
operations = [
migrations.CreateModel(
name='TagCategory',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('label', utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='label')),
('public', models.BooleanField(default=False)),
('country', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='location.Country')),
],
options={
'verbose_name': 'tag category',
'verbose_name_plural': 'tag categories',
},
bases=(utils.models.TranslatedFieldsMixin, models.Model),
),
migrations.CreateModel(
name='Tag',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('label', utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='label')),
('category', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tags', to='tag.TagCategory', verbose_name='category')),
],
options={
'verbose_name': 'tag',
'verbose_name_plural': 'tags',
},
bases=(utils.models.TranslatedFieldsMixin, models.Model),
),
]

View File

@ -0,0 +1,27 @@
# Generated by Django 2.2.4 on 2019-10-09 14:08
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('tag', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='tag',
options={'verbose_name': 'Tag', 'verbose_name_plural': 'Tags'},
),
migrations.AlterModelOptions(
name='tagcategory',
options={'verbose_name': 'Tag category', 'verbose_name_plural': 'Tag categories'},
),
migrations.AlterField(
model_name='tag',
name='category',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='tags', to='tag.TagCategory', verbose_name='Category'),
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 2.2.4 on 2019-10-18 07:58
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('tag', '0002_auto_20191009_1408'),
]
operations = [
migrations.AlterField(
model_name='tag',
name='category',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='tags', to='tag.TagCategory', verbose_name='Category'),
),
]

View File

85
apps/tag/models.py Normal file
View File

@ -0,0 +1,85 @@
"""Tag app models."""
from django.db import models
from django.utils.translation import gettext_lazy as _
from configuration.models import TranslationSettings
from utils.models import TJSONField, TranslatedFieldsMixin
class Tag(TranslatedFieldsMixin, models.Model):
"""Tag model."""
label = TJSONField(blank=True, null=True, default=None,
verbose_name=_('label'),
help_text='{"en-GB":"some text"}')
category = models.ForeignKey('TagCategory', on_delete=models.CASCADE,
null=True, related_name='tags',
verbose_name=_('Category'))
class Meta:
"""Meta class."""
verbose_name = _('Tag')
verbose_name_plural = _('Tags')
def __str__(self):
label = 'None'
lang = TranslationSettings.get_solo().default_language
if self.label and lang in self.label:
label = self.label[lang]
return f'id:{self.id}-{label}'
class TagCategoryQuerySet(models.QuerySet):
"""Extended queryset for TagCategory model."""
def with_base_related(self):
"""Select related objects."""
return self.prefetch_related('tags')
def with_extended_related(self):
"""Select related objects."""
return self.select_related('country')
def for_news(self):
"""Select tag categories for news."""
return self.filter(news_types__isnull=True)
def for_establishments(self):
"""Select tag categories for establishments."""
return self.filter(models.Q(establishment_types__isnull=False) |
models.Q(establishment_subtypes__isnull=False))
def by_establishment_type(self, index_name):
"""Filter by establishment type index name."""
return self.filter(establishment_types__index_name=index_name)
def with_tags(self, switcher=True):
"""Filter by existing tags."""
return self.filter(tags__isnull=not switcher)
class TagCategory(TranslatedFieldsMixin, models.Model):
"""Tag base category model."""
label = TJSONField(blank=True, null=True, default=None,
verbose_name=_('label'),
help_text='{"en-GB":"some text"}')
country = models.ForeignKey('location.Country',
on_delete=models.SET_NULL, null=True,
default=None)
public = models.BooleanField(default=False)
objects = TagCategoryQuerySet.as_manager()
class Meta:
"""Meta class."""
verbose_name = _('Tag category')
verbose_name_plural = _('Tag categories')
def __str__(self):
label = 'None'
lang = TranslationSettings.get_solo().default_language
if self.label and lang in self.label:
label = self.label[lang]
return f'id:{self.id}-{label}'

167
apps/tag/serializers.py Normal file
View File

@ -0,0 +1,167 @@
"""Tag serializers."""
from rest_framework import serializers
from establishment.models import (Establishment, EstablishmentType,
EstablishmentSubType)
from news.models import News, NewsType
from tag import models
from utils.exceptions import (ObjectAlreadyAdded, BindingObjectNotFound,
RemovedBindingObjectNotFound)
from utils.serializers import TranslatedField
class TagBaseSerializer(serializers.ModelSerializer):
"""Serializer for model Tag."""
label_translated = TranslatedField()
class Meta:
"""Meta class."""
model = models.Tag
fields = (
'id',
'label_translated',
)
class TagBackOfficeSerializer(TagBaseSerializer):
"""Serializer for Tag model for Back office users."""
class Meta(TagBaseSerializer.Meta):
"""Meta class."""
fields = TagBaseSerializer.Meta.fields + (
'label',
'category'
)
class TagCategoryBaseSerializer(serializers.ModelSerializer):
"""Serializer for model TagCategory."""
label_translated = TranslatedField()
tags = TagBaseSerializer(many=True, read_only=True)
class Meta:
"""Meta class."""
model = models.TagCategory
fields = (
'id',
'label_translated',
'tags'
)
class TagCategoryBackOfficeDetailSerializer(TagCategoryBaseSerializer):
"""Tag Category detail serializer for back-office users."""
country_translated = TranslatedField(source='country.name_translated')
class Meta(TagCategoryBaseSerializer.Meta):
"""Meta class."""
fields = TagCategoryBaseSerializer.Meta.fields + (
'label',
'country',
'country_translated',
)
class TagBindObjectSerializer(serializers.Serializer):
"""Serializer for binding tag category and objects"""
ESTABLISHMENT = 'establishment'
NEWS = 'news'
TYPE_CHOICES = (
(ESTABLISHMENT, 'Establishment type'),
(NEWS, 'News type'),
)
type = serializers.ChoiceField(TYPE_CHOICES)
object_id = serializers.IntegerField()
def validate(self, attrs):
view = self.context.get('view')
request = self.context.get('request')
obj_type = attrs.get('type')
obj_id = attrs.get('object_id')
tag = view.get_object()
attrs['tag'] = tag
if obj_type == self.ESTABLISHMENT:
establishment = Establishment.objects.filter(pk=obj_id).first()
if not establishment:
raise BindingObjectNotFound()
if request.method == 'POST' and tag.establishments.filter(
pk=establishment.pk).exists():
raise ObjectAlreadyAdded()
if request.method == 'DELETE' and not tag.establishments.filter(
pk=establishment.pk).exists():
raise RemovedBindingObjectNotFound()
attrs['related_object'] = establishment
elif obj_type == self.NEWS:
news = News.objects.filter(pk=obj_id).first()
if not news:
raise BindingObjectNotFound()
if request.method == 'POST' and tag.news.filter(pk=news.pk).exists():
raise ObjectAlreadyAdded()
if request.method == 'DELETE' and not tag.news.filter(
pk=news.pk).exists():
raise RemovedBindingObjectNotFound()
attrs['related_object'] = news
return attrs
class TagCategoryBindObjectSerializer(serializers.Serializer):
"""Serializer for binding tag category and objects"""
ESTABLISHMENT_TYPE = 'establishment_type'
NEWS_TYPE = 'news_type'
TYPE_CHOICES = (
(ESTABLISHMENT_TYPE, 'Establishment type'),
(NEWS_TYPE, 'News type'),
)
type = serializers.ChoiceField(TYPE_CHOICES)
object_id = serializers.IntegerField()
def validate(self, attrs):
view = self.context.get('view')
request = self.context.get('request')
obj_type = attrs.get('type')
obj_id = attrs.get('object_id')
tag_category = view.get_object()
attrs['tag_category'] = tag_category
if obj_type == self.ESTABLISHMENT_TYPE:
establishment_type = EstablishmentType.objects.filter(pk=obj_id).\
first()
if not establishment_type:
raise BindingObjectNotFound()
if request.method == 'POST' and tag_category.establishment_types.\
filter(pk=establishment_type.pk).exists():
raise ObjectAlreadyAdded()
if request.method == 'DELETE' and not tag_category.\
establishment_types.filter(pk=establishment_type.pk).\
exists():
raise RemovedBindingObjectNotFound()
attrs['related_object'] = establishment_type
elif obj_type == self.NEWS_TYPE:
news_type = NewsType.objects.filter(pk=obj_id).first()
if not news_type:
raise BindingObjectNotFound()
if request.method == 'POST' and tag_category.news_types.\
filter(pk=news_type.pk).exists():
raise ObjectAlreadyAdded()
if request.method == 'DELETE' and not tag_category.news_types.\
filter(pk=news_type.pk).exists():
raise RemovedBindingObjectNotFound()
attrs['related_object'] = news_type
return attrs

3
apps/tag/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

11
apps/tag/urls/back.py Normal file
View File

@ -0,0 +1,11 @@
"""Urlconf for app tag."""
from rest_framework.routers import SimpleRouter
from tag import views
app_name = 'tag'
router = SimpleRouter()
router.register(r'categories', views.TagCategoryBackOfficeViewSet)
router.register(r'', views.TagBackOfficeViewSet)
urlpatterns = router.urls

16
apps/tag/urls/web.py Normal file
View File

@ -0,0 +1,16 @@
"""Tag app urlpatterns web users."""
from rest_framework.routers import SimpleRouter
from tag import views
app_name = 'tag'
router = SimpleRouter()
router.register(r'categories', views.TagCategoryViewSet)
urlpatterns = [
]
urlpatterns += router.urls

111
apps/tag/views.py Normal file
View File

@ -0,0 +1,111 @@
"""Tag views."""
from rest_framework import viewsets, mixins, status
from rest_framework.decorators import action
from rest_framework.response import Response
from tag import filters, models, serializers
from rest_framework import permissions
# User`s views & viewsets
class TagCategoryViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
"""ViewSet for TagCategory model."""
filterset_class = filters.TagCategoryFilterSet
pagination_class = None
permission_classes = (permissions.AllowAny, )
queryset = models.TagCategory.objects.with_tags().with_base_related().\
distinct()
serializer_class = serializers.TagCategoryBaseSerializer
# BackOffice user`s views & viewsets
class BindObjectMixin:
"""Bind object mixin."""
def get_serializer_class(self):
if self.action == 'bind_object':
return self.bind_object_serializer_class
return self.serializer_class
def perform_binding(self, serializer):
raise NotImplemented
def perform_unbinding(self, serializer):
raise NotImplemented
@action(methods=['post', 'delete'], detail=True, url_path='bind-object')
def bind_object(self, request, pk=None):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
if request.method == 'POST':
self.perform_binding(serializer)
return Response(serializer.data, status=status.HTTP_201_CREATED)
elif request.method == 'DELETE':
self.perform_unbinding(serializer)
return Response(status=status.HTTP_204_NO_CONTENT)
class TagBackOfficeViewSet(mixins.ListModelMixin, mixins.CreateModelMixin,
mixins.UpdateModelMixin, mixins.DestroyModelMixin,
BindObjectMixin, viewsets.GenericViewSet):
"""List/create tag view."""
pagination_class = None
permission_classes = (permissions.IsAuthenticated, )
queryset = models.Tag.objects.all()
serializer_class = serializers.TagBackOfficeSerializer
bind_object_serializer_class = serializers.TagBindObjectSerializer
def perform_binding(self, serializer):
data = serializer.validated_data
tag = data.pop('tag')
obj_type = data.get('type')
related_object = data.get('related_object')
if obj_type == self.bind_object_serializer_class.ESTABLISHMENT:
tag.establishments.add(related_object)
elif obj_type == self.bind_object_serializer_class.NEWS:
tag.news.add(related_object)
def perform_unbinding(self, serializer):
data = serializer.validated_data
tag = data.pop('tag')
obj_type = data.get('type')
related_object = data.get('related_object')
if obj_type == self.bind_object_serializer_class.ESTABLISHMENT:
tag.establishments.remove(related_object)
elif obj_type == self.bind_object_serializer_class.NEWS:
tag.news.remove(related_object)
class TagCategoryBackOfficeViewSet(mixins.CreateModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.RetrieveModelMixin,
BindObjectMixin,
TagCategoryViewSet):
"""ViewSet for TagCategory model for BackOffice users."""
permission_classes = (permissions.IsAuthenticated, )
queryset = TagCategoryViewSet.queryset.with_extended_related()
serializer_class = serializers.TagCategoryBackOfficeDetailSerializer
bind_object_serializer_class = serializers.TagCategoryBindObjectSerializer
def perform_binding(self, serializer):
data = serializer.validated_data
tag_category = data.pop('tag_category')
obj_type = data.get('type')
related_object = data.get('related_object')
if obj_type == self.bind_object_serializer_class.ESTABLISHMENT_TYPE:
tag_category.establishment_types.add(related_object)
elif obj_type == self.bind_object_serializer_class.NEWS_TYPE:
tag_category.news_types.add(related_object)
def perform_unbinding(self, serializer):
data = serializer.validated_data
tag_category = data.pop('tag_category')
obj_type = data.get('type')
related_object = data.get('related_object')
if obj_type == self.bind_object_serializer_class.ESTABLISHMENT_TYPE:
tag_category.establishment_types.remove(related_object)
elif obj_type == self.bind_object_serializer_class.NEWS_TYPE:
tag_category.news_types.remove(related_object)

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.4 on 2019-10-18 08:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('translation', '0003_auto_20190901_1032'),
]
operations = [
migrations.AlterField(
model_name='language',
name='locale',
field=models.CharField(max_length=10, unique=True, verbose_name='Locale identifier'),
),
]

View File

@ -22,7 +22,7 @@ class Language(models.Model):
title = models.CharField(max_length=255,
verbose_name=_('Language title'))
locale = models.CharField(max_length=10,
locale = models.CharField(max_length=10, unique=True,
verbose_name=_('Locale identifier'))
objects = LanguageQuerySet.as_manager()

View File

@ -23,14 +23,24 @@ class GMJWTAuthentication(JWTAuthentication):
"""
def authenticate(self, request):
token = get_token_from_cookies(request)
if token is None:
try:
token = get_token_from_cookies(request)
# Return non-authorized user if token not in cookies
assert token
raw_token = self.get_raw_token(token)
# Return non-authorized user if cant get raw token
assert raw_token
validated_token = self.get_validated_token(raw_token)
user = self.get_user(validated_token)
# Check record in DB
token_is_valid = user.access_tokens.valid() \
.by_jti(jti=validated_token.payload.get('jti'))
assert token_is_valid.exists()
except:
# Return non-authorized user if token is invalid or raised an error when run checks.
return None
raw_token = self.get_raw_token(token)
if raw_token is None:
return None
validated_token = self.get_validated_token(raw_token)
return self.get_user(validated_token), None
else:
return user, None

View File

@ -1,5 +1,5 @@
from django.utils.translation import gettext_lazy as _
from rest_framework import exceptions, status
from rest_framework import exceptions, serializers, status
class ProjectBaseException(exceptions.APIException):
@ -142,3 +142,24 @@ class PasswordResetRequestExistedError(exceptions.APIException):
"""
status_code = status.HTTP_400_BAD_REQUEST
default_detail = _('Password reset request is already exists and valid.')
class ObjectAlreadyAdded(serializers.ValidationError):
"""
The exception must be thrown if the object has already been added to the
list.
"""
default_detail = _('Object has already been added.')
class BindingObjectNotFound(serializers.ValidationError):
"""The exception must be thrown if the object not found."""
default_detail = _('Binding object not found.')
class RemovedBindingObjectNotFound(serializers.ValidationError):
"""The exception must be thrown if the object not found."""
default_detail = _('Removed binding object not found.')

File diff suppressed because it is too large Load Diff

View File

@ -73,6 +73,7 @@ PROJECT_APPS = [
'comment.apps.CommentConfig',
'favorites.apps.FavoritesConfig',
'rating.apps.RatingConfig',
'tag.apps.TagConfig',
]
EXTERNAL_APPS = [

View File

@ -3,12 +3,15 @@ from django.urls import path, include
app_name = 'back'
urlpatterns = [
path('gallery/', include(('gallery.urls', 'gallery'),
namespace='gallery')),
path('account/', include('account.urls.back')),
path('comment/', include('comment.urls.back')),
path('establishments/', include('establishment.urls.back')),
path('gallery/', include(('gallery.urls', 'gallery'), namespace='gallery')),
path('location/', include('location.urls.back')),
path('news/', include('news.urls.back')),
path('account/', include('account.urls.back')),
path('comment/', include('comment.urls.back')),
path('review/', include('review.urls.back')),
]
path('tags/', include(('tag.urls.back', 'tag'), namespace='tag')),
]

View File

@ -24,11 +24,13 @@ urlpatterns = [
path('collections/', include('collection.urls.web')),
path('establishments/', include('establishment.urls.web')),
path('news/', include('news.urls.web')),
path('notifications/', include(('notification.urls.web', "notification"), namespace='notification')),
path('notifications/', include(('notification.urls.web', "notification"),
namespace='notification')),
path('partner/', include('partner.urls.web')),
path('location/', include('location.urls.web')),
path('main/', include('main.urls.web')),
path('recipes/', include('recipe.urls.web')),
path('tags/', include('tag.urls.web')),
path('translation/', include('translation.urls')),
path('comments/', include('comment.urls.web')),
path('favorites/', include('favorites.urls')),