+ array of payment_proof
+ Checklist.images is m2m now + Image.type & dynamic upload path
This commit is contained in:
parent
810fd7ce3a
commit
fc2297b17c
|
|
@ -0,0 +1,31 @@
|
|||
# Generated by Django 4.2.2 on 2023-07-07 03:08
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('store', '0034_alter_promocode_discount'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='image',
|
||||
name='checklist',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='checklist',
|
||||
name='images',
|
||||
field=models.ManyToManyField(related_name='+', to='store.image', verbose_name='Изображения'),
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='checklist',
|
||||
name='payment_proof',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='checklist',
|
||||
name='payment_proof',
|
||||
field=models.ManyToManyField(related_name='+', to='store.image', verbose_name='Подтверждение оплаты'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# Generated by Django 4.2.2 on 2023-07-07 03:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('store', '0035_remove_image_checklist_checklist_images_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='image',
|
||||
name='is_preview',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='image',
|
||||
name='type',
|
||||
field=models.PositiveSmallIntegerField(choices=[(0, 'Изображение'), (1, 'Превью')], default=0, verbose_name='Тип'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='checklist',
|
||||
name='receipt',
|
||||
field=models.ImageField(blank=True, null=True, upload_to='', verbose_name='Фото чека'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='image',
|
||||
name='image',
|
||||
field=models.ImageField(upload_to='', verbose_name='Файл изображения'),
|
||||
),
|
||||
]
|
||||
19
store/migrations/0037_alter_image_image.py
Normal file
19
store/migrations/0037_alter_image_image.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 4.2.2 on 2023-07-07 03:23
|
||||
|
||||
from django.db import migrations, models
|
||||
import store.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('store', '0036_remove_image_is_preview_image_type_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='image',
|
||||
name='image',
|
||||
field=models.ImageField(upload_to=store.models.image_upload_path, verbose_name='Файл изображения'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import posixpath
|
||||
from decimal import Decimal
|
||||
import random
|
||||
import string
|
||||
|
|
@ -166,18 +167,37 @@ class PaymentMethod(models.Model):
|
|||
return self.name
|
||||
|
||||
|
||||
def image_upload_path(instance, filename):
|
||||
dirname = Image.TYPE_TO_UPLOAD_PATH[instance.type]
|
||||
return posixpath.join(dirname, filename)
|
||||
|
||||
|
||||
@cleanup.select
|
||||
class Image(models.Model):
|
||||
image = models.ImageField(upload_to='checklist_images')
|
||||
is_preview = models.BooleanField(default=False)
|
||||
checklist = models.ForeignKey('Checklist', on_delete=models.CASCADE, related_name='images')
|
||||
DEFAULT = 0
|
||||
PREVIEW = 1
|
||||
DOC = 2
|
||||
|
||||
TYPE_CHOICES = (
|
||||
(DEFAULT, 'Изображение'),
|
||||
(PREVIEW, 'Превью'),
|
||||
)
|
||||
|
||||
TYPE_TO_UPLOAD_PATH = {
|
||||
DEFAULT: 'checklist_images/',
|
||||
PREVIEW: 'checklist_images/',
|
||||
DOC: 'docs/',
|
||||
}
|
||||
|
||||
image = models.ImageField('Файл изображения', upload_to=image_upload_path)
|
||||
type = models.PositiveSmallIntegerField('Тип', choices=TYPE_CHOICES, default=DEFAULT)
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Изображение'
|
||||
verbose_name_plural = 'Изображения'
|
||||
|
||||
def __str__(self):
|
||||
return getattr(self.image, 'name')
|
||||
return f"{self.get_type_display()}: {getattr(self.image, 'name', '')}"
|
||||
|
||||
|
||||
def generate_checklist_id():
|
||||
|
|
@ -195,6 +215,7 @@ def generate_checklist_id():
|
|||
class ChecklistQuerySet(models.QuerySet):
|
||||
def with_base_related(self):
|
||||
return self.select_related('manager', 'category', 'payment_method', 'promocode')\
|
||||
.prefetch_related('payment_proof') \
|
||||
.prefetch_related(Prefetch('images', to_attr='_images'))
|
||||
|
||||
def default_ordering(self):
|
||||
|
|
@ -299,8 +320,10 @@ class Checklist(models.Model):
|
|||
payment_method = models.ForeignKey('PaymentMethod', verbose_name='Метод оплаты',
|
||||
null=True, blank=True,
|
||||
on_delete=models.SET_NULL)
|
||||
payment_proof = models.ImageField('Подтверждение оплаты', upload_to='docs', null=True, blank=True) # paymentproovement
|
||||
receipt = models.ImageField('Фото чека', upload_to='docs', null=True, blank=True) # checkphoto
|
||||
|
||||
images = models.ManyToManyField('Image', verbose_name='Изображения', related_name='+')
|
||||
payment_proof = models.ManyToManyField('Image', verbose_name='Подтверждение оплаты', related_name='+') # paymentproovement
|
||||
receipt = models.ImageField('Фото чека', upload_to=Image.TYPE_TO_UPLOAD_PATH[Image.DOC], null=True, blank=True) # checkphoto
|
||||
|
||||
delivery = models.CharField('Тип доставки', max_length=10, choices=DeliveryType.CHOICES, null=True, blank=True)
|
||||
# trackid
|
||||
|
|
@ -330,6 +353,8 @@ class Checklist(models.Model):
|
|||
no_comission = False
|
||||
|
||||
if self.promocode_id:
|
||||
# We assume that working promocode was bound correctly through serializer
|
||||
# and intentionally don't check if promocode is active here
|
||||
promocode = self.promocode
|
||||
price -= promocode.discount * self.price_rub
|
||||
|
||||
|
|
@ -362,10 +387,10 @@ class Checklist(models.Model):
|
|||
def preview_image(self):
|
||||
# Prefer annotated field
|
||||
if hasattr(self, '_images'):
|
||||
# Get first is_preview image from all images
|
||||
return next(filter(lambda x: x.is_preview, self._images), None)
|
||||
# Get first preview image from all images
|
||||
return next(filter(lambda x: x.type == Image.PREVIEW, self._images), None)
|
||||
|
||||
return self.images.filter(is_preview=True).first()
|
||||
return self.images.filter(type=Image.PREVIEW).first()
|
||||
|
||||
@property
|
||||
def preview_image_url(self):
|
||||
|
|
@ -375,9 +400,9 @@ class Checklist(models.Model):
|
|||
def main_images(self) -> list:
|
||||
# Prefer prefetched field
|
||||
if hasattr(self, '_images'):
|
||||
return [img for img in self._images if not img.is_preview]
|
||||
return [img for img in self._images if img.type == Image.DEFAULT]
|
||||
|
||||
return self.images.filter(is_preview=False)
|
||||
return self.images.filter(type=Image.DEFAULT)
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
|
|
@ -399,11 +424,13 @@ class Checklist(models.Model):
|
|||
preview.save(image_io, format='JPEG')
|
||||
|
||||
# Create Image model and save it
|
||||
image_obj = Image(is_preview=True, checklist_id=self.id)
|
||||
image_obj = Image(type=Image.PREVIEW)
|
||||
image_obj.image.save(name=f'{self.id}_preview.jpg',
|
||||
content=ContentFile(image_io.getvalue()),
|
||||
save=True)
|
||||
|
||||
self.images.add(image_obj)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.id:
|
||||
old_obj = Checklist.objects.filter(id=self.id).first()
|
||||
|
|
|
|||
|
|
@ -58,7 +58,8 @@ class ImageListSerializer(serializers.ListSerializer):
|
|||
child = ImageSerializer()
|
||||
|
||||
def to_internal_value(self, data):
|
||||
data = [{"image": x} for x in data]
|
||||
if isinstance(data, list):
|
||||
data = [{"image": x} for x in data]
|
||||
return super().to_internal_value(data)
|
||||
|
||||
def to_representation(self, data):
|
||||
|
|
@ -72,7 +73,7 @@ class ChecklistSerializer(serializers.ModelSerializer):
|
|||
link = serializers.URLField(source='product_link', required=False)
|
||||
category = serializers.SlugRelatedField(slug_field='slug', queryset=Category.objects.all())
|
||||
|
||||
image = ImageListSerializer(source='main_images')
|
||||
image = ImageListSerializer(source='main_images', required=False)
|
||||
previewimage = serializers.ImageField(source='preview_image_url', read_only=True)
|
||||
|
||||
promo = serializers.SlugRelatedField(source='promocode', slug_field='name',
|
||||
|
|
@ -100,7 +101,7 @@ class ChecklistSerializer(serializers.ModelSerializer):
|
|||
paymenttype = serializers.SlugRelatedField(source='payment_method', slug_field='slug',
|
||||
queryset=PaymentMethod.objects.all(),
|
||||
required=False, allow_null=True)
|
||||
paymentproovement = Base64ImageField(source='payment_proof', required=False, allow_null=True)
|
||||
paymentproovement = ImageListSerializer(source='payment_proof', required=False, allow_null=True)
|
||||
checkphoto = Base64ImageField(source='receipt', required=False, allow_null=True)
|
||||
trackid = serializers.CharField(source='poizon_tracking', required=False, allow_null=True)
|
||||
cdek_tracking = serializers.CharField(required=False, allow_null=True)
|
||||
|
|
@ -109,27 +110,49 @@ class ChecklistSerializer(serializers.ModelSerializer):
|
|||
startDate = serializers.DateTimeField(source='created_at', read_only=True)
|
||||
currentDate = serializers.DateTimeField(source='status_updated_at', read_only=True)
|
||||
|
||||
def _create_images(self, instance, images):
|
||||
img_objs = [Image(image=img['image'], checklist_id=instance.id) for img in images]
|
||||
img_objs = Image.objects.bulk_create(img_objs)
|
||||
def _collect_images_by_fields(self, validated_data):
|
||||
images = {}
|
||||
for k in ('main_images', 'payment_proof'):
|
||||
if k in validated_data:
|
||||
images[k] = validated_data.pop(k)
|
||||
|
||||
return images
|
||||
|
||||
def _create_main_images(self, instance, images):
|
||||
if images is None:
|
||||
return
|
||||
|
||||
img_objs = [Image(image=img['image'], type=Image.DEFAULT) for img in images]
|
||||
img_objs = Image.objects.bulk_create(img_objs)
|
||||
instance.images.set(img_objs)
|
||||
instance.generate_preview(next(iter(img_objs), None))
|
||||
|
||||
def _create_payment_proofs(self, instance, images):
|
||||
if images is None:
|
||||
return
|
||||
|
||||
img_objs = [Image(image=img['image'], type=Image.DOC) for img in images]
|
||||
img_objs = Image.objects.bulk_create(img_objs)
|
||||
instance.payment_proof.set(img_objs)
|
||||
|
||||
def create(self, validated_data):
|
||||
images = validated_data.pop('main_images', [])
|
||||
images = self._collect_images_by_fields(validated_data)
|
||||
|
||||
instance = super().create(validated_data)
|
||||
self._create_images(instance, images)
|
||||
self._create_main_images(instance, images.get('main_images'))
|
||||
self._create_payment_proofs(instance, images.get('payment_proof'))
|
||||
return instance
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
images = validated_data.pop('main_images', [])
|
||||
images = self._collect_images_by_fields(validated_data)
|
||||
|
||||
self._create_main_images(instance, images.get('main_images'))
|
||||
self._create_payment_proofs(instance, images.get('payment_proof'))
|
||||
instance: Checklist = super().update(instance, validated_data)
|
||||
|
||||
# Replace images basically
|
||||
if images:
|
||||
instance.images.all().delete()
|
||||
self._create_images(instance, images)
|
||||
instance.refresh_from_db(fields=('images',))
|
||||
# Invalidate prefetched images
|
||||
delattr(instance, '_images')
|
||||
|
||||
return instance
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user