Site settings and determine user location over ip

This commit is contained in:
evgeniy-st 2019-08-22 14:23:02 +03:00
parent 94fe15af35
commit e839094e28
20 changed files with 353 additions and 100 deletions

View File

@ -0,0 +1,17 @@
# Generated by Django 2.2.4 on 2019-08-21 13:37
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('location', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='country',
name='name',
),
]

View File

@ -0,0 +1,35 @@
# Generated by Django 2.2.4 on 2019-08-21 14:07
import django.contrib.postgres.fields.jsonb
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('location', '0002_remove_country_name'),
]
operations = [
migrations.AddField(
model_name='country',
name='code',
field=models.CharField(blank=True, default=None, max_length=255, null=True, unique=True, verbose_name='Code'),
),
migrations.AddField(
model_name='country',
name='created',
field=models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date created'),
),
migrations.AddField(
model_name='country',
name='modified',
field=models.DateTimeField(auto_now=True, verbose_name='Date updated'),
),
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'),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 2.2.4 on 2019-08-22 07:10
import django.contrib.postgres.fields.jsonb
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('location', '0003_auto_20190821_1407'),
]
operations = [
migrations.AlterModelOptions(
name='country',
options={'verbose_name': 'Country', 'verbose_name_plural': 'Countries'},
),
migrations.AlterField(
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='Name'),
),
]

View File

