Rakenna · Django

Renderöi try-on-kuvia Django-workerilla

Yhdellä lauseella

Django-integraatio tallentaa yhden rivin per työ, lähettää sen Celery-tehtävästä, tekee pollingia 3 sekunnin välein, kunnes tila on completed, ja lataa sitten tiedoston omaan tallennustilaasi.

  • Credits: 5 credits resoluutiolla 2K · 6 credits resoluutiolla 4K
  • Tyypillinen kesto: 1.5–4 min
  • Käsittely: asynkroninen, polling

Jokainen luku tällä sivulla on peräisin Photta-rajapinnan dokumentaatiosta · tarkistettu

Malli, joka peilaa työn tilaa, Celery-tehtävä, joka lähettää pyynnön ja hoitaa pollingin, sekä tallennustila, joka säilyttää tiedoston toimitus-URL:n vanhentumisen jälkeen.

RESPONSE=$(curl -s -X POST https://api.photta.app/api/v1/tryon/apparel \
  -H "Authorization: Bearer $PHOTTA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "product_type": "top",
  "product_images": [
    "https://cdn.yourstore.com/products/shirt-front.jpg"
  ],
  "product_back_image": "https://cdn.yourstore.com/products/shirt-back.jpg",
  "product_category": "t_shirt",
  "mannequin_id": "mnk_f3a9b1",
  "pose_id": "pose_c2d4e5",
  "background": "https://cdn.yourstore.com/backgrounds/studio-loft.jpg",
  "lock_background": true,
  "resolution": "2K",
  "aspect_ratio": "3:4",
  "body_size": "M",
  "expression": "neutral",
  "user_instruction": "Keep the collar pattern crisp and centered."
}')
ID=$(echo "$RESPONSE" | jq -r '.data.id')

Miten se toimii

Yksi rivi, yksi tehtävä, yksi tiedosto

Mallinna työ, lähetä tehtävästä, tee pollingia backoffilla, tallenna tiedosto, tarjoa tila eteenpäin.

  1. Vaihe 1

    Mallinna työ

    Yksi rivi generointia kohden: tuote, generointitunniste, tila ja output_url. Riviltä näkymäsi lukevat tiedot; rajapinta on se, mitä worker lukee.

  2. Vaihe 2

    Hanki Photta API-avain

    Lue se asetuksissa ympäristömuuttujista, älä koskaan tietokannasta, ja käytä eri avainta kussakin ympäristössä, jotta staging-vuoto ei kuluta tuotannon creditsejä.

  3. Vaihe 3

    Lähetä tehtävässä

    Näkymien tulisi palauttaa vastaus heti. Celery-tehtävä tekee POST-pyynnön työlle ja kirjoittaa palautetun tunnisteen riville.

  4. Vaihe 4

    Tee pollingia backoffilla

    Tee pollingia 3 sekunnin välein, kunnes tila on completed tai failed. Jos vastauksena on 429, odota Retry-After-sekunnit sen sijaan, että yrittäisit heti uudelleen.

  5. Vaihe 5

    Tallenna tiedosto pysyvästi

    Lataa valmis kuva omaan tallennustilaasi. URL palvelee yhtä työtä; oma buckettisi jakelee kuvaa tuotesivulle vuosien ajan.

  6. Vaihe 6

    Tarjoa tila eteenpäin

    Käyttöliittymäsi tekee pollingia omaan riviisi, ei Photta-rajapintaan. Tämä pitää avaimen palvelimella ja antaa sinun näyttää edistymisen haluamallasi tavalla.

Malli, tehtävä, virheet, tallennus

Lähetys-ja-polling-malli sekä virheidenkäsittely on renderöity API-dokumentaatiosta.

# store/models.py
from django.db import models

class TryOn(models.Model):
    product = models.ForeignKey("Product", on_delete=models.CASCADE)
    generation_id = models.CharField(max_length=64, db_index=True)
    status = models.CharField(max_length=16, default="processing")
    output_url = models.URLField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
# store/tasks.py
from celery import shared_task

