added wine origins

This commit is contained in:
Anatoly 2019-12-04 20:13:09 +03:00
parent 627f7562ec
commit 29ff10fb4b
14 changed files with 294 additions and 27 deletions

View File

@ -136,3 +136,4 @@ class SocialNetworkAdmin(BaseModelAdminMixin, admin.ModelAdmin):
class CompanyAdmin(BaseModelAdminMixin, admin.ModelAdmin):
"""Admin conf for Company model."""
raw_id_fields = ['establishment', 'address', ]

View File

@ -0,0 +1,30 @@
from django.core.management.base import BaseCommand
from location.models import WineOriginAddress, EstablishmentWineOriginAddress
from product.models import Product
class Command(BaseCommand):
help = 'Add to establishment wine origin object.'
def handle(self, *args, **kwarg):
create_counter = 0
for product in Product.objects.exclude(establishment__isnull=True):
establishment = product.establishment
if product.wine_origins.exists():
for wine_origin in product.wine_origins.all():
wine_region = wine_origin.wine_region
wine_sub_region = wine_origin.wine_sub_region
if not EstablishmentWineOriginAddress.objects.filter(establishment=establishment,
wine_region=wine_region,
wine_sub_region=wine_sub_region) \
.exists():
EstablishmentWineOriginAddress.objects.create(
establishment=establishment,
wine_region=wine_origin.wine_region,
wine_sub_region=wine_origin.wine_sub_region,
)
create_counter += 1
self.stdout.write(self.style.WARNING(f'COUNT CREATED OBJECTS: {create_counter}'))

View File

@ -1,8 +1,8 @@
"""Establishment models."""
from datetime import datetime
from functools import reduce
from typing import List
from operator import or_
from typing import List
import elasticsearch_dsl
from django.conf import settings
@ -22,6 +22,7 @@ from timezone_field import TimeZoneField
from collection.models import Collection
from location.models import Address
from location.models import WineOriginAddressMixin
from main.models import Award, Currency
from review.models import Review
from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin,
@ -590,6 +591,10 @@ class Establishment(GalleryModelMixin, ProjectBaseMixin, URLImageMixin,
if qs.exists():
return qs.first().image
@property
def wine_origins_unique(self):
return self.wine_origins.distinct('wine_region')
class EstablishmentNoteQuerySet(models.QuerySet):
"""QuerySet for model EstablishmentNote."""

View File

@ -16,6 +16,8 @@ from utils import exceptions as utils_exceptions
from utils.serializers import ImageBaseSerializer, CarouselCreateSerializer
from utils.serializers import (ProjectModelSerializer, TranslatedField,
FavoritesCreateSerializer)
from location.serializers import EstablishmentWineRegionBaseSerializer, \
EstablishmentWineOriginBaseSerializer
class ContactPhonesSerializer(serializers.ModelSerializer):
@ -283,6 +285,8 @@ class EstablishmentBaseSerializer(ProjectModelSerializer):
type = EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True)
subtypes = EstablishmentSubTypeBaseSerializer(many=True, source='establishment_subtypes')
image = serializers.URLField(source='image_url', read_only=True)
wine_regions = EstablishmentWineRegionBaseSerializer(many=True, source='wine_origins_unique',
read_only=True, allow_null=True)
preview_image = serializers.URLField(source='preview_image_url',
allow_null=True,
read_only=True)
@ -311,6 +315,7 @@ class EstablishmentBaseSerializer(ProjectModelSerializer):
'image',
'preview_image',
'new_image',
'wine_regions',
]
@ -365,6 +370,7 @@ class EstablishmentDetailSerializer(EstablishmentBaseSerializer):
range_price_carte = RangePriceSerializer(read_only=True)
vintage_year = serializers.ReadOnlyField()
gallery = ImageBaseSerializer(read_only=True, source='crop_gallery', many=True)
wine_origins = EstablishmentWineOriginBaseSerializer(many=True, read_only=True)
class Meta(EstablishmentBaseSerializer.Meta):
"""Meta class."""
@ -391,6 +397,7 @@ class EstablishmentDetailSerializer(EstablishmentBaseSerializer):
'transportation',
'vintage_year',
'gallery',
'wine_origins',
]

View File

@ -45,3 +45,15 @@ class AddressAdmin(admin.OSMGeoAdmin):
def geo_lat(self, item):
if isinstance(item.coordinates, Point):
return item.coordinates.y
@admin.register(models.EstablishmentWineOriginAddress)
class EstablishmentWineOriginAddress(admin.ModelAdmin):
"""Admin model for EstablishmentWineOriginAddress."""
raw_id_fields = ['establishment', ]
@admin.register(models.WineOriginAddress)
class WineOriginAddress(admin.ModelAdmin):
"""Admin page for model WineOriginAddress."""
raw_id_fields = ['product', ]

View File

@ -0,0 +1,42 @@
# Generated by Django 2.2.7 on 2019-12-04 14:20
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('establishment', '0067_auto_20191122_1244'),
('product', '0019_auto_20191204_1420'),
('location', '0030_auto_20191120_1010'),
]
operations = [
migrations.CreateModel(
name='WineOriginAddress',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='wine_origins', to='product.Product', verbose_name='product')),
('wine_region', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='location.WineRegion', verbose_name='wine region')),
('wine_sub_region', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='location.WineSubRegion', verbose_name='wine sub region')),
],
options={
'verbose_name': 'wine origin address',
'verbose_name_plural': 'wine origin addresses',
},
),
migrations.CreateModel(
name='EstablishmentWineOriginAddress',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('establishment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='wine_origins', to='establishment.Establishment', verbose_name='product')),
('wine_region', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='location.WineRegion', verbose_name='wine region')),
('wine_sub_region', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='location.WineSubRegion', verbose_name='wine sub region')),
],
options={
'verbose_name': 'establishment wine origin address',
'verbose_name_plural': 'establishment wine origin addresses',
},
),
]

View File

@ -192,9 +192,9 @@ class WineRegionQuerySet(models.QuerySet):
def with_sub_region_related(self):
return self.prefetch_related('wine_sub_region')
def having_wines(self, value = True):
def having_wines(self, value=True):
"""Return qs with regions, which have any wine related to them"""
return self.exclude(wines__isnull=value)
return self.exclude(wineoriginaddress__product__isnull=value)
class WineRegion(TranslatedFieldsMixin, models.Model):
@ -278,6 +278,55 @@ class WineVillage(models.Model):
return self.name
class WineOriginAddressMixin(models.Model):
"""Model for wine origin address."""
wine_region = models.ForeignKey('location.WineRegion', on_delete=models.CASCADE,
verbose_name=_('wine region'))
wine_sub_region = models.ForeignKey('location.WineSubRegion', on_delete=models.CASCADE,
blank=True, null=True, default=None,
verbose_name=_('wine sub region'))
class Meta:
"""Meta class."""
abstract = True
class EstablishmentWineOriginAddressQuerySet(models.QuerySet):
"""QuerySet for EstablishmentWineOriginAddress model."""
class EstablishmentWineOriginAddress(WineOriginAddressMixin):
"""Establishment wine origin address model."""
establishment = models.ForeignKey('establishment.Establishment', on_delete=models.CASCADE,
related_name='wine_origins',
verbose_name=_('product'))
objects = EstablishmentWineOriginAddressQuerySet.as_manager()
class Meta:
"""Meta class."""
verbose_name = _('establishment wine origin address')
verbose_name_plural = _('establishment wine origin addresses')
class WineOriginAddressQuerySet(models.QuerySet):
"""QuerySet for WineOriginAddress model."""
class WineOriginAddress(WineOriginAddressMixin):
"""Wine origin address model."""
product = models.ForeignKey('product.Product', on_delete=models.CASCADE,
related_name='wine_origins',
verbose_name=_('product'))
objects = WineOriginAddressQuerySet.as_manager()
class Meta:
"""Meta class."""
verbose_name = _('wine origin address')
verbose_name_plural = _('wine origin addresses')
# todo: Make recalculate price levels
@receiver(post_save, sender=Country)
def run_recalculate_price_levels(sender, instance, **kwargs):

View File

@ -191,10 +191,58 @@ class WineSubRegionBaseSerializer(serializers.ModelSerializer):
]
class WineRegionSerializer(WineRegionBaseSerializer):
"""Wine region w/ subregion serializer"""
class EstablishmentWineRegionBaseSerializer(serializers.ModelSerializer):
"""Establishment wine region origin serializer."""
wine_sub_region = WineSubRegionBaseSerializer(allow_null=True, many=True)
id = serializers.IntegerField(source='wine_region.id')
name = serializers.CharField(source='wine_region.name')
country = CountrySerializer(source='wine_region.country')
class Meta:
"""Meta class."""
model = models.EstablishmentWineOriginAddress
fields = [
'id',
'name',
'country',
]
class EstablishmentWineOriginBaseSerializer(serializers.ModelSerializer):
"""Serializer for intermediate model EstablishmentWineOrigin."""
wine_region = WineRegionBaseSerializer()
wine_sub_region = WineSubRegionBaseSerializer(allow_null=True)
class Meta:
"""Meta class."""
model = models.EstablishmentWineOriginAddress
fields = [
'wine_region',
'wine_sub_region',
]
class WineOriginRegionBaseSerializer(EstablishmentWineRegionBaseSerializer):
"""Product wine region origin serializer."""
class Meta(EstablishmentWineRegionBaseSerializer.Meta):
"""Meta class."""
model = models.WineOriginAddress
class WineOriginBaseSerializer(EstablishmentWineOriginBaseSerializer):
"""Serializer for intermediate model ProductWineOrigin."""
class Meta(EstablishmentWineOriginBaseSerializer.Meta):
"""Meta class."""
model = models.WineOriginAddress
class WineRegionSerializer(serializers.ModelSerializer):
"""Wine region w/ sub region serializer"""
wine_sub_region = WineSubRegionBaseSerializer(source='wine_region.wine_sub_region',
allow_null=True, many=True)
class Meta(WineRegionBaseSerializer.Meta):
fields = WineRegionBaseSerializer.Meta.fields + [

View File

@ -1,5 +1,6 @@
"""Product admin conf."""
from django.contrib import admin
from utils.admin import BaseModelAdminMixin
from .models import Product, ProductType, ProductSubType, ProductGallery, Unit

View File

@ -0,0 +1,44 @@
from django.core.management.base import BaseCommand
from tqdm import tqdm
from location.models import WineOriginAddress
from product.models import Product
from transfer.models import Products
from transfer.serializers.product import ProductSerializer
class Command(BaseCommand):
help = 'Add to product wine origin object.'
def handle(self, *args, **kwarg):
def get_product(old_id: int):
if old_id:
qs = Product.objects.filter(old_id=old_id)
if qs.exists():
return qs.first()
objects_to_create = []
products = Products.objects.exclude(wine_region_id__isnull=True) \
.values_list('id', 'wine_sub_region_id', 'wine_region_id')
for old_id, wine_sub_region_id, wine_region_id in tqdm(products):
product = get_product(old_id)
if product:
wine_sub_region = ProductSerializer.get_wine_sub_region(wine_sub_region_id)
wine_region = ProductSerializer.get_wine_region(wine_region_id)
if wine_region:
filters = {
'product': product,
'wine_region': wine_region}
wine_origin_address = WineOriginAddress(
product=product,
wine_region=wine_region)
if wine_sub_region:
filters.update({'wine_sub_region': wine_sub_region})
wine_origin_address.wine_sub_region = wine_sub_region
if not WineOriginAddress.objects.filter(**filters).exists():
objects_to_create.append(wine_origin_address)
WineOriginAddress.objects.bulk_create(objects_to_create)
self.stdout.write(self.style.WARNING(f'COUNT CREATED OBJECTS: {len(objects_to_create)}'))

View File

@ -0,0 +1,21 @@
# Generated by Django 2.2.7 on 2019-12-04 14:20
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('product', '0018_purchasedproduct'),
]
operations = [
migrations.RemoveField(
model_name='product',
name='wine_region',
),
migrations.RemoveField(
model_name='product',
name='wine_sub_region',
),
]

View File

@ -2,11 +2,12 @@
from django.contrib.contenttypes import fields as generic
from django.contrib.gis.db import models as gis_models
from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.db.models import Case, When
from django.utils.translation import gettext_lazy as _
from django.core.validators import MaxValueValidator, MinValueValidator
from location.models import WineOriginAddressMixin
from utils.models import (BaseAttributes, ProjectBaseMixin, HasTagsMixin,
TranslatedFieldsMixin, TJSONField, FavoritesMixin,
GalleryModelMixin, IntermediateGalleryModelMixin)
@ -89,8 +90,8 @@ class ProductQuerySet(models.QuerySet):
'establishment__address__city', 'establishment__address__city__country',
'establishment__establishment_subtypes', 'product_gallery',
'gallery', 'product_type', 'subtypes',
'classifications__classification_type', 'classifications__tags') \
.select_related('wine_region', 'wine_sub_region')
'classifications__classification_type', 'classifications__tags',
'wine_origins__wine_region', 'wine_origins__wine_sub_region', )
def common(self):
return self.filter(category=self.model.COMMON)
@ -176,14 +177,6 @@ class Product(GalleryModelMixin, TranslatedFieldsMixin, BaseAttributes,
verbose_name=_('establishment'))
public_mark = models.PositiveIntegerField(blank=True, null=True, default=None,
verbose_name=_('public mark'), )
wine_region = models.ForeignKey('location.WineRegion', on_delete=models.PROTECT,
related_name='wines',
blank=True, null=True, default=None,
verbose_name=_('wine region'))
wine_sub_region = models.ForeignKey('location.WineSubRegion', on_delete=models.PROTECT,
related_name='wines',
blank=True, null=True, default=None,
verbose_name=_('wine sub region'))
classifications = models.ManyToManyField('ProductClassification',
blank=True,
verbose_name=_('classifications'))

View File

@ -11,7 +11,7 @@ from review.serializers import ReviewShortSerializer
from utils import exceptions as utils_exceptions
from utils.serializers import TranslatedField, FavoritesCreateSerializer, ImageBaseSerializer
from main.serializers import AwardSerializer
from location.serializers import WineRegionBaseSerializer, WineSubRegionBaseSerializer
from location.serializers import WineOriginRegionBaseSerializer, WineOriginBaseSerializer
from tag.serializers import TagBaseSerializer, TagCategoryProductSerializer
@ -90,7 +90,7 @@ class ProductBaseSerializer(serializers.ModelSerializer):
subtypes = ProductSubTypeBaseSerializer(many=True, read_only=True)
establishment_detail = EstablishmentProductShortSerializer(source='establishment', read_only=True)
tags = ProductTagSerializer(source='related_tags', many=True, read_only=True)
wine_region = WineRegionBaseSerializer(read_only=True)
wine_regions = WineOriginRegionBaseSerializer(many=True, source='wine_origins', read_only=True)
wine_colors = TagBaseSerializer(many=True, read_only=True)
preview_image_url = serializers.URLField(allow_null=True,
read_only=True)
@ -110,7 +110,7 @@ class ProductBaseSerializer(serializers.ModelSerializer):
'vintage',
'tags',
'preview_image_url',
'wine_region',
'wine_regions',
'wine_colors',
'in_favorites',
]
@ -124,7 +124,7 @@ class ProductDetailSerializer(ProductBaseSerializer):
awards = AwardSerializer(many=True, read_only=True)
classifications = ProductClassificationBaseSerializer(many=True, read_only=True)
standards = ProductStandardBaseSerializer(many=True, read_only=True)
wine_sub_region = WineSubRegionBaseSerializer(read_only=True)
wine_origins = WineOriginBaseSerializer(many=True, read_only=True)
bottles_produced = TagBaseSerializer(many=True, read_only=True)
sugar_contents = TagBaseSerializer(many=True, read_only=True)
grape_variety = TagBaseSerializer(many=True, read_only=True)
@ -141,7 +141,7 @@ class ProductDetailSerializer(ProductBaseSerializer):
'awards',
'classifications',
'standards',
'wine_sub_region',
'wine_origins',
'bottles_produced',
'sugar_contents',
'image_url',

View File

@ -340,6 +340,9 @@ class ProductSerializer(TransferSerializerMixin):
def create(self, validated_data):
qs = self.Meta.model.objects.filter(old_id=validated_data.get('old_id'))
wine_region = validated_data.pop('wine_region')
wine_sub_region = validated_data.pop('wine_sub_region')
# classifications
classifications = [validated_data.pop('wine_classification', None)]
# standards
@ -361,6 +364,11 @@ class ProductSerializer(TransferSerializerMixin):
obj.standards.add(*[i for i in standards if i and i not in obj.standards.all()])
# adding tags
obj.tags.add(*[i for i in tags if i and i not in obj.tags.all()])
# checking wine origin address
wine_origin_address, _ = location_models.WineOriginAddress.objects.get_or_create(
product=obj,
wine_region=wine_region,
wine_sub_region=wine_sub_region)
return obj
def get_name(self, name, brand):
@ -390,17 +398,23 @@ class ProductSerializer(TransferSerializerMixin):
if classification_qs.exists():
return classification_qs.first()
def get_wine_region(self, wine_region):
@staticmethod
def get_wine_region(wine_region):
if wine_region:
old_id = wine_region if not isinstance(wine_region, transfer_models.WineLocations) \
else wine_region.id
wine_region_qs = location_models.WineRegion.objects.filter(
old_id=wine_region.id)
old_id=old_id)
if wine_region_qs.exists():
return wine_region_qs.first()
def get_wine_sub_region(self, wine_sub_region_id):
if wine_sub_region_id:
@staticmethod
def get_wine_sub_region(wine_sub_region):
if wine_sub_region:
old_id = wine_sub_region if not isinstance(wine_sub_region, transfer_models.WineLocations) \
else wine_sub_region.id
sub_region_qs = location_models.WineSubRegion.objects.filter(
old_id=wine_sub_region_id)
old_id=old_id)
if sub_region_qs.exists():
return sub_region_qs.first()