@ -1,21 +1,37 @@
"""Location app models."""
from django.contrib.postgres.fields import JSONField
from django.contrib.gis.db import models
from django.utils.translation import gettext_lazy as _
from utils.models import ProjectBaseMixin, LocaleManagerMixin
class Country(models.Model):
class CountryManager(LocaleManagerMixin):
"""Extended manager for Country model."""
class Country(ProjectBaseMixin):
"""Country model."""
name = models.CharField(_('name'), max_length=250)
name = JSONField(null=True, blank=True, default=None,
verbose_name=_('Name'), help_text='{"en":"some text"}')
code = models.CharField(max_length=255, blank=True, null=True,
default=None, unique=True, verbose_name=_('Code'))
objects = CountryManager()
class Meta:
verbose_name_plural = _('countries')
verbose_name = _('country')
"""Meta class."""
verbose_name_plural = _('Countries')
verbose_name = _('Country')
def __str__(self):
return self.name
return self.code
class Region(models.Model):
"""Region model."""
name = models.CharField(_('name'), max_length=250)
code = models.CharField(_('code'), max_length=250)
parent_region = models.ForeignKey(
@ -25,6 +41,8 @@ class Region(models.Model):
Country, verbose_name=_('country'), on_delete=models.CASCADE)
class Meta:
"""Meta class."""
verbose_name_plural = _('regions')
verbose_name = _('region')
@ -34,6 +52,7 @@ class Region(models.Model):
class City(models.Model):
"""Region model."""
name = models.CharField(_('name'), max_length=250)
code = models.CharField(_('code'), max_length=250)
region = models.ForeignKey(

View File

@ -5,16 +5,21 @@ from django.contrib.gis.geos import Point
class CountrySerializer(serializers.ModelSerializer):
"""Country serializer."""
name_trans = serializers.CharField(read_only=True, allow_null=True)
class Meta:
model = models.Country
fields = [
'id',
'name'
'code',
'name_trans',
]
class RegionSerializer(serializers.ModelSerializer):
"""Region serializer"""
country = CountrySerializer()
class Meta:

View File

@ -6,11 +6,9 @@ from location import views
app_name = 'location'
urlpatterns = [
path('country/', views.CountryListView.as_view(), name='country_list'),
path('country/create/', views.CountryCreateView.as_view(), name='country_create'),
path('country/<int:pk>/', views.CountryRetrieveView.as_view(), name='country_retrieve'),
path('country/<int:pk>/delete/', views.CountryDestroyView.as_view(), name='country_destroy'),
path('country/<int:pk>/update/', views.CountryUpdateView.as_view(), name='country_update'),
path('country/', views.CountryListView.as_view(), name='country-list'),
path('country/<int:pk>/', views.CountryRetrieveView.as_view(),
name='country-retrieve'),
path('region/', views.RegionListView.as_view(), name='region_list'),
path('region/create/', views.RegionCreateView.as_view(), name='region_create'),

View File

@ -1,14 +1,20 @@
"""Location app views."""
from rest_framework import generics
from rest_framework import permissions
from location import models, serializers
from utils.views import JWTGenericViewMixin
# Mixins
class CountryViewMixin(generics.GenericAPIView):
class CountryViewMixin(JWTGenericViewMixin, generics.GenericAPIView):
"""View Mixin for model Country"""
model = models.Country
queryset = models.Country.objects.all()
serializer_class = serializers.CountrySerializer
permission_classes = (permissions.AllowAny, )
def get_queryset(self):
return models.Country.objects.annotate_localized_fields(
locale=self._get_locale(request=self.request))
class RegionViewMixin(generics.GenericAPIView):
@ -30,30 +36,14 @@ class AddressViewMixin(generics.GenericAPIView):
# Country
class CountryCreateView(CountryViewMixin, generics.CreateAPIView):
"""Create view for model Country"""
serializer_class = serializers.CountrySerializer
class CountryListView(CountryViewMixin, generics.ListAPIView):
"""List view for model Country."""
pagination_class = None
class CountryRetrieveView(CountryViewMixin, generics.RetrieveAPIView):
"""Retrieve view for model Country"""
serializer_class = serializers.CountrySerializer
class CountryListView(CountryViewMixin, generics.ListAPIView):
"""List view for model Country"""
permission_classes = (permissions.AllowAny, )
serializer_class = serializers.CountrySerializer
class CountryDestroyView(CountryViewMixin, generics.DestroyAPIView):
"""Destroy view for model Country"""
serializer_class = serializers.CountrySerializer
class CountryUpdateView(CountryViewMixin, generics.UpdateAPIView):
"""Update view for model Country"""
serializer_class = serializers.CountrySerializer
"""Retrieve view for model Country."""
# Region

61
apps/main/methods.py Normal file
View File

@ -0,0 +1,61 @@
"""Main app methods."""
import logging
from django.conf import settings
from django.contrib.gis.geoip2 import GeoIP2, GeoIP2Exception
from main import models
logger = logging.getLogger(__name__)
def site_url(schema, subdomain, domain):
return f'{schema}://{subdomain}.{domain}'
def get_user_ip(request):
"""Get user ip from request."""
META = request.META
x_forwarded_for = META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = META.get('REMOTE_ADDR')
return ip
def determine_country_code(ip_addr):
"""Determine country code."""
country_code = None
if ip_addr:
try:
geoip = GeoIP2()
country_code = geoip.country_code(ip_addr)
country_code = country_code.lower()
except GeoIP2Exception as ex:
logger.error(f'GEOIP Exception: {ex}')
except Exception as ex:
logger.error(f'GEOIP Base exception: {ex}')
return country_code
def determine_user_site_url(country_code):
"""Determine user's site url."""
try:
if country_code is None:
raise ValueError
# search localized site
site = models.SiteSettings.objects.filter(
country__code=country_code).first()
# search default site
if not site:
site = models.SiteSettings.objects.filter(default_site=True).first()
if not site:
raise ValueError
except Exception:
return site_url(schema=settings.SCHEMA_URI,
subdomain=settings.DEFAULT_SUBDOMAIN,
domain=settings.DOMAIN_URI)
else:
return site.site_url

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.4 on 2019-08-22 07:26
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('main', '0002_auto_20190821_0830'),
]
operations = [
migrations.RenameField(
model_name='sitesettings',
old_name='country_code',
new_name='subdomain',
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.4 on 2019-08-22 07:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('main', '0003_auto_20190822_0726'),
]
operations = [
migrations.AlterField(
model_name='sitesettings',
name='subdomain',
field=models.CharField(db_index=True, max_length=255, verbose_name='Subdomain'),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 2.2.4 on 2019-08-22 07:27
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('location', '0004_auto_20190822_0710'),
('main', '0004_auto_20190822_0726'),
]
operations = [
migrations.AddField(
model_name='sitesettings',
name='country',
field=models.OneToOneField(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='location.Country', verbose_name='Country'),
),
migrations.AddField(
model_name='sitesettings',
name='default_site',
field=models.BooleanField(default=False, verbose_name='Default site'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.4 on 2019-08-22 11:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('main', '0005_auto_20190822_0727'),
]
operations = [
migrations.AlterField(
model_name='sitesettings',
name='subdomain',
field=models.CharField(db_index=True, max_length=255, unique=True, verbose_name='Subdomain'),
),
]

View File

@ -1,7 +1,10 @@
"""Main app models."""
from django.conf import settings
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.contrib.postgres.fields import JSONField
from main import methods
from location.models import Country
from utils.models import ProjectBaseMixin
#
#
@ -92,8 +95,13 @@ from utils.models import ProjectBaseMixin
class SiteSettings(ProjectBaseMixin):
# todo: mb foreign key?
country_code = models.CharField(max_length=255, db_index=True,
verbose_name=_('Country code'))
subdomain = models.CharField(max_length=255, db_index=True, unique=True,
verbose_name=_('Subdomain'))
country = models.OneToOneField(Country, on_delete=models.PROTECT,
null=True, blank=True, default=None,
verbose_name=_('Country'))
default_site = models.BooleanField(default=False,
verbose_name=_('Default site'))
pinterest_page_url = models.URLField(blank=True, null=True, default=None,
verbose_name=_('Pinterest page URL'))
twitter_page_url = models.URLField(blank=True, null=True, default=None,
@ -116,13 +124,19 @@ class SiteSettings(ProjectBaseMixin):
verbose_name_plural = _('Site settings')
def __str__(self):
return f'Site: "{self.country_code}"'
return f'ID: "{self.id}". Site: "{self.site_url}"'
@property
def published_features(self):
return self.feature_set.filter(sitefeature__site_settings=self,
sitefeature__published=True)
@property
def site_url(self):
return methods.site_url(schema=settings.SCHEMA_URI,
subdomain=self.subdomain,
domain=settings.DOMAIN_URI)
class Feature(ProjectBaseMixin):
"""Feature model."""

View File

@ -21,12 +21,16 @@ class SiteSettingsSerializer(serializers.ModelSerializer):
published_features = FeatureSerializer(many=True, allow_null=None)
#todo: remove this
country_code = serializers.CharField(source='subdomain', read_only=True)
class Meta:
"""Meta class."""
model = models.SiteSettings
fields = (
'country_code',
'subdomain',
'pinterest_page_url',
'twitter_page_url',
'facebook_page_url',

View File

@ -7,14 +7,14 @@ app = 'main'
urlpatterns = [
path('determine-site/', views.DetermineSiteView.as_view(),
name='determine-site'),
path('site-settings/<country_code>/', views.SiteSettingsView.as_view(),
path('site-settings/<subdomain>/', views.SiteSettingsView.as_view(),
name='site-settings', ),
path('features/', views.FeaturesLCView.as_view(),
name='features-lc'),
path('features/<int:pk>/', views.FeaturesRUDView.as_view(),
name='features-rud'),
path('site-features/', views.SiteFeaturesLCView.as_view(),
name='site-features-lc'),
path('site-features/<int:pk>/', views.SiteFeaturesRUDView.as_view(),
name='site-features-rud'),
# path('features/', views.FeaturesLCView.as_view(),
# name='features-lc'),
# path('features/<int:pk>/', views.FeaturesRUDView.as_view(),
# name='features-rud'),
# path('site-features/', views.SiteFeaturesLCView.as_view(),
# name='site-features-lc'),
# path('site-features/<int:pk>/', views.SiteFeaturesRUDView.as_view(),
# name='site-features-rud'),
]

View File

@ -1,53 +1,7 @@
"""Main app views."""
from django.http.response import HttpResponseRedirect
from rest_framework import generics, permissions
from main import models, serializers
class SiteSettingsView(generics.RetrieveAPIView):
"""Site settings View."""
lookup_field = 'country_code'
permission_classes = (permissions.AllowAny,)
queryset = models.SiteSettings.objects.all()
serializer_class = serializers.SiteSettingsSerializer
class FeatureViewMixin:
"""Feature view mixin."""
queryset = models.Feature.objects.all()
serializer_class = serializers.FeatureSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
class FeaturesLCView(FeatureViewMixin, generics.ListCreateAPIView):
"""Site features LC View."""
pagination_class = None
class FeaturesRUDView(FeatureViewMixin, generics.RetrieveUpdateDestroyAPIView):
"""Site features RUD View."""
class SiteFeaturesViewMixin:
"""Site feature view mixin."""
queryset = models.SiteFeature.objects.all()
serializer_class = serializers.SiteFeatureSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
class SiteFeaturesLCView(SiteFeaturesViewMixin, generics.ListCreateAPIView):
"""Site features LC."""
pagination_class = None
class SiteFeaturesRUDView(SiteFeaturesViewMixin,
generics.RetrieveUpdateDestroyAPIView):
"""Site features RUD."""
from main import methods, models, serializers
class DetermineSiteView(generics.GenericAPIView):
@ -56,4 +10,53 @@ class DetermineSiteView(generics.GenericAPIView):
permission_classes = (permissions.AllowAny,)
def get(self, request, *args, **kwargs):
return HttpResponseRedirect('http://ru.gm.id-east.ru/')
user_ip = methods.get_user_ip(request)
country_code = methods.determine_country_code(user_ip)
url = methods.determine_user_site_url(country_code)
return HttpResponseRedirect(url)
class SiteSettingsView(generics.RetrieveAPIView):
"""Site settings View."""
lookup_field = 'subdomain'
permission_classes = (permissions.AllowAny,)
queryset = models.SiteSettings.objects.all()
serializer_class = serializers.SiteSettingsSerializer
#
#
# class FeatureViewMixin:
# """Feature view mixin."""
#
# queryset = models.Feature.objects.all()
# serializer_class = serializers.FeatureSerializer
# permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
#
#
# class FeaturesLCView(FeatureViewMixin, generics.ListCreateAPIView):
# """Site features LC View."""
#
# pagination_class = None
#
#
# class FeaturesRUDView(FeatureViewMixin, generics.RetrieveUpdateDestroyAPIView):
# """Site features RUD View."""
#
#
# class SiteFeaturesViewMixin:
# """Site feature view mixin."""
#
# queryset = models.SiteFeature.objects.all()
# serializer_class = serializers.SiteFeatureSerializer
# permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
#
#
# class SiteFeaturesLCView(SiteFeaturesViewMixin, generics.ListCreateAPIView):
# """Site features LC."""
#
# pagination_class = None
#
#
# class SiteFeaturesRUDView(SiteFeaturesViewMixin,
# generics.RetrieveUpdateDestroyAPIView):
# """Site features RUD."""

View File

@ -33,6 +33,7 @@ class SiteInterfaceDictionaryMixin:
return models.SiteInterfaceDictionary.objects.annotate_localized_fields(
locale=locale)
class SiteInterfaceDictionaryView(JWTGenericViewMixin,
SiteInterfaceDictionaryMixin,
generics.ListCreateAPIView):

View File

@ -1,10 +1,10 @@
"""Utils app method."""
import random
from django.http.request import HttpRequest
from rest_framework.request import Request
import re
from django.utils.timezone import datetime
from django.conf import settings
from django.http.request import HttpRequest
from django.utils.timezone import datetime
from rest_framework.request import Request
def generate_code(digits=6, string_output=True):

View File

@ -6,4 +6,6 @@ ALLOWED_HOSTS = ['gm.id-east.ru', ]
SEND_SMS = False
SMS_CODE_SHOW = True
SCHEMA_URI = 'http'
DEFAULT_SUBDOMAIN = 'www'
DOMAIN_URI = 'gm.id-east.ru'

View File

@ -7,7 +7,9 @@ SEND_SMS = False
SMS_CODE_SHOW = True
USE_CELERY = False
DOMAIN_URI = '0.0.0.0:8000'
SCHEMA_URI = 'http'
DEFAULT_SUBDOMAIN = 'www'
DOMAIN_URI = 'testserver.com:8000'
# OTHER SETTINGS
API_HOST = '0.0.0.0:8000'