@shared_task(bind=True, max_retries=3)
def run_try_on(self, try_on_id):
    import os, requests
    
    res = requests.post(
        "https://api.photta.app/api/v1/tryon/apparel",
        headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"},
        json={
          "product_type": "top",
          "product_images": [
            "https://cdn.yourstore.com/products/shirt-front.jpg"
          ],
          "product_back_image": "https://cdn.yourstore.com/products/shirt-back.jpg",
          "product_category": "t_shirt",
          "mannequin_id": "mnk_f3a9b1",
          "pose_id": "pose_c2d4e5",
          "background": "https://cdn.yourstore.com/backgrounds/studio-loft.jpg",
          "lock_background": True,
          "resolution": "2K",
          "aspect_ratio": "3:4",
          "body_size": "M",
          "expression": "neutral",
          "user_instruction": "Keep the collar pattern crisp and centered."
        },
    )
    data = res.json()["data"]  # data["id"], data["status"]
    
    import time
    
    def wait_for(job_id):
        for _ in range(120):
            res = requests.get(f"https://api.photta.app/api/v1/tryon/apparel/{job_id}", headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"})
            data = res.json()["data"]
            if data["status"] == "completed":
                return data["output_url"]
            if data["status"] == "failed":
                raise RuntimeError(data.get("error_message", "generation failed"))
            time.sleep(3)
        raise TimeoutError("timed out after 6 minutes")
try:
    photta("POST", "/tryon/apparel", json={
      "product_type": "top",
      "product_images": [
        "https://cdn.yourstore.com/products/shirt-front.jpg"
      ],
      "product_back_image": "https://cdn.yourstore.com/products/shirt-back.jpg",
      "product_category": "t_shirt",
      "mannequin_id": "mnk_f3a9b1",
      "pose_id": "pose_c2d4e5",
      "background": "https://cdn.yourstore.com/backgrounds/studio-loft.jpg",
      "lock_background": True,
      "resolution": "2K",
      "aspect_ratio": "3:4",
      "body_size": "M",
      "expression": "neutral",
      "user_instruction": "Keep the collar pattern crisp and centered."
    })
except PhottaError as e:
    if e.status == 402:
        print("insufficient_credits_error: top up before retrying")
    elif e.status == 429:
        time.sleep(int(e.retry_after or 30))
    else:
        raise
# store/tasks.py — the output URL delivers one job; your storage keeps it
import requests
from django.core.files.base import ContentFile

def persist(try_on, output_url):
    body = requests.get(output_url, timeout=60).content
    try_on.product.gallery.save(f"{try_on.generation_id}.jpg", ContentFile(body))
    try_on.status, try_on.output_url = "completed", output_url
    try_on.save(update_fields=["status", "output_url"])

Kehittäjien usein kysymää

Django-kehittäjien usein kysymät kysymykset

Miksi worker näkymän sijaan?

Generointi kestää pidempään kuin pyynnön pitäisi kestää. Worker selviää suljetusta selainvälilehdestä, deployauksesta ja aikakatkaisusta; näkymä ei selviä yhdestäkään.

Kuinka kauan tehtävän tulisi tehdä pollingia?

Riittävän kauan endpointin dokumentoidun viiveen kattamiseksi, ylärajalla varustettuna. Tämän esimerkit pysähtyvät määrätyn yritysmäärän jälkeen sen sijaan, että polling jatkuisi ikuisesti.

Voinko käyttää asynkronista Djangoa Celeryn sijaan?

Kyllä — kutsut ovat puhdasta HTTP:tä. Olennaista on, että työ elää pidempään kuin pyyntö, järjestitpä sen miten tahansa.

Miten testaan kuluttamatta creditsejä?

Tallenna yksi oikea vastaus ja toista se testeissä. Resurssi-endpointit ja yksityiskohtien kuva-analyysi ovat ilmaisia, joten avain- ja yhteystarkistukset eivät maksa mitään.

Voiko yksi avain palvella jokaista ympäristöä?

Voi, mutta jokainen avain käyttää samaa saldoa, joten staging-silmukka kuluttaa tuotannon creditsejä. Erilliset avaimet tekevät vahingosta näkyvän ja helposti pysäytettävän.

Worker, rivi ja tiedosto

Tämän sivun lähetys ja polling on renderöity API-dokumentaatiosta.