added migrations, refactored establishment serializers

This commit is contained in:
Anatoly 2020-02-04 15:07:58 +03:00
parent 11cd688851
commit b286bbb1fa
7 changed files with 116 additions and 33 deletions

View File

@ -182,7 +182,7 @@ class LoginByUsernameOrEmailView(JWTGenericViewMixin, generics.GenericAPIView):
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
""" """
## Login view. ## Login view.
POST-request data ### POST-request data
``` ```
{ {
"username_or_email": <str>, "username_or_email": <str>,
@ -191,11 +191,12 @@ class LoginByUsernameOrEmailView(JWTGenericViewMixin, generics.GenericAPIView):
"source": <int> # 0 - Mobile, 1 - Web, 2 - All (by default used: 1) "source": <int> # 0 - Mobile, 1 - Web, 2 - All (by default used: 1)
} }
``` ```
## Response ### Response
After a successful login, server side set up access_token and refresh token to cookies. After a successful login, server side set up access_token and refresh token to cookies.
In a payload of access token, the following information is being embed: In a payload of access token, the following information is being embed:
see `User().get_user_info()`. see `User().get_user_info()`.
### Description
COOKIE Max-age are determined by `remember` flag: COOKIE Max-age are determined by `remember` flag:
if `remember` is `True` then `Max-age` parameter taken from `settings.COOKIES_MAX_AGE` if `remember` is `True` then `Max-age` parameter taken from `settings.COOKIES_MAX_AGE`
otherwise using session COOKIE Max-age. otherwise using session COOKIE Max-age.
@ -221,7 +222,23 @@ class LogoutView(JWTGenericViewMixin, generics.GenericAPIView):
permission_classes = (IsAuthenticatedAndTokenIsValid, ) permission_classes = (IsAuthenticatedAndTokenIsValid, )
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
"""Override create method""" """
## Logout view.
### POST-request data
```
{}
```
### Response
If user has *valid* `access_token` in COOKIES, then response return
blank response data with `HTTP_STATUS_CODE` *204*.
### Description
For complete logout, user must provide *valid* `access_token`
(`access_token` must be kept in `COOKIES`).
After successful request with valid access_token, token would be expired,
for reuse protection.
"""
# Get access token objs by JTI # Get access token objs by JTI
access_token = AccessToken(request.COOKIES.get('access_token')) access_token = AccessToken(request.COOKIES.get('access_token'))
access_token_obj = JWTAccessToken.objects.get(jti=access_token.payload.get('jti')) access_token_obj = JWTAccessToken.objects.get(jti=access_token.payload.get('jti'))

View File

@ -0,0 +1,19 @@
# Generated by Django 2.2.7 on 2020-02-04 12:05
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('comment', '0008_comment_status'),
]
operations = [
migrations.AlterField(
model_name='comment',
name='mark',
field=models.PositiveIntegerField(blank=True, default=None, null=True, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10)], verbose_name='Mark'),
),
]

View File

@ -0,0 +1,24 @@
# Generated by Django 2.2.7 on 2020-02-04 12:05
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('establishment', '0097_merge_20200204_1135'),
]
operations = [
migrations.AlterField(
model_name='company',
name='establishment',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='companies', to='establishment.Establishment', verbose_name='establishment'),
),
migrations.AlterField(
model_name='establishmentnote',
name='establishment',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='establishment.Establishment', verbose_name='establishment'),
),
]

View File

@ -3,7 +3,6 @@ from datetime import datetime
from functools import reduce from functools import reduce
from operator import or_ from operator import or_
from typing import List from typing import List
from slugify import slugify
import elasticsearch_dsl import elasticsearch_dsl
from django.conf import settings from django.conf import settings
@ -668,24 +667,6 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin,
def __str__(self): def __str__(self):
return f'id:{self.id}-{self.name}' return f'id:{self.id}-{self.name}'
def save(self, *args, **kwargs):
slugify_slug = slugify(
self.index_name,
word_boundary=True
)
self.slug = slugify_slug
super(Establishment, self).save(*args, **kwargs)
def delete(self, using=None, keep_parents=False):
"""Overridden delete method"""
# TODO: If this does not contradict the plan,
# it is better to change it.
# Just add CASCADE to Company and Note in establishment fk field.
# Delete all related companies
self.companies.all().delete()
# Delete all related notes
self.notes.all().delete()
return super().delete(using, keep_parents)
@property @property
def visible_tags(self): def visible_tags(self):
@ -953,7 +934,7 @@ class EstablishmentNote(ProjectBaseMixin):
"""Note model for Establishment entity.""" """Note model for Establishment entity."""
old_id = models.PositiveIntegerField(null=True, blank=True) old_id = models.PositiveIntegerField(null=True, blank=True)
text = models.TextField(verbose_name=_('text')) text = models.TextField(verbose_name=_('text'))
establishment = models.ForeignKey(Establishment, on_delete=models.PROTECT, establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE,
related_name='notes', related_name='notes',
verbose_name=_('establishment')) verbose_name=_('establishment'))
user = models.ForeignKey('account.User', on_delete=models.PROTECT, user = models.ForeignKey('account.User', on_delete=models.PROTECT,
@ -1563,7 +1544,7 @@ class CompanyQuerySet(models.QuerySet):
class Company(ProjectBaseMixin): class Company(ProjectBaseMixin):
"""Establishment company model.""" """Establishment company model."""
establishment = models.ForeignKey(Establishment, on_delete=models.PROTECT, establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE,
related_name='companies', related_name='companies',
verbose_name=_('establishment')) verbose_name=_('establishment'))
name = models.CharField(max_length=255, verbose_name=_('name')) name = models.CharField(max_length=255, verbose_name=_('name'))

View File

@ -3,7 +3,9 @@ from functools import lru_cache
from django.db.models import F from django.db.models import F
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from phonenumber_field.serializerfields import PhoneNumberField
from rest_framework import serializers from rest_framework import serializers
from slugify import slugify
from account.serializers.common import UserShortSerializer from account.serializers.common import UserShortSerializer
from collection.models import Guide from collection.models import Guide
@ -15,6 +17,7 @@ from location.serializers import AddressDetailSerializer, TranslatedField
from main.models import Currency from main.models import Currency
from main.serializers import AwardSerializer from main.serializers import AwardSerializer
from utils.decorators import with_base_attributes from utils.decorators import with_base_attributes
from utils.methods import string_random
from utils.serializers import ImageBaseSerializer, ProjectModelSerializer, TimeZoneChoiceField, \ from utils.serializers import ImageBaseSerializer, ProjectModelSerializer, TimeZoneChoiceField, \
PhoneMixinSerializer PhoneMixinSerializer
@ -68,7 +71,7 @@ class EstablishmentListCreateSerializer(model_serializers.EstablishmentBaseSeria
source='contact_phones', source='contact_phones',
allow_null=True, allow_null=True,
allow_empty=True, allow_empty=True,
child=serializers.CharField(max_length=128), child=PhoneNumberField(),
required=False, required=False,
write_only=True, write_only=True,
) )
@ -126,6 +129,18 @@ class EstablishmentListCreateSerializer(model_serializers.EstablishmentBaseSeria
if 'contact_emails' in validated_data: if 'contact_emails' in validated_data:
emails_list = validated_data.pop('contact_emails') emails_list = validated_data.pop('contact_emails')
index_name = validated_data.get('index_name')
if 'slug' in validated_data and index_name:
slug = slugify(
index_name,
word_boundary=True
)
while models.Establishment.objects.filter(slug=slug).exists():
slug = slugify(
f'{index_name} {string_random()}',
word_boundary=True
)
instance = super().create(validated_data) instance = super().create(validated_data)
phones_handler(phones_list, instance) phones_handler(phones_list, instance)
emails_handler(emails_list, instance) emails_handler(emails_list, instance)
@ -165,7 +180,15 @@ class EstablishmentRUDSerializer(model_serializers.EstablishmentBaseSerializer):
subtypes = model_serializers.EstablishmentSubTypeBaseSerializer(source='establishment_subtypes', subtypes = model_serializers.EstablishmentSubTypeBaseSerializer(source='establishment_subtypes',
read_only=True, many=True) read_only=True, many=True)
type = model_serializers.EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True) type = model_serializers.EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True)
phones = ContactPhonesSerializer(read_only=True, many=True) phones = serializers.ListField(
source='contact_phones',
allow_null=True,
allow_empty=True,
child=PhoneNumberField(),
required=False,
write_only=True,
)
contact_phones = ContactPhonesSerializer(source='phones', read_only=True, many=True)
class Meta(model_serializers.EstablishmentBaseSerializer.Meta): class Meta(model_serializers.EstablishmentBaseSerializer.Meta):
fields = [ fields = [
@ -176,6 +199,7 @@ class EstablishmentRUDSerializer(model_serializers.EstablishmentBaseSerializer):
'index_name', 'index_name',
'website', 'website',
'phones', 'phones',
'contact_phones',
'emails', 'emails',
'price_level', 'price_level',
'toque_number', 'toque_number',
@ -196,6 +220,11 @@ class EstablishmentRUDSerializer(model_serializers.EstablishmentBaseSerializer):
'status_display', 'status_display',
] ]
def to_representation(self, instance):
data = super(EstablishmentRUDSerializer, self).to_representation(instance)
data['phones'] = data.pop('contact_phones', None)
return data
def update(self, instance, validated_data): def update(self, instance, validated_data):
phones_list = [] phones_list = []
if 'contact_phones' in validated_data: if 'contact_phones' in validated_data:

View File

@ -0,0 +1,19 @@
# Generated by Django 2.2.7 on 2020-02-04 12:05
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('product', '0025_auto_20191227_1443'),
]
operations = [
migrations.AlterField(
model_name='productnote',
name='product',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='product.Product', verbose_name='product'),
),
]

View File

@ -344,12 +344,6 @@ class Product(GalleryMixin, TranslatedFieldsMixin, BaseAttributes,
"""Override str dunder method.""" """Override str dunder method."""
return f'{self.name}' return f'{self.name}'
def delete(self, using=None, keep_parents=False):
"""Overridden delete method"""
# Delete all related notes
self.notes.all().delete()
return super().delete(using, keep_parents)
@property @property
def product_type_translated_name(self): def product_type_translated_name(self):
"""Get translated name of product type.""" """Get translated name of product type."""
@ -624,7 +618,7 @@ class ProductNote(ProjectBaseMixin):
"""Note model for Product entity.""" """Note model for Product entity."""
old_id = models.PositiveIntegerField(null=True, blank=True) old_id = models.PositiveIntegerField(null=True, blank=True)
text = models.TextField(verbose_name=_('text')) text = models.TextField(verbose_name=_('text'))
product = models.ForeignKey(Product, on_delete=models.PROTECT, product = models.ForeignKey(Product, on_delete=models.CASCADE,
related_name='notes', related_name='notes',
verbose_name=_('product')) verbose_name=_('product'))
user = models.ForeignKey('account.User', on_delete=models.PROTECT, user = models.ForeignKey('account.User', on_delete=models.PROTECT,