Merge branch 'feature/slugs-in-path' into 'develop'
Feature/slugs in path See merge request gm/gm-backend!20
This commit is contained in:
commit
d627df41c0
18
apps/collection/migrations/0013_collection_slug.py
Normal file
18
apps/collection/migrations/0013_collection_slug.py
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.2.4 on 2019-09-24 08:06
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('collection', '0012_auto_20190923_1340'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='collection',
|
||||||
|
name='slug',
|
||||||
|
field=models.SlugField(null=True, unique=True, verbose_name='Collection slug'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from django.contrib.postgres.fields import JSONField
|
from django.contrib.postgres.fields import JSONField
|
||||||
from django.contrib.contenttypes.fields import ContentType
|
from django.contrib.contenttypes.fields import ContentType
|
||||||
|
|
||||||
from utils.models import TJSONField
|
from utils.models import TJSONField
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
@ -67,6 +68,8 @@ class Collection(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin,
|
||||||
description = TJSONField(
|
description = TJSONField(
|
||||||
_('description'), null=True, blank=True,
|
_('description'), null=True, blank=True,
|
||||||
default=None, help_text='{"en-GB":"some text"}')
|
default=None, help_text='{"en-GB":"some text"}')
|
||||||
|
slug = models.SlugField(max_length=50, unique=True,
|
||||||
|
verbose_name=_('Collection slug'), editable=True, null=True)
|
||||||
|
|
||||||
objects = CollectionQuerySet.as_manager()
|
objects = CollectionQuerySet.as_manager()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ class CollectionSerializer(serializers.ModelSerializer):
|
||||||
block_size = serializers.JSONField()
|
block_size = serializers.JSONField()
|
||||||
is_publish = serializers.BooleanField()
|
is_publish = serializers.BooleanField()
|
||||||
on_top = serializers.BooleanField()
|
on_top = serializers.BooleanField()
|
||||||
|
slug = serializers.SlugField(allow_blank=False, required=True, unique=True, max_length=50)
|
||||||
|
|
||||||
# REQUEST
|
# REQUEST
|
||||||
start = serializers.DateTimeField(write_only=True)
|
start = serializers.DateTimeField(write_only=True)
|
||||||
|
|
@ -34,6 +35,7 @@ class CollectionSerializer(serializers.ModelSerializer):
|
||||||
'on_top',
|
'on_top',
|
||||||
'country',
|
'country',
|
||||||
'block_size',
|
'block_size',
|
||||||
|
'slug',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,11 @@ class BaseTestCase(APITestCase):
|
||||||
self.newsletter = True
|
self.newsletter = True
|
||||||
self.user = User.objects.create_user(
|
self.user = User.objects.create_user(
|
||||||
username=self.username, email=self.email, password=self.password)
|
username=self.username, email=self.email, password=self.password)
|
||||||
#get tokkens
|
#get tokens
|
||||||
tokkens = User.create_jwt_tokens(self.user)
|
tokens = User.create_jwt_tokens(self.user)
|
||||||
self.client.cookies = SimpleCookie(
|
self.client.cookies = SimpleCookie(
|
||||||
{'access_token': tokkens.get('access_token'),
|
{'access_token': tokens.get('access_token'),
|
||||||
'refresh_token': tokkens.get('refresh_token'),
|
'refresh_token': tokens.get('refresh_token'),
|
||||||
'country_code': 'en'})
|
'country_code': 'en'})
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -51,11 +51,12 @@ class CollectionDetailTests(BaseTestCase):
|
||||||
is_publish=True,
|
is_publish=True,
|
||||||
start=datetime.now(pytz.utc),
|
start=datetime.now(pytz.utc),
|
||||||
end=datetime.now(pytz.utc),
|
end=datetime.now(pytz.utc),
|
||||||
country=country
|
country=country,
|
||||||
|
slug='test-collection-slug',
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_collection_detail_Read(self):
|
def test_collection_detail_Read(self):
|
||||||
response = self.client.get(f'/api/web/collections/{self.collection.id}/establishments/?country_code=en',
|
response = self.client.get(f'/api/web/collections/{self.collection.slug}/establishments/?country_code=en',
|
||||||
format='json')
|
format='json')
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ app_name = 'collection'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.CollectionListView.as_view(), name='list'),
|
path('', views.CollectionListView.as_view(), name='list'),
|
||||||
path('<int:pk>/establishments/', views.CollectionEstablishmentListView.as_view(),
|
path('<slug:slug>/establishments/', views.CollectionEstablishmentListView.as_view(),
|
||||||
name='detail'),
|
name='detail'),
|
||||||
|
|
||||||
path('guides/', views.GuideListView.as_view(), name='guides-list'),
|
path('guides/', views.GuideListView.as_view(), name='guides-list'),
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ class CollectionEstablishmentListView(CollectionListView):
|
||||||
permission_classes = (permissions.AllowAny,)
|
permission_classes = (permissions.AllowAny,)
|
||||||
pagination_class = ProjectPageNumberPagination
|
pagination_class = ProjectPageNumberPagination
|
||||||
serializer_class = EstablishmentListSerializer
|
serializer_class = EstablishmentListSerializer
|
||||||
|
lookup_field = 'slug'
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -47,7 +48,7 @@ class CollectionEstablishmentListView(CollectionListView):
|
||||||
"""
|
"""
|
||||||
queryset = super(CollectionEstablishmentListView, self).get_queryset()
|
queryset = super(CollectionEstablishmentListView, self).get_queryset()
|
||||||
# Perform the lookup filtering.
|
# Perform the lookup filtering.
|
||||||
collection = get_object_or_404(queryset, pk=self.kwargs['pk'])
|
collection = get_object_or_404(queryset, slug=self.kwargs['slug'])
|
||||||
|
|
||||||
# May raise a permission denied
|
# May raise a permission denied
|
||||||
self.check_object_permissions(self.request, collection)
|
self.check_object_permissions(self.request, collection)
|
||||||
|
|
|
||||||
18
apps/establishment/migrations/0031_establishment_slug.py
Normal file
18
apps/establishment/migrations/0031_establishment_slug.py
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.2.4 on 2019-09-24 08:06
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('establishment', '0030_auto_20190923_1340'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='establishment',
|
||||||
|
name='slug',
|
||||||
|
field=models.SlugField(null=True, unique=True, verbose_name='Establishment slug'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -166,15 +166,15 @@ class EstablishmentQuerySet(models.QuerySet):
|
||||||
total_mark=(models.F('distance_mark') + models.F('additional_mark')) *
|
total_mark=(models.F('distance_mark') + models.F('additional_mark')) *
|
||||||
models.F('intermediate_public_mark'))
|
models.F('intermediate_public_mark'))
|
||||||
|
|
||||||
def similar(self, establishment_pk: int):
|
def similar(self, establishment_slug: str):
|
||||||
"""
|
"""
|
||||||
Return QuerySet with objects that similar to Establishment.
|
Return QuerySet with objects that similar to Establishment.
|
||||||
:param establishment_pk: integer
|
:param establishment_slug: str Establishment slug
|
||||||
"""
|
"""
|
||||||
establishment_qs = Establishment.objects.filter(pk=establishment_pk)
|
establishment_qs = Establishment.objects.filter(slug=establishment_slug)
|
||||||
if establishment_qs.exists():
|
if establishment_qs.exists():
|
||||||
establishment = establishment_qs.first()
|
establishment = establishment_qs.first()
|
||||||
return self.exclude(pk=establishment_pk) \
|
return self.exclude(slug=establishment_slug) \
|
||||||
.filter(is_publish=True,
|
.filter(is_publish=True,
|
||||||
image_url__isnull=False,
|
image_url__isnull=False,
|
||||||
reviews__isnull=False,
|
reviews__isnull=False,
|
||||||
|
|
@ -277,6 +277,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,
|
||||||
|
verbose_name=_('Establishment slug'), editable=True)
|
||||||
|
|
||||||
awards = generic.GenericRelation(to='main.Award')
|
awards = generic.GenericRelation(to='main.Award')
|
||||||
tags = generic.GenericRelation(to='main.MetaDataContent')
|
tags = generic.GenericRelation(to='main.MetaDataContent')
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ class EstablishmentListCreateSerializer(EstablishmentBaseSerializer):
|
||||||
phones = ContactPhonesSerializer(read_only=True, many=True, )
|
phones = ContactPhonesSerializer(read_only=True, many=True, )
|
||||||
emails = ContactEmailsSerializer(read_only=True, many=True, )
|
emails = ContactEmailsSerializer(read_only=True, many=True, )
|
||||||
socials = SocialNetworkRelatedSerializers(read_only=True, many=True, )
|
socials = SocialNetworkRelatedSerializers(read_only=True, many=True, )
|
||||||
|
slug = serializers.SlugField(required=True, allow_blank=False, unique=True, max_length=50)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Establishment
|
model = models.Establishment
|
||||||
|
|
@ -35,6 +36,7 @@ class EstablishmentListCreateSerializer(EstablishmentBaseSerializer):
|
||||||
'type',
|
'type',
|
||||||
'socials',
|
'socials',
|
||||||
'image_url',
|
'image_url',
|
||||||
|
'slug',
|
||||||
# TODO: check in admin filters
|
# TODO: check in admin filters
|
||||||
'is_publish'
|
'is_publish'
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -146,6 +146,7 @@ class EstablishmentBaseSerializer(serializers.ModelSerializer):
|
||||||
subtypes = EstablishmentSubTypeSerializer(many=True)
|
subtypes = EstablishmentSubTypeSerializer(many=True)
|
||||||
address = AddressSerializer()
|
address = AddressSerializer()
|
||||||
tags = MetaDataContentSerializer(many=True)
|
tags = MetaDataContentSerializer(many=True)
|
||||||
|
slug = serializers.SlugField(allow_blank=False, required=True, unique=True, max_length=50)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Meta class."""
|
"""Meta class."""
|
||||||
|
|
@ -162,6 +163,7 @@ class EstablishmentBaseSerializer(serializers.ModelSerializer):
|
||||||
'subtypes',
|
'subtypes',
|
||||||
'address',
|
'address',
|
||||||
'tags',
|
'tags',
|
||||||
|
'slug',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -197,6 +199,8 @@ class EstablishmentDetailSerializer(EstablishmentListSerializer):
|
||||||
best_price_menu = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
|
best_price_menu = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
|
||||||
best_price_carte = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
|
best_price_carte = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
|
||||||
|
|
||||||
|
slug = serializers.SlugField(required=True, allow_blank=False, unique=True, max_length=50)
|
||||||
|
|
||||||
in_favorites = serializers.SerializerMethodField()
|
in_favorites = serializers.SerializerMethodField()
|
||||||
|
|
||||||
image = serializers.URLField(source='image_url')
|
image = serializers.URLField(source='image_url')
|
||||||
|
|
@ -224,6 +228,7 @@ class EstablishmentDetailSerializer(EstablishmentListSerializer):
|
||||||
'best_price_menu',
|
'best_price_menu',
|
||||||
'best_price_carte',
|
'best_price_carte',
|
||||||
'transportation',
|
'transportation',
|
||||||
|
'slug',
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_review(self, obj):
|
def get_review(self, obj):
|
||||||
|
|
@ -261,8 +266,8 @@ class EstablishmentCommentCreateSerializer(comment_serializers.CommentSerializer
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
"""Override validate method"""
|
"""Override validate method"""
|
||||||
# Check establishment object
|
# Check establishment object
|
||||||
establishment_id = self.context.get('request').parser_context.get('kwargs').get('pk')
|
establishment_slug = self.context.get('request').parser_context.get('kwargs').get('slug')
|
||||||
establishment_qs = models.Establishment.objects.filter(id=establishment_id)
|
establishment_qs = models.Establishment.objects.filter(slug=establishment_slug)
|
||||||
if not establishment_qs.exists():
|
if not establishment_qs.exists():
|
||||||
raise serializers.ValidationError({'detail': _('Establishment not found.')})
|
raise serializers.ValidationError({'detail': _('Establishment not found.')})
|
||||||
attrs['establishment'] = establishment_qs.first()
|
attrs['establishment'] = establishment_qs.first()
|
||||||
|
|
|
||||||
|
|
@ -29,13 +29,15 @@ class BaseTestCase(APITestCase):
|
||||||
|
|
||||||
class EstablishmentTests(BaseTestCase):
|
class EstablishmentTests(BaseTestCase):
|
||||||
def test_establishment_CRUD(self):
|
def test_establishment_CRUD(self):
|
||||||
response = self.client.get('/api/back/establishments/', format='json')
|
params = {'page': 1, 'page_size': 1,}
|
||||||
|
response = self.client.get('/api/back/establishments/', params, format='json')
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'name': 'Test establishment',
|
'name': 'Test establishment',
|
||||||
'type_id': self.establishment_type.id,
|
'type_id': self.establishment_type.id,
|
||||||
'is_publish': True
|
'is_publish': True,
|
||||||
|
'slug': 'test-establishment-slug',
|
||||||
}
|
}
|
||||||
|
|
||||||
response = self.client.post('/api/back/establishments/', data=data, format='json')
|
response = self.client.post('/api/back/establishments/', data=data, format='json')
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@ app_name = 'establishment'
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.EstablishmentListView.as_view(), name='list'),
|
path('', views.EstablishmentListView.as_view(), name='list'),
|
||||||
path('tags/', views.EstablishmentTagListView.as_view(), name='tags'),
|
path('tags/', views.EstablishmentTagListView.as_view(), name='tags'),
|
||||||
path('<int:pk>/', views.EstablishmentRetrieveView.as_view(), name='detail'),
|
path('<slug:slug>/', views.EstablishmentRetrieveView.as_view(), name='detail'),
|
||||||
path('<int:pk>/similar/', views.EstablishmentSimilarListView.as_view(), name='similar'),
|
path('<slug:slug>/similar/', views.EstablishmentSimilarListView.as_view(), name='similar'),
|
||||||
path('<int:pk>/comments/', views.EstablishmentCommentListView.as_view(), name='list-comments'),
|
path('<slug:slug>/comments/', views.EstablishmentCommentListView.as_view(), name='list-comments'),
|
||||||
path('<int:pk>/comments/create/', views.EstablishmentCommentCreateView.as_view(),
|
path('<slug:slug>/comments/create/', views.EstablishmentCommentCreateView.as_view(),
|
||||||
name='create-comment'),
|
name='create-comment'),
|
||||||
path('<int:pk>/comments/<int:comment_id>/', views.EstablishmentCommentRUDView.as_view(),
|
path('<slug:slug>/comments/<int:comment_id>/', views.EstablishmentCommentRUDView.as_view(),
|
||||||
name='rud-comment'),
|
name='rud-comment'),
|
||||||
path('<int:pk>/favorites/', views.EstablishmentFavoritesCreateDestroyView.as_view(),
|
path('<slug:slug>/favorites/', views.EstablishmentFavoritesCreateDestroyView.as_view(),
|
||||||
name='add-to-favorites')
|
name='add-to-favorites')
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,12 @@ class EstablishmentSimilarListView(EstablishmentListView):
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""Override get_queryset method"""
|
"""Override get_queryset method"""
|
||||||
return super().get_queryset().similar(establishment_pk=self.kwargs.get('pk'))\
|
return super().get_queryset().similar(establishment_slug=self.kwargs.get('slug'))\
|
||||||
.order_by('-total_mark')[:13]
|
.order_by('-total_mark')[:13]
|
||||||
|
|
||||||
class EstablishmentRetrieveView(EstablishmentMixin, generics.RetrieveAPIView):
|
class EstablishmentRetrieveView(EstablishmentMixin, generics.RetrieveAPIView):
|
||||||
"""Resource for getting a establishment."""
|
"""Resource for getting a establishment."""
|
||||||
|
lookup_field = 'slug'
|
||||||
serializer_class = serializers.EstablishmentDetailSerializer
|
serializer_class = serializers.EstablishmentDetailSerializer
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -48,6 +49,7 @@ class EstablishmentTypeListView(generics.ListAPIView):
|
||||||
|
|
||||||
class EstablishmentCommentCreateView(generics.CreateAPIView):
|
class EstablishmentCommentCreateView(generics.CreateAPIView):
|
||||||
"""View for create new comment."""
|
"""View for create new comment."""
|
||||||
|
lookup_field = 'slug'
|
||||||
serializer_class = serializers.EstablishmentCommentCreateSerializer
|
serializer_class = serializers.EstablishmentCommentCreateSerializer
|
||||||
queryset = comment_models.Comment.objects.all()
|
queryset = comment_models.Comment.objects.all()
|
||||||
|
|
||||||
|
|
@ -59,9 +61,11 @@ class EstablishmentCommentListView(generics.ListAPIView):
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""Override get_queryset method"""
|
"""Override get_queryset method"""
|
||||||
|
|
||||||
|
establishment = get_object_or_404(models.Establishment, slug=self.kwargs['slug'])
|
||||||
return comment_models.Comment.objects.by_content_type(app_label='establishment',
|
return comment_models.Comment.objects.by_content_type(app_label='establishment',
|
||||||
model='establishment')\
|
model='establishment')\
|
||||||
.by_object_id(object_id=self.kwargs.get('pk'))\
|
.by_object_id(object_id=establishment.pk)\
|
||||||
.order_by('-created')
|
.order_by('-created')
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -77,7 +81,7 @@ class EstablishmentCommentRUDView(generics.RetrieveUpdateDestroyAPIView):
|
||||||
queryset = self.filter_queryset(self.get_queryset())
|
queryset = self.filter_queryset(self.get_queryset())
|
||||||
|
|
||||||
establishment_obj = get_object_or_404(queryset,
|
establishment_obj = get_object_or_404(queryset,
|
||||||
pk=self.kwargs['pk'])
|
slug=self.kwargs['slug'])
|
||||||
comment_obj = get_object_or_404(establishment_obj.comments.by_user(self.request.user),
|
comment_obj = get_object_or_404(establishment_obj.comments.by_user(self.request.user),
|
||||||
pk=self.kwargs['comment_id'])
|
pk=self.kwargs['comment_id'])
|
||||||
|
|
||||||
|
|
@ -90,15 +94,18 @@ class EstablishmentCommentRUDView(generics.RetrieveUpdateDestroyAPIView):
|
||||||
class EstablishmentFavoritesCreateDestroyView(generics.CreateAPIView, generics.DestroyAPIView):
|
class EstablishmentFavoritesCreateDestroyView(generics.CreateAPIView, generics.DestroyAPIView):
|
||||||
"""View for create/destroy establishment from favorites."""
|
"""View for create/destroy establishment from favorites."""
|
||||||
serializer_class = serializers.EstablishmentFavoritesCreateSerializer
|
serializer_class = serializers.EstablishmentFavoritesCreateSerializer
|
||||||
|
lookup_field = 'slug'
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
"""
|
"""
|
||||||
Returns the object the view is displaying.
|
Returns the object the view is displaying.
|
||||||
"""
|
"""
|
||||||
|
establishment_obj = get_object_or_404(models.Establishment,
|
||||||
|
slug=self.kwargs['slug'])
|
||||||
obj = get_object_or_404(
|
obj = get_object_or_404(
|
||||||
self.request.user.favorites.by_content_type(app_label='establishment',
|
self.request.user.favorites.by_content_type(app_label='establishment',
|
||||||
model='establishment')
|
model='establishment')
|
||||||
.by_object_id(object_id=self.kwargs['pk']))
|
.by_object_id(object_id=establishment_obj.pk))
|
||||||
# May raise a permission denied
|
# May raise a permission denied
|
||||||
self.check_object_permissions(self.request, obj)
|
self.check_object_permissions(self.request, obj)
|
||||||
return obj
|
return obj
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ class BaseTestCase(APITestCase):
|
||||||
use_subtypes=False)
|
use_subtypes=False)
|
||||||
|
|
||||||
self.test_establishment = Establishment.objects.create(name="test establishment",
|
self.test_establishment = Establishment.objects.create(name="test establishment",
|
||||||
name_transliterated="test-establishment",
|
|
||||||
description={"en-GB": "description of test establishment"},
|
description={"en-GB": "description of test establishment"},
|
||||||
establishment_type=self.test_establishment_type,
|
establishment_type=self.test_establishment_type,
|
||||||
is_publish=True)
|
is_publish=True)
|
||||||
|
|
|
||||||
20
apps/news/migrations/0013_auto_20190924_0806.py
Normal file
20
apps/news/migrations/0013_auto_20190924_0806.py
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Generated by Django 2.2.4 on 2019-09-24 08:06
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('gallery', '0001_initial'),
|
||||||
|
('news', '0012_auto_20190923_1416'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='news',
|
||||||
|
name='slug',
|
||||||
|
field=models.SlugField(null=True, unique=True, verbose_name='News slug'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -63,6 +63,8 @@ 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, null=True,
|
||||||
|
verbose_name=_('News slug'), editable=True,)
|
||||||
playlist = models.IntegerField(_('playlist'))
|
playlist = models.IntegerField(_('playlist'))
|
||||||
is_publish = models.BooleanField(default=False,
|
is_publish = models.BooleanField(default=False,
|
||||||
verbose_name=_('Publish status'))
|
verbose_name=_('Publish status'))
|
||||||
|
|
@ -93,8 +95,8 @@ class News(BaseAttributes, TranslatedFieldsMixin):
|
||||||
verbose_name_plural = _('news')
|
verbose_name_plural = _('news')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'news: {self.id}'
|
return f'news: {self.slug}'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def web_url(self):
|
def web_url(self):
|
||||||
return reverse('web:news:rud', kwargs={'pk': self.pk})
|
return reverse('web:news:rud', kwargs={'slug': self.slug})
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ class NewsBaseSerializer(serializers.ModelSerializer):
|
||||||
news_type = NewsTypeSerializer(read_only=True)
|
news_type = NewsTypeSerializer(read_only=True)
|
||||||
tags = MetaDataContentSerializer(read_only=True, many=True)
|
tags = MetaDataContentSerializer(read_only=True, many=True)
|
||||||
|
|
||||||
|
slug = serializers.SlugField(allow_blank=False, required=True, unique=True, max_length=50)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Meta class."""
|
"""Meta class."""
|
||||||
|
|
||||||
|
|
@ -41,6 +43,7 @@ class NewsBaseSerializer(serializers.ModelSerializer):
|
||||||
'preview_image_url',
|
'preview_image_url',
|
||||||
'news_type',
|
'news_type',
|
||||||
'tags',
|
'tags',
|
||||||
|
'slug',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
from http.cookies import SimpleCookie
|
from http.cookies import SimpleCookie
|
||||||
|
|
||||||
from django.test import TestCase
|
|
||||||
from rest_framework.test import APITestCase
|
from rest_framework.test import APITestCase
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from news.models import NewsType, News
|
from news.models import NewsType, News
|
||||||
from account.models import User
|
from account.models import User
|
||||||
|
|
@ -24,8 +24,9 @@ class BaseTestCase(APITestCase):
|
||||||
self.test_news_type = NewsType.objects.create(name="Test news type")
|
self.test_news_type = NewsType.objects.create(name="Test news type")
|
||||||
self.test_news = News.objects.create(created_by=self.user, modified_by=self.user, title={"en-GB": "Test news"},
|
self.test_news = News.objects.create(created_by=self.user, modified_by=self.user, title={"en-GB": "Test news"},
|
||||||
news_type=self.test_news_type, description={"en-GB": "Description test news"},
|
news_type=self.test_news_type, description={"en-GB": "Description test news"},
|
||||||
playlist=1, start="2020-12-03 12:00:00", end="2020-12-13 12:00:00",
|
playlist=1, start=datetime.now() + timedelta(hours=-2),
|
||||||
is_publish=True)
|
end=datetime.now() + timedelta(hours=2),
|
||||||
|
is_publish=True, slug='test-news-slug',)
|
||||||
|
|
||||||
|
|
||||||
class NewsTestCase(BaseTestCase):
|
class NewsTestCase(BaseTestCase):
|
||||||
|
|
@ -35,10 +36,10 @@ class NewsTestCase(BaseTestCase):
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
def test_news_detail(self):
|
def test_news_detail(self):
|
||||||
response = self.client.get(f"/api/web/news/{self.test_news.id}/")
|
response = self.client.get(f"/api/web/news/{self.test_news.slug}/")
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
def test_news_type_list(self):
|
def test_news_type_list(self):
|
||||||
response = self.client.get("/api/web/news/type/")
|
response = self.client.get("/api/web/news/types/")
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,6 @@ app_name = 'news'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.NewsListView.as_view(), name='list'),
|
path('', views.NewsListView.as_view(), name='list'),
|
||||||
path('<int:pk>/', views.NewsDetailView.as_view(), name='rud'),
|
path('<slug:slug>/', views.NewsDetailView.as_view(), name='rud'),
|
||||||
path('types/', views.NewsTypeListView.as_view(), name='type'),
|
path('types/', views.NewsTypeListView.as_view(), name='type'),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,12 @@ class NewsMixinView:
|
||||||
|
|
||||||
class NewsListView(NewsMixinView, generics.ListAPIView):
|
class NewsListView(NewsMixinView, generics.ListAPIView):
|
||||||
"""News list view."""
|
"""News list view."""
|
||||||
|
|
||||||
filter_class = filters.NewsListFilterSet
|
filter_class = filters.NewsListFilterSet
|
||||||
|
|
||||||
|
|
||||||
class NewsDetailView(NewsMixinView, generics.RetrieveAPIView):
|
class NewsDetailView(NewsMixinView, generics.RetrieveAPIView):
|
||||||
"""News detail view."""
|
"""News detail view."""
|
||||||
|
lookup_field = 'slug'
|
||||||
serializer_class = serializers.NewsDetailSerializer
|
serializer_class = serializers.NewsDetailSerializer
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
0
apps/news/views/common.py
Normal file
0
apps/news/views/common.py
Normal file
|
|
@ -13,7 +13,7 @@ class NewsDocumentViewSet(BaseDocumentViewSet):
|
||||||
"""News document ViewSet."""
|
"""News document ViewSet."""
|
||||||
|
|
||||||
document = NewsDocument
|
document = NewsDocument
|
||||||
lookup_field = 'id'
|
lookup_field = 'slug'
|
||||||
pagination_class = PageNumberPagination
|
pagination_class = PageNumberPagination
|
||||||
permission_classes = (permissions.AllowAny,)
|
permission_classes = (permissions.AllowAny,)
|
||||||
serializer_class = serializers.NewsDocumentSerializer
|
serializer_class = serializers.NewsDocumentSerializer
|
||||||
|
|
@ -39,7 +39,7 @@ class EstablishmentDocumentViewSet(BaseDocumentViewSet):
|
||||||
"""Establishment document ViewSet."""
|
"""Establishment document ViewSet."""
|
||||||
|
|
||||||
document = EstablishmentDocument
|
document = EstablishmentDocument
|
||||||
lookup_field = 'id'
|
lookup_field = 'slug'
|
||||||
pagination_class = PageNumberPagination
|
pagination_class = PageNumberPagination
|
||||||
permission_classes = (permissions.AllowAny,)
|
permission_classes = (permissions.AllowAny,)
|
||||||
serializer_class = serializers.EstablishmentDocumentSerializer
|
serializer_class = serializers.EstablishmentDocumentSerializer
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user