Merge remote-tracking branch 'origin/develop' into features/comments
# Conflicts: # apps/establishment/models.py # apps/establishment/serializers.py
This commit is contained in:
commit
21dd87e0ec
|
|
@ -4,6 +4,7 @@ from django.contrib.contenttypes.admin import GenericTabularInline
|
|||
|
||||
from establishment import models
|
||||
from main.models import Award, MetaDataContent
|
||||
from review import models as review_models
|
||||
|
||||
|
||||
@admin.register(models.EstablishmentType)
|
||||
|
|
@ -26,10 +27,29 @@ class MetaDataContentInline(GenericTabularInline):
|
|||
extra = 0
|
||||
|
||||
|
||||
class ContactPhoneInline(admin.TabularInline):
|
||||
"""Contact phone inline admin."""
|
||||
model = models.ContactPhone
|
||||
extra = 0
|
||||
|
||||
|
||||
class ContactEmailInline(admin.TabularInline):
|
||||
"""Contact email inline admin."""
|
||||
model = models.ContactEmail
|
||||
extra = 0
|
||||
|
||||
class ReviewInline(GenericTabularInline):
|
||||
model = review_models.Review
|
||||
extra = 0
|
||||
|
||||
|
||||
@admin.register(models.Establishment)
|
||||
class EstablishmentAdmin(admin.ModelAdmin):
|
||||
"""Establishment admin."""
|
||||
inlines = [AwardInline, MetaDataContentInline]
|
||||
inlines = [
|
||||
AwardInline, MetaDataContentInline,
|
||||
ContactPhoneInline, ContactEmailInline,
|
||||
ReviewInline]
|
||||
|
||||
|
||||
@admin.register(models.EstablishmentSchedule)
|
||||
|
|
|
|||
|
|
@ -1,16 +1,28 @@
|
|||
from django_filters import FilterSet
|
||||
"""Establishment app filters."""
|
||||
from django.core.validators import EMPTY_VALUES
|
||||
from django_filters import rest_framework as filters
|
||||
|
||||
from establishment import models
|
||||
|
||||
|
||||
class EstablishmentFilter(FilterSet):
|
||||
class EstablishmentFilter(filters.FilterSet):
|
||||
"""Establishment filterset."""
|
||||
|
||||
tag_id = filters.NumberFilter(field_name='tags__metadata__id',)
|
||||
award_id = filters.NumberFilter(field_name='awards__id',)
|
||||
search = filters.CharFilter(method='search_text')
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
||||
model = models.Establishment
|
||||
fields = (
|
||||
'tag_id',
|
||||
'award_id'
|
||||
'award_id',
|
||||
'search',
|
||||
)
|
||||
|
||||
def search_text(self, queryset, name, value):
|
||||
"""Search text."""
|
||||
if value not in EMPTY_VALUES:
|
||||
return queryset.search(value, locale=self.request.locale)
|
||||
return queryset
|
||||
|
|
|
|||
77
apps/establishment/migrations/0005_auto_20190901_0831.py
Normal file
77
apps/establishment/migrations/0005_auto_20190901_0831.py
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
# Generated by Django 2.2.4 on 2019-09-01 08:31
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import phonenumber_field.modelfields
|
||||
import utils.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('location', '0009_auto_20190901_0831'),
|
||||
('establishment', '0004_auto_20190828_1156'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Contact',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('address', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='location.Address')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'contact',
|
||||
'verbose_name_plural': 'contacts',
|
||||
},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='establishment',
|
||||
name='description',
|
||||
field=utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='Description'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='establishment',
|
||||
name='name',
|
||||
field=utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='Name'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='establishmentsubtype',
|
||||
name='name',
|
||||
field=utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='Description'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='establishmenttype',
|
||||
name='name',
|
||||
field=utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='Description'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ContactPhone',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('phone', phonenumber_field.modelfields.PhoneNumberField(max_length=128)),
|
||||
('contact', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='phones', to='establishment.Contact')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'contact phone',
|
||||
'verbose_name_plural': 'contact phones',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ContactEmail',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('email', models.EmailField(max_length=254)),
|
||||
('contact', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='emails', to='establishment.Contact')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'contact email',
|
||||
'verbose_name_plural': 'contact emails',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='contact',
|
||||
name='establishment',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contacts', to='establishment.Establishment'),
|
||||
),
|
||||
]
|
||||
14
apps/establishment/migrations/0006_merge_20190901_0846.py
Normal file
14
apps/establishment/migrations/0006_merge_20190901_0846.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Generated by Django 2.2.4 on 2019-09-01 08:46
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('establishment', '0005_auto_20190901_0831'),
|
||||
('establishment', '0005_establishmentschedule'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
]
|
||||
30
apps/establishment/migrations/0007_auto_20190901_1032.py
Normal file
30
apps/establishment/migrations/0007_auto_20190901_1032.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# Generated by Django 2.2.4 on 2019-09-01 10:32
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('establishment', '0006_merge_20190901_0846'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='contactemail',
|
||||
name='contact',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='contactphone',
|
||||
name='contact',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Contact',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='ContactEmail',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='ContactPhone',
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
# Generated by Django 2.2.4 on 2019-09-01 10:36
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import phonenumber_field.modelfields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('establishment', '0007_auto_20190901_1032'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ContactPhone',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('phone', phonenumber_field.modelfields.PhoneNumberField(max_length=128)),
|
||||
('establishment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='phones', to='establishment.Establishment')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'contact phone',
|
||||
'verbose_name_plural': 'contact phones',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ContactEmail',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('email', models.EmailField(max_length=254)),
|
||||
('establishment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='emails', to='establishment.Establishment')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'contact email',
|
||||
'verbose_name_plural': 'contact emails',
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -3,10 +3,13 @@ from django.contrib.contenttypes import fields as generic
|
|||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from phonenumber_field.modelfields import PhoneNumberField
|
||||
|
||||
from location.models import Address
|
||||
from utils.models import (ProjectBaseMixin, ImageMixin, TJSONField,
|
||||
TraslatedFieldsMixin, BaseAttributes)
|
||||
from utils.models import (
|
||||
ProjectBaseMixin, ImageMixin, TJSONField,
|
||||
TraslatedFieldsMixin, BaseAttributes
|
||||
)
|
||||
|
||||
|
||||
# todo: establishment type&subtypes check
|
||||
|
|
@ -14,7 +17,7 @@ class EstablishmentType(ProjectBaseMixin, TraslatedFieldsMixin):
|
|||
"""Establishment type model."""
|
||||
|
||||
name = TJSONField(blank=True, null=True, default=None, verbose_name=_('Description'),
|
||||
help_text='{"en":"some text"}')
|
||||
help_text='{"en-GB":"some text"}')
|
||||
use_subtypes = models.BooleanField(_('Use subtypes'), default=True)
|
||||
|
||||
class Meta:
|
||||
|
|
@ -38,7 +41,7 @@ class EstablishmentSubType(ProjectBaseMixin, TraslatedFieldsMixin):
|
|||
"""Establishment type model."""
|
||||
|
||||
name = TJSONField(blank=True, null=True, default=None, verbose_name=_('Description'),
|
||||
help_text='{"en":"some text"}')
|
||||
help_text='{"en-GB":"some text"}')
|
||||
establishment_type = models.ForeignKey(EstablishmentType,
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_('Type'))
|
||||
|
|
@ -51,23 +54,34 @@ class EstablishmentSubType(ProjectBaseMixin, TraslatedFieldsMixin):
|
|||
verbose_name = _('Establishment subtype')
|
||||
verbose_name_plural = _('Establishment subtypes')
|
||||
|
||||
# def __str__(self):
|
||||
# """__str__ method."""
|
||||
# return self.name
|
||||
|
||||
def clean_fields(self, exclude=None):
|
||||
if not self.establishment_type.use_subtypes:
|
||||
raise ValidationError(_('Establishment type is not use subtypes.'))
|
||||
|
||||
|
||||
class EstablishmentQuerySet(models.QuerySet):
|
||||
"""Extended queryset for Establishment model."""
|
||||
|
||||
def search(self, value, locale=None):
|
||||
"""Search text in JSON fields."""
|
||||
if locale is not None:
|
||||
filters = [
|
||||
{f'name__{locale}__icontains': value},
|
||||
{f'description__{locale}__icontains': value}
|
||||
]
|
||||
return self.filter(reduce(lambda x, y: x | y, [models.Q(**i) for i in filters]))
|
||||
else:
|
||||
return self.none()
|
||||
|
||||
|
||||
class Establishment(ProjectBaseMixin, ImageMixin, TraslatedFieldsMixin):
|
||||
"""Establishment model."""
|
||||
|
||||
name = TJSONField(blank=True, null=True, default=None,
|
||||
verbose_name=_('Name'), help_text='{"en":"some text"}')
|
||||
verbose_name=_('Name'), help_text='{"en-GB":"some text"}')
|
||||
description = TJSONField(blank=True, null=True, default=None,
|
||||
verbose_name=_('Description'),
|
||||
help_text='{"en":"some text"}')
|
||||
help_text='{"en-GB":"some text"}')
|
||||
public_mark = models.PositiveIntegerField(blank=True, null=True,
|
||||
default=None,
|
||||
verbose_name=_('Public mark'),)
|
||||
|
|
@ -89,6 +103,9 @@ class Establishment(ProjectBaseMixin, ImageMixin, TraslatedFieldsMixin):
|
|||
verbose_name=_('Price level'))
|
||||
awards = generic.GenericRelation(to='main.Award')
|
||||
tags = generic.GenericRelation(to='main.MetaDataContent')
|
||||
reviews = generic.GenericRelation(to='review.Review')
|
||||
|
||||
objects = EstablishmentQuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
|
@ -149,6 +166,97 @@ class EstablishmentSchedule(BaseAttributes):
|
|||
verbose_name_plural = _('Establishment schedules')
|
||||
|
||||
|
||||
class ContactPhone(models.Model):
|
||||
"""Contact phone model."""
|
||||
establishment = models.ForeignKey(
|
||||
Establishment, related_name='phones', on_delete=models.CASCADE)
|
||||
phone = PhoneNumberField()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('contact phone')
|
||||
verbose_name_plural = _('contact phones')
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.phone.as_e164}'
|
||||
|
||||
|
||||
class ContactEmail(models.Model):
|
||||
"""Contact email model."""
|
||||
establishment = models.ForeignKey(
|
||||
Establishment, related_name='emails', on_delete=models.CASCADE)
|
||||
email = models.EmailField()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('contact email')
|
||||
verbose_name_plural = _('contact emails')
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.email}'
|
||||
|
||||
#
|
||||
# class Wine(TraslatedFieldsMixin, models.Model):
|
||||
# """Wine model."""
|
||||
# establishment = models.ForeignKey(
|
||||
# 'establishment.Establishment', verbose_name=_('establishment'),
|
||||
# on_delete=models.CASCADE)
|
||||
# bottles = models.IntegerField(_('bottles'))
|
||||
# price_min = models.DecimalField(
|
||||
# _('price min'), max_digits=14, decimal_places=2)
|
||||
# price_max = models.DecimalField(
|
||||
# _('price max'), max_digits=14, decimal_places=2)
|
||||
# by_glass = models.BooleanField(_('by glass'))
|
||||
# price_glass_min = models.DecimalField(
|
||||
# _('price min'), max_digits=14, decimal_places=2)
|
||||
# price_glass_max = models.DecimalField(
|
||||
# _('price max'), max_digits=14, decimal_places=2)
|
||||
#
|
||||
#
|
||||
# class Plate(TraslatedFieldsMixin, models.Model):
|
||||
# """Plate model."""
|
||||
#
|
||||
# STARTER = 0
|
||||
# MAIN = 1
|
||||
# COURSE = 2
|
||||
# DESSERT = 3
|
||||
#
|
||||
# PLATE_TYPE_CHOICES = (
|
||||
# (STARTER, _('starter')),
|
||||
# (MAIN, _('main')),
|
||||
# (COURSE, _('course')),
|
||||
# (DESSERT, _('dessert')),
|
||||
# )
|
||||
# name = models.CharField(_('name'), max_length=255)
|
||||
# plate_type = models.PositiveSmallIntegerField(_('plate_type'), choices=PLATE_TYPE_CHOICES)
|
||||
# description = TJSONField(
|
||||
# blank=True, null=True, default=None, verbose_name=_('description'),
|
||||
# help_text='{"en-GB":"some text"}')
|
||||
# price = models.DecimalField(
|
||||
# _('price'), max_digits=14, decimal_places=2)
|
||||
# is_signature_plate = models.BooleanField(_('is signature plate'))
|
||||
# currency = models.ForeignKey(
|
||||
# 'main.Currency', verbose_name=_('currency'), on_delete=models.CASCADE)
|
||||
#
|
||||
# menu = models.ManyToManyField(to='Plate', verbose_name=_(''), through='establishment.Menu')
|
||||
#
|
||||
# class Meta:
|
||||
# verbose_name = _('plate')
|
||||
# verbose_name_plural = _('plates')
|
||||
#
|
||||
# def __str__(self):
|
||||
# return f'plate_id:{self.id}'
|
||||
#
|
||||
#
|
||||
# class Menu(TraslatedFieldsMixin, BaseAttributes):
|
||||
# """Menu model."""
|
||||
# establishment = models.ForeignKey(
|
||||
# 'establishment.Establishment', verbose_name=_('establishment'),
|
||||
# on_delete=models.CASCADE)
|
||||
# plate = models.ForeignKey(Plate, verbose_name=_('menu'), on_delete=models.CASCADE)
|
||||
#
|
||||
# class Meta:
|
||||
# verbose_name = _('menu')
|
||||
# verbose_name_plural = _('menu')
|
||||
|
||||
class CommentQuerySet(models.QuerySet):
|
||||
"""QuerySets for Comment model."""
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,28 @@ from rest_framework import serializers
|
|||
from establishment import models
|
||||
from location.serializers import AddressSerializer
|
||||
from main.serializers import MetaDataContentSerializer, AwardSerializer
|
||||
from review import models as review_models
|
||||
from timetable.models import Timetable
|
||||
|
||||
|
||||
class ContactPhonesSerializer(serializers.ModelSerializer):
|
||||
"""Contact phone serializer"""
|
||||
class Meta:
|
||||
model = models.ContactPhone
|
||||
fields = [
|
||||
'phone'
|
||||
]
|
||||
|
||||
|
||||
class ContactEmailsSerializer(serializers.ModelSerializer):
|
||||
"""Contact email serializer"""
|
||||
class Meta:
|
||||
model = models.ContactEmail
|
||||
fields = [
|
||||
'email'
|
||||
]
|
||||
|
||||
|
||||
class EstablishmentTypeSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for EstablishmentType model."""
|
||||
|
||||
|
|
@ -47,6 +66,16 @@ class EstablishmentScheduleSerializer(serializers.ModelSerializer):
|
|||
)
|
||||
|
||||
|
||||
class ReviewSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for model Review."""
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
model = review_models.Review
|
||||
fields = (
|
||||
'text',
|
||||
)
|
||||
|
||||
|
||||
class CommentSerializer(serializers.ModelSerializer):
|
||||
"""Comment serializer"""
|
||||
nickname = serializers.CharField(source='author.username')
|
||||
|
|
@ -76,6 +105,10 @@ class EstablishmentSerializer(serializers.ModelSerializer):
|
|||
schedule = EstablishmentScheduleSerializer(source='schedule.schedule',
|
||||
many=True,
|
||||
allow_null=True)
|
||||
phones = ContactPhonesSerializer(read_only=True, many=True, )
|
||||
emails = ContactEmailsSerializer(read_only=True, many=True, )
|
||||
reviews = ReviewSerializer(source='reviews.last',
|
||||
allow_null=True)
|
||||
comments = CommentSerializer(many=True,
|
||||
allow_null=True)
|
||||
|
||||
|
|
@ -98,5 +131,8 @@ class EstablishmentSerializer(serializers.ModelSerializer):
|
|||
'tags',
|
||||
'awards',
|
||||
'schedule',
|
||||
'phones',
|
||||
'emails',
|
||||
'reviews',
|
||||
'comments',
|
||||
)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,6 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='country',
|
||||
name='name',
|
||||
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=None, help_text='{"en":"some text"}', null=True, verbose_name='Text'),
|
||||
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='Text'),
|
||||
),
|
||||
]
|
||||
|
|
|
|||
19
apps/location/migrations/0009_auto_20190901_0831.py
Normal file
19
apps/location/migrations/0009_auto_20190901_0831.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 2.2.4 on 2019-09-01 08:31
|
||||
|
||||
import django.contrib.postgres.fields.jsonb
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('location', '0008_auto_20190827_1302'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='country',
|
||||
name='name',
|
||||
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='Name'),
|
||||
),
|
||||
]
|
||||
|
|
@ -17,7 +17,7 @@ class Country(SVGImageMixin, ProjectBaseMixin):
|
|||
"""Country model."""
|
||||
|
||||
name = JSONField(null=True, blank=True, default=None,
|
||||
verbose_name=_('Name'), help_text='{"en":"some text"}')
|
||||
verbose_name=_('Name'), help_text='{"en-GB":"some text"}')
|
||||
code = models.CharField(max_length=255, unique=True, verbose_name=_('Code'))
|
||||
low_price = models.IntegerField(default=25, verbose_name=_('Low price'))
|
||||
high_price = models.IntegerField(default=50, verbose_name=_('High price'))
|
||||
|
|
|
|||
35
apps/main/migrations/0013_auto_20190901_1032.py
Normal file
35
apps/main/migrations/0013_auto_20190901_1032.py
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Generated by Django 2.2.4 on 2019-09-01 10:32
|
||||
|
||||
from django.db import migrations, models
|
||||
import utils.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('main', '0012_auto_20190829_1155'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Currency',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, verbose_name='name')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'currency',
|
||||
'verbose_name_plural': 'currencies',
|
||||
},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='award',
|
||||
name='title',
|
||||
field=utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='title'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='metadata',
|
||||
name='label',
|
||||
field=utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='label'),
|
||||
),
|
||||
]
|
||||
|
|
@ -190,7 +190,7 @@ class Award(TraslatedFieldsMixin, models.Model):
|
|||
award_type = models.ForeignKey('main.AwardType', on_delete=models.CASCADE)
|
||||
title = TJSONField(
|
||||
_('title'), null=True, blank=True,
|
||||
default=None, help_text='{"en":"some text"}')
|
||||
default=None, help_text='{"en-GB":"some text"}')
|
||||
vintage_year = models.CharField(_('vintage year'), max_length=255, default='')
|
||||
|
||||
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
|
||||
|
|
@ -228,7 +228,7 @@ class MetaData(TraslatedFieldsMixin, models.Model):
|
|||
"""MetaData model."""
|
||||
label = TJSONField(
|
||||
_('label'), null=True, blank=True,
|
||||
default=None, help_text='{"en":"some text"}')
|
||||
default=None, help_text='{"en-GB":"some text"}')
|
||||
category = models.ForeignKey(
|
||||
MetaDataCategory, verbose_name=_('category'), on_delete=models.CASCADE)
|
||||
|
||||
|
|
@ -250,3 +250,15 @@ class MetaDataContent(models.Model):
|
|||
object_id = models.PositiveIntegerField()
|
||||
content_object = generic.GenericForeignKey('content_type', 'object_id')
|
||||
metadata = models.ForeignKey(MetaData, on_delete=models.CASCADE)
|
||||
|
||||
|
||||
class Currency(models.Model):
|
||||
"""Currency model."""
|
||||
name = models.CharField(_('name'), max_length=50)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('currency')
|
||||
verbose_name_plural = _('currencies')
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.name}'
|
||||
|
|
|
|||
29
apps/news/migrations/0009_auto_20190901_1032.py
Normal file
29
apps/news/migrations/0009_auto_20190901_1032.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Generated by Django 2.2.4 on 2019-09-01 10:32
|
||||
|
||||
from django.db import migrations
|
||||
import utils.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('news', '0008_auto_20190828_1522'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='news',
|
||||
name='description',
|
||||
field=utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='description'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='news',
|
||||
name='subtitle',
|
||||
field=utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='subtitle'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='news',
|
||||
name='title',
|
||||
field=utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='title'),
|
||||
),
|
||||
]
|
||||
|
|
@ -43,14 +43,14 @@ class News(BaseAttributes, TraslatedFieldsMixin):
|
|||
|
||||
title = TJSONField(
|
||||
_('title'), null=True, blank=True,
|
||||
default=None, help_text='{"en":"some text"}')
|
||||
default=None, help_text='{"en-GB":"some text"}')
|
||||
subtitle = TJSONField(
|
||||
_('subtitle'), null=True, blank=True,
|
||||
default=None, help_text='{"en":"some text"}'
|
||||
default=None, help_text='{"en-GB":"some text"}'
|
||||
)
|
||||
description = TJSONField(
|
||||
_('description'), null=True, blank=True,
|
||||
default=None, help_text='{"en":"some text"}'
|
||||
default=None, help_text='{"en-GB":"some text"}'
|
||||
)
|
||||
start = models.DateTimeField(_('start'))
|
||||
end = models.DateTimeField(_('end'))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
"""News app common serializers."""
|
||||
from rest_framework import serializers
|
||||
|
||||
from gallery import models as gallery_models
|
||||
from location.models import Address
|
||||
from location.serializers import AddressSerializer
|
||||
|
|
@ -23,7 +23,7 @@ class NewsSerializer(serializers.ModelSerializer):
|
|||
title_translated = serializers.CharField(read_only=True, allow_null=True)
|
||||
subtitle_translated = serializers.CharField(read_only=True, allow_null=True)
|
||||
description_translated = serializers.CharField(read_only=True, allow_null=True)
|
||||
image_url = serializers.ImageField(source='image.image')
|
||||
image_url = serializers.ImageField(source='image.image', allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = models.News
|
||||
|
|
|
|||
|
|
@ -1,50 +1,37 @@
|
|||
"""News app common app."""
|
||||
from rest_framework import generics, permissions
|
||||
|
||||
from news import filters
|
||||
from news.models import News, NewsType
|
||||
from news import filters, models
|
||||
from news.serializers import common as serializers
|
||||
from utils.views import (JWTGenericViewMixin,
|
||||
JWTListAPIView)
|
||||
from utils.views import JWTGenericViewMixin, JWTListAPIView
|
||||
|
||||
|
||||
# Mixins
|
||||
class NewsViewMixin(JWTGenericViewMixin):
|
||||
"""View mixin for News model"""
|
||||
class NewsMixin:
|
||||
"""News mixin."""
|
||||
|
||||
def get_queryset(self, *args, **kwargs):
|
||||
"""Override get_queryset method"""
|
||||
return News.objects.annotate_localized_fields(locale=self.request.locale)
|
||||
|
||||
|
||||
class NewsListView(NewsViewMixin, JWTListAPIView):
|
||||
"""News list view."""
|
||||
permission_classes = (permissions.AllowAny, )
|
||||
serializer_class = serializers.NewsSerializer
|
||||
filter_class = filters.NewsListFilterSet
|
||||
|
||||
def get_queryset(self, *args, **kwargs):
|
||||
"""Override get_queryset method"""
|
||||
return News.objects.published()\
|
||||
.by_country_code(code=self.request.country_code)\
|
||||
return models.News.objects.published() \
|
||||
.by_country_code(code=self.request.country_code) \
|
||||
.order_by('-is_highlighted', '-created')
|
||||
|
||||
|
||||
# class NewsCreateView(generics.CreateAPIView):
|
||||
# """News list view."""
|
||||
# queryset = News.objects.all()
|
||||
# permission_classes = (permissions.IsAuthenticated, )
|
||||
# serializer_class = serializers.NewsCreateUpdateSerializer
|
||||
class NewsListView(NewsMixin, JWTListAPIView):
|
||||
"""News list view."""
|
||||
|
||||
filter_class = filters.NewsListFilterSet
|
||||
|
||||
|
||||
class NewsDetailView(NewsViewMixin, generics.RetrieveAPIView):
|
||||
class NewsDetailView(NewsMixin, JWTGenericViewMixin, generics.RetrieveAPIView):
|
||||
"""News detail view."""
|
||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly, )
|
||||
serializer_class = serializers.NewsSerializer
|
||||
|
||||
|
||||
class NewsTypeListView(generics.ListAPIView):
|
||||
"""NewsType list view."""
|
||||
|
||||
serializer_class = serializers.NewsTypeSerializer
|
||||
permission_classes = (permissions.AllowAny, )
|
||||
pagination_class = None
|
||||
queryset = NewsType.objects.all()
|
||||
queryset = models.NewsType.objects.all()
|
||||
|
|
|
|||
0
apps/notification/__init__.py
Normal file
0
apps/notification/__init__.py
Normal file
3
apps/notification/admin.py
Normal file
3
apps/notification/admin.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
7
apps/notification/apps.py
Normal file
7
apps/notification/apps.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
from django.apps import AppConfig
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class NotificationConfig(AppConfig):
|
||||
name = 'notification'
|
||||
verbose_name = _('notification')
|
||||
37
apps/notification/migrations/0001_initial.py
Normal file
37
apps/notification/migrations/0001_initial.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Generated by Django 2.2.4 on 2019-08-30 11:22
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Subscriber',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date created')),
|
||||
('modified', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||
('email', models.EmailField(blank=True, default=None, max_length=254, null=True, unique=True, verbose_name='Email')),
|
||||
('ip_address', models.GenericIPAddressField(blank=True, default=None, null=True, verbose_name='IP address')),
|
||||
('country_code', models.CharField(blank=True, default=None, max_length=10, null=True, verbose_name='Country code')),
|
||||
('locale', models.CharField(blank=True, default=None, max_length=10, null=True, verbose_name='Locale identifier')),
|
||||
('state', models.PositiveIntegerField(choices=[(0, 'Unusable'), (1, 'Usable')], default=1, verbose_name='State')),
|
||||
('update_code', models.CharField(blank=True, db_index=True, default=None, max_length=254, null=True, verbose_name='Token')),
|
||||
('user', models.OneToOneField(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='subscriber', to=settings.AUTH_USER_MODEL, verbose_name='User')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Subscriber',
|
||||
'verbose_name_plural': 'Subscribers',
|
||||
},
|
||||
),
|
||||
]
|
||||
0
apps/notification/migrations/__init__.py
Normal file
0
apps/notification/migrations/__init__.py
Normal file
124
apps/notification/models.py
Normal file
124
apps/notification/models.py
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
"""Notification app models."""
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from account.models import User
|
||||
from utils.methods import generate_string_code
|
||||
from utils.models import ProjectBaseMixin
|
||||
|
||||
|
||||
# todo: associate user & subscriber after users registration
|
||||
class SubscriberManager(models.Manager):
|
||||
"""Extended manager for Subscriber model."""
|
||||
|
||||
def make_subscriber(self, email=None, user=None, ip_address=None, country_code=None,
|
||||
locale=None, *args, **kwargs):
|
||||
"""Make subscriber and update info."""
|
||||
# search existing object
|
||||
if not user:
|
||||
user = User.objects.filter(email=email).first()
|
||||
if user:
|
||||
obj = self.model.objects.filter(models.Q(user=user) | models.Q(
|
||||
email=user.email)).first()
|
||||
else:
|
||||
obj = self.model.objects.filter(email=email).first()
|
||||
|
||||
# update or create
|
||||
if obj:
|
||||
if user:
|
||||
obj.user = user
|
||||
obj.email = None
|
||||
else:
|
||||
obj.email = email
|
||||
obj.ip_address = ip_address
|
||||
obj.country_code = country_code
|
||||
obj.locale = locale
|
||||
obj.state = self.model.USABLE
|
||||
obj.update_code = generate_string_code()
|
||||
obj.save()
|
||||
else:
|
||||
obj = self.model.objects.create(user=user, email=email, ip_address=ip_address,
|
||||
country_code=country_code, locale=locale)
|
||||
return obj
|
||||
|
||||
def associate_user(self, user):
|
||||
"""Associate user."""
|
||||
obj = self.model.objects.filter(user=user).first()
|
||||
if obj is None:
|
||||
obj = self.model.objects.filter(email=user.email_confirmed, user__isnull=True).first()
|
||||
if obj:
|
||||
obj.user = user
|
||||
obj.email = None
|
||||
obj.save()
|
||||
return obj
|
||||
|
||||
|
||||
class SubscriberQuerySet(models.QuerySet):
|
||||
"""Extended queryset for Subscriber model."""
|
||||
|
||||
def by_usable(self, switcher=True):
|
||||
if switcher:
|
||||
return self.filter(state=self.model.USABLE)
|
||||
else:
|
||||
return self.filter(state=self.model.UNUSABLE)
|
||||
|
||||
|
||||
class Subscriber(ProjectBaseMixin):
|
||||
"""Subscriber model."""
|
||||
|
||||
UNUSABLE = 0
|
||||
USABLE = 1
|
||||
|
||||
STATE_CHOICES = (
|
||||
(UNUSABLE, _('Unusable')),
|
||||
(USABLE, _('Usable')),
|
||||
)
|
||||
|
||||
user = models.OneToOneField(User, blank=True, null=True, default=None,
|
||||
on_delete=models.SET_NULL, related_name='subscriber',
|
||||
verbose_name=_('User'))
|
||||
email = models.EmailField(blank=True, null=True, default=None, unique=True,
|
||||
verbose_name=_('Email'))
|
||||
ip_address = models.GenericIPAddressField(blank=True, null=True, default=None,
|
||||
verbose_name=_('IP address'))
|
||||
country_code = models.CharField(max_length=10, blank=True, null=True, default=None,
|
||||
verbose_name=_('Country code'))
|
||||
locale = models.CharField(blank=True, null=True, default=None,
|
||||
max_length=10, verbose_name=_('Locale identifier'))
|
||||
state = models.PositiveIntegerField(choices=STATE_CHOICES, default=USABLE,
|
||||
verbose_name=_('State'))
|
||||
update_code = models.CharField(max_length=254, blank=True, null=True, default=None,
|
||||
db_index=True, verbose_name=_('Token'))
|
||||
|
||||
objects = SubscriberManager.from_queryset(SubscriberQuerySet)()
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
||||
verbose_name = _('Subscriber')
|
||||
verbose_name_plural = _('Subscribers')
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""Overrided save method."""
|
||||
if self.update_code is None:
|
||||
self.update_code = generate_string_code()
|
||||
return super(Subscriber, self).save(*args, **kwargs)
|
||||
|
||||
def unsubscribe(self):
|
||||
"""Unsubscribe user."""
|
||||
self.state = self.UNUSABLE
|
||||
self.save()
|
||||
|
||||
@property
|
||||
def send_to(self):
|
||||
"""Actual email."""
|
||||
return self.user.email if self.user else self.email
|
||||
|
||||
@property
|
||||
def link_to_unsubscribe(self):
|
||||
"""Link to unsubscribe."""
|
||||
schema = settings.SCHEMA_URI
|
||||
site_domain = settings.SITE_DOMAIN_URI
|
||||
url = settings.SITE_REDIRECT_URL_UNSUBSCRIBE
|
||||
query = f'?code={self.update_code}'
|
||||
return f'{schema}://{site_domain}{url}{query}'
|
||||
0
apps/notification/serializers/__init__.py
Normal file
0
apps/notification/serializers/__init__.py
Normal file
47
apps/notification/serializers/common.py
Normal file
47
apps/notification/serializers/common.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
"""Notification app serializers."""
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
from notification import models
|
||||
from utils.methods import get_user_ip
|
||||
|
||||
|
||||
class SubscribeSerializer(serializers.ModelSerializer):
|
||||
"""Subscribe serializer."""
|
||||
|
||||
email = serializers.EmailField(required=False, source='send_to')
|
||||
state_display = serializers.CharField(source='get_state_display', read_only=True)
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
||||
model = models.Subscriber
|
||||
fields = ('email', 'state', 'state_display')
|
||||
read_only_fields = ('state', 'state_display')
|
||||
|
||||
def validate(self, attrs):
|
||||
"""Validate attrs."""
|
||||
request = self.context.get('request')
|
||||
user = request.user
|
||||
|
||||
# validate email
|
||||
email = attrs.get('send_to')
|
||||
if user.is_authenticated:
|
||||
if email is not None and email != user.email:
|
||||
raise serializers.ValidationError(_('Does not match user email'))
|
||||
else:
|
||||
if email is None:
|
||||
raise serializers.ValidationError({'email': _('This field is required.')})
|
||||
|
||||
# append info
|
||||
attrs['email'] = email
|
||||
attrs['country_code'] = request.country_code
|
||||
attrs['locale'] = request.locale
|
||||
attrs['ip_address'] = get_user_ip(request)
|
||||
if user.is_authenticated:
|
||||
attrs['user'] = user
|
||||
return attrs
|
||||
|
||||
def create(self, validated_data):
|
||||
"""Create obj."""
|
||||
obj = models.Subscriber.objects.make_subscriber(**validated_data)
|
||||
return obj
|
||||
0
apps/notification/urls/__init__.py
Normal file
0
apps/notification/urls/__init__.py
Normal file
12
apps/notification/urls/common.py
Normal file
12
apps/notification/urls/common.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
"""Notification app common urlconf."""
|
||||
from django.urls import path
|
||||
from notification.views import common
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('subscribe/', common.SubscribeView.as_view(), name='subscribe'),
|
||||
path('subscribe-info/', common.SubscribeInfoAuthUserView.as_view(), name='check-code-auth'),
|
||||
path('subscribe-info/<code>/', common.SubscribeInfoView.as_view(), name='check-code'),
|
||||
path('unsubscribe/', common.UnsubscribeAuthUserView.as_view(), name='unsubscribe-auth'),
|
||||
path('unsubscribe/<code>/', common.UnsubscribeView.as_view(), name='unsubscribe'),
|
||||
]
|
||||
7
apps/notification/urls/web.py
Normal file
7
apps/notification/urls/web.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"""Establishment app web urlconf."""
|
||||
from notification.urls.common import urlpatterns as common_urlpatterns
|
||||
|
||||
|
||||
urlpatterns = []
|
||||
|
||||
urlpatterns.extend(common_urlpatterns)
|
||||
0
apps/notification/views/__init__.py
Normal file
0
apps/notification/views/__init__.py
Normal file
78
apps/notification/views/common.py
Normal file
78
apps/notification/views/common.py
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
"""Notification app common views."""
|
||||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework import generics, permissions
|
||||
from rest_framework.response import Response
|
||||
from notification import models
|
||||
from notification.serializers import common as serializers
|
||||
|
||||
|
||||
class SubscribeView(generics.GenericAPIView):
|
||||
"""Subscribe View."""
|
||||
|
||||
queryset = models.Subscriber.objects.all()
|
||||
permission_classes = (permissions.AllowAny, )
|
||||
serializer_class = serializers.SubscribeSerializer
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
return Response(data=serializer.data)
|
||||
|
||||
|
||||
class SubscribeInfoView(generics.RetrieveAPIView):
|
||||
"""Subscribe info view."""
|
||||
|
||||
lookup_field = 'update_code'
|
||||
lookup_url_kwarg = 'code'
|
||||
permission_classes = (permissions.AllowAny, )
|
||||
queryset = models.Subscriber.objects.all()
|
||||
serializer_class = serializers.SubscribeSerializer
|
||||
|
||||
|
||||
class SubscribeInfoAuthUserView(generics.RetrieveAPIView):
|
||||
"""Subscribe info auth user view."""
|
||||
|
||||
permission_classes = (permissions.IsAuthenticated, )
|
||||
queryset = models.Subscriber.objects.all()
|
||||
serializer_class = serializers.SubscribeSerializer
|
||||
|
||||
def get_object(self):
|
||||
user = self.request.user
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
filter_kwargs = {'user': user}
|
||||
obj = get_object_or_404(queryset, **filter_kwargs)
|
||||
self.check_object_permissions(self.request, obj)
|
||||
return obj
|
||||
|
||||
|
||||
class UnsubscribeView(generics.GenericAPIView):
|
||||
"""Unsubscribe view."""
|
||||
|
||||
lookup_field = 'update_code'
|
||||
lookup_url_kwarg = 'code'
|
||||
permission_classes = (permissions.AllowAny, )
|
||||
queryset = models.Subscriber.objects.all()
|
||||
serializer_class = serializers.SubscribeSerializer
|
||||
|
||||
def patch(self, request, *args, **kw):
|
||||
obj = self.get_object()
|
||||
obj.unsubscribe()
|
||||
serializer = self.get_serializer(instance=obj)
|
||||
return Response(data=serializer.data)
|
||||
|
||||
|
||||
class UnsubscribeAuthUserView(generics.GenericAPIView):
|
||||
"""Unsubscribe auth user view."""
|
||||
|
||||
permission_classes = (permissions.IsAuthenticated, )
|
||||
queryset = models.Subscriber.objects.all()
|
||||
serializer_class = serializers.SubscribeSerializer
|
||||
|
||||
def patch(self, request, *args, **kw):
|
||||
user = request.user
|
||||
obj = get_object_or_404(models.Subscriber, user=user)
|
||||
obj.unsubscribe()
|
||||
serializer = self.get_serializer(instance=obj)
|
||||
return Response(data=serializer.data)
|
||||
|
||||
|
|
@ -1,7 +1,3 @@
|
|||
from django.contrib import admin
|
||||
from review import models
|
||||
|
||||
|
||||
@admin.register(models.Review)
|
||||
class ReviewAdminModel(admin.ModelAdmin):
|
||||
"""Admin model for model Review."""
|
||||
# @admin.register(models.Review)
|
||||
# class ReviewAdminModel(admin.ModelAdmin):
|
||||
# """Admin model for model Review."""
|
||||
|
|
|
|||
43
apps/review/migrations/0001_initial.py
Normal file
43
apps/review/migrations/0001_initial.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# Generated by Django 2.2.4 on 2019-09-01 09:32
|
||||
|
||||
import django.core.validators
|
||||
import django.utils.timezone
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
('translation', '0002_siteinterfacedictionary'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Review',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date created')),
|
||||
('modified', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||
('object_id', models.PositiveIntegerField()),
|
||||
('text', models.TextField(verbose_name='Text')),
|
||||
('status', models.PositiveSmallIntegerField(choices=[(0, 'To investigate'), (1, 'To review'), (2, 'Ready')], default=0)),
|
||||
('published_at', models.DateTimeField(blank=True, default=None, help_text='Review published datetime', null=True, verbose_name='Publish datetime')),
|
||||
('vintage', models.IntegerField(validators=[django.core.validators.MinValueValidator(1900), django.core.validators.MaxValueValidator(2100)], verbose_name='Year of review')),
|
||||
('child', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='review.Review', verbose_name='Child review')),
|
||||
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
|
||||
('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='review_records_created', to=settings.AUTH_USER_MODEL, verbose_name='created by')),
|
||||
('language', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reviews', to='translation.Language', verbose_name='Review language')),
|
||||
('modified_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='review_records_modified', to=settings.AUTH_USER_MODEL, verbose_name='modified by')),
|
||||
('reviewer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reviews', to=settings.AUTH_USER_MODEL, verbose_name='Reviewer')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Review',
|
||||
'verbose_name_plural': 'Reviews',
|
||||
},
|
||||
),
|
||||
]
|
||||
19
apps/translation/migrations/0003_auto_20190901_1032.py
Normal file
19
apps/translation/migrations/0003_auto_20190901_1032.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 2.2.4 on 2019-09-01 10:32
|
||||
|
||||
import django.contrib.postgres.fields.jsonb
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('translation', '0002_siteinterfacedictionary'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='siteinterfacedictionary',
|
||||
name='text',
|
||||
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='Text'),
|
||||
),
|
||||
]
|
||||
|
|
@ -49,7 +49,7 @@ class SiteInterfaceDictionary(ProjectBaseMixin):
|
|||
verbose_name=_('Page'))
|
||||
keywords = models.CharField(max_length=255, verbose_name='Keywords')
|
||||
text = JSONField(_('Text'), null=True, blank=True,
|
||||
default=None, help_text='{"en":"some text"}')
|
||||
default=None, help_text='{"en-GB":"some text"}')
|
||||
|
||||
objects = SiteInterfaceDictionaryManager()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"""Utils app method."""
|
||||
import random
|
||||
import re
|
||||
import string
|
||||
|
||||
from django.conf import settings
|
||||
from django.http.request import HttpRequest
|
||||
|
|
@ -60,3 +61,20 @@ def svg_image_path(instance, filename):
|
|||
instance._meta.model_name,
|
||||
datetime.now().strftime(settings.REST_DATE_FORMAT),
|
||||
filename)
|
||||
|
||||
|
||||
def get_user_ip(request):
|
||||
"""Get user ip."""
|
||||
meta_dict = request.META
|
||||
x_forwarded_for = meta_dict.get('HTTP_X_FORWARDED_FOR')
|
||||
if x_forwarded_for:
|
||||
ip = x_forwarded_for.split(',')[0]
|
||||
else:
|
||||
ip = meta_dict.get('REMOTE_ADDR')
|
||||
return ip
|
||||
|
||||
|
||||
def generate_string_code(size=64,
|
||||
chars=string.ascii_lowercase + string.ascii_uppercase + string.digits):
|
||||
"""Generate string code."""
|
||||
return ''.join([random.SystemRandom().choice(chars) for _ in range(size)])
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ PROJECT_APPS = [
|
|||
'location.apps.LocationConfig',
|
||||
'main.apps.MainConfig',
|
||||
'news.apps.NewsConfig',
|
||||
'notification.apps.NotificationConfig',
|
||||
'partner.apps.PartnerConfig',
|
||||
'translation.apps.TranslationConfig',
|
||||
'configuration.apps.ConfigurationConfig',
|
||||
|
|
@ -82,6 +83,7 @@ EXTERNAL_APPS = [
|
|||
'django_extensions',
|
||||
'rest_framework_simplejwt.token_blacklist',
|
||||
'solo',
|
||||
'phonenumber_field',
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -302,7 +304,6 @@ CELERY_ACCEPT_CONTENT = ['application/json']
|
|||
CELERY_TASK_SERIALIZER = 'json'
|
||||
CELERY_RESULT_SERIALIZER = 'json'
|
||||
CELERY_TIMEZONE = TIME_ZONE
|
||||
USE_CELERY = False
|
||||
|
||||
# Django FCM (Firebase push notificatoins)
|
||||
FCM_DJANGO_SETTINGS = {
|
||||
|
|
@ -388,5 +389,7 @@ FILE_UPLOAD_PERMISSIONS = 0o644
|
|||
|
||||
SOLO_CACHE_TIMEOUT = 300
|
||||
|
||||
# REDIRECT URL
|
||||
SITE_REDIRECT_URL_UNSUBSCRIBE = '/unsubscribe/'
|
||||
|
||||
SITE_NAME = 'Gault & Millau'
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ ALLOWED_HOSTS = ['gm.id-east.ru', '95.213.204.126']
|
|||
|
||||
SEND_SMS = False
|
||||
SMS_CODE_SHOW = True
|
||||
USE_CELERY = False
|
||||
|
||||
SCHEMA_URI = 'http'
|
||||
DEFAULT_SUBDOMAIN = 'www'
|
||||
|
|
|
|||
|
|
@ -12,11 +12,6 @@ DEFAULT_SUBDOMAIN = 'www'
|
|||
SITE_DOMAIN_URI = 'testserver.com:8000'
|
||||
DOMAIN_URI = '0.0.0.0:8000'
|
||||
|
||||
# OTHER SETTINGS
|
||||
API_HOST = '0.0.0.0:8000'
|
||||
API_HOST_URL = 'http://%s' % API_HOST
|
||||
|
||||
|
||||
# CELERY
|
||||
BROKER_URL = 'amqp://rabbitmq:5672'
|
||||
CELERY_RESULT_BACKEND = BROKER_URL
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ urlpatterns = [
|
|||
path('collection/', include('collection.urls.web')),
|
||||
path('establishments/', include('establishment.urls.web')),
|
||||
path('news/', include('news.urls.web')),
|
||||
path('notifications/', include('notification.urls.web')),
|
||||
path('partner/', include('partner.urls.web')),
|
||||
path('location/', include('location.urls')),
|
||||
path('main/', include('main.urls')),
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ djangorestframework-xml
|
|||
celery
|
||||
amqp>=2.4.0
|
||||
geoip2==2.9.0
|
||||
django-phonenumber-field[phonenumbers]==2.1.0
|
||||
|
||||
# auth socials
|
||||
djangorestframework-oauth
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user