restore composer.json, add mysqli extension

This commit is contained in:
2026-04-13 17:31:07 +05:00
parent 0ed82854d9
commit 524c5cc69c
4 changed files with 82 additions and 12 deletions

View File

@@ -0,0 +1,22 @@
# Generated by Django 6.0.4 on 2026-04-13 12:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("vendors", "0006_productattributemodel_productvariantmodel"),
]
operations = [
migrations.RemoveField(
model_name="productvariantmodel",
name="image_url",
),
migrations.AddField(
model_name="productvariantmodel",
name="image",
field=models.ImageField(blank=True, null=True, upload_to="variants/", verbose_name="image"),
),
]

View File

@@ -14,7 +14,7 @@ class ProductVariantModel(AbstractBaseModel):
price = models.DecimalField(verbose_name=_("price"), max_digits=12, decimal_places=2, default=0)
sku = models.CharField(verbose_name=_("SKU"), max_length=255, null=True, blank=True)
quantity = models.IntegerField(verbose_name=_("quantity"), default=-1)
image_url = models.URLField(verbose_name=_("image url"), max_length=1000, null=True, blank=True)
image = models.ImageField(verbose_name=_("image"), upload_to="variants/", null=True, blank=True)
attribute_data = models.JSONField(verbose_name=_("attribute data"), null=True, blank=True)
def __str__(self):

View File

@@ -22,12 +22,24 @@ class ProductVariantSerializer(serializers.ModelSerializer):
"price",
"sku",
"quantity",
"image_url",
"image",
"attribute_data",
]
def to_representation(self, instance):
ret = super().to_representation(instance)
# Absolute URL for variant image
request = self.context.get("request")
image = ret.get("image")
if image and isinstance(image, str) and not image.startswith("http"):
if not image.startswith("/resources/"):
image = f"/resources/media/{image.lstrip('/')}"
if request:
ret["image"] = request.build_absolute_uri(image)
else:
ret["image"] = image
attr_data = ret.get("attribute_data")
if attr_data and isinstance(attr_data, list):

View File

@@ -24,6 +24,8 @@ import firebase_admin
from firebase_admin import credentials, firestore
from decimal import Decimal, InvalidOperation
from datetime import datetime, timezone
import requests
from django.core.files.base import ContentFile
# ── Django sozlash ───────────────────────────────────────────────────────────
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -131,18 +133,26 @@ def build_product_data(doc_id: str, data: dict) -> dict:
)
def print_progress(current, total, created, updated, skipped, errors):
percent = int(current / total * 100) if total else 0
bar_filled = percent // 5
bar = "" * bar_filled + "" * (20 - bar_filled)
line = (
f"\r [{bar}] {percent:3d}% "
f"{current}/{total} | "
f"{created}{updated}{skipped}{errors}"
)
print(line, end="", flush=True)
def download_and_save_image(url: str, model_instance, field_name: str, filename: str):
"""Rasmni yuklab oladi va modelning ko'rsatilgan maydoniga saqlaydi."""
if not url or not url.startswith("http"):
return False
try:
response = requests.get(url, timeout=10)
if response.status_code == 200:
content = ContentFile(response.content)
field = getattr(model_instance, field_name)
# S3'da duplicatelarning oldini olish uchun save(name, content, save=True)
field.save(filename, content, save=True)
return True
except Exception as e:
print(f"\n [IMG XATO] {url}: {e}")
return False
def sync_attributes():
print("Firebase vendor_attributes kolleksiyasi o'qilmoqda…")
docs = db.collection("vendor_attributes").stream()
@@ -224,6 +234,21 @@ def sync_products(dry_run: bool = False, update: bool = False, after: datetime |
)
created += 1
# ── Rasmni yuklab olish (Asosiy mahsulot uchun) ────────────────────
if not dry_run and product_obj:
# Agar asosiy rasm bo'lmasa, uni yuklab olamiz
if not product_obj.image and product_data.get("photos_json"):
first_photo = product_data["photos_json"][0]
if isinstance(first_photo, str):
ext = first_photo.split(".")[-1].split("?")[0] or "jpg"
download_and_save_image(
first_photo,
product_obj,
"image",
f"product_{doc_id}.{ext}"
)
# ── Variantlarni qayta ishlash ────────────────────────────────────
item_attr = data.get("item_attribute")
@@ -244,10 +269,21 @@ def sync_products(dry_run: bool = False, update: bool = False, after: datetime |
"price": to_decimal(v_data.get("variant_price", 0)),
"sku": v_data.get("variant_sku"),
"quantity": to_int(v_data.get("variant_quantity", -1)),
"image_url": v_data.get("variant_image"),
"attribute_data": item_attr.get("attributes")
}
)
# Variant rasm yuklash
v_obj = ProductVariantModel.objects.filter(firestore_id=v_id).first()
if v_obj and not v_obj.image and v_data.get("variant_image"):
v_img_url = v_data["variant_image"]
ext = v_img_url.split(".")[-1].split("?")[0] or "jpg"
download_and_save_image(
v_img_url,
v_obj,
"image",
f"variant_{v_id}.{ext}"
)
except Exception as e:
print(f"\n [XATO] {doc_id}: {e}")