Build · Django
Générer des images de try-on depuis un worker Django
En une phrase
Une intégration Django stocke une ligne par job, soumet depuis une tâche Celery, effectue un polling toutes les 3 secondes jusqu’à ce que le statut soit completed, puis télécharge le fichier dans votre propre stockage.
- Crédits: 5 crédits en 2K · 6 crédits en 4K
- Délai typique: 1.5–4 min
- Traitement: asynchrone, polling
Chaque chiffre sur cette page provient de la documentation de l'API Photta · vérifié
Un modèle qui reproduit le job, une tâche Celery qui soumet et effectue le polling, et un stockage qui conserve le fichier une fois l’URL de livraison expirée.
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')const res = await fetch("https://api.photta.app/api/v1/tryon/apparel", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"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."
}),
});
const { data } = await res.json(); // data.id, data.statusimport 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"]Comment ça fonctionne
Une ligne, une tâche, un fichier
Modélisez le job, soumettez depuis une tâche, effectuez le polling avec backoff, persistez le fichier, exposez le statut.
Étape 1
Modéliser le job
Une ligne par génération : le produit, l’id de génération, le statut et l’URL de sortie. Vos vues lisent la ligne ; le worker lit l’API.
Étape 2
Obtenir une clé d’API Photta
Lisez-la depuis l’environnement dans settings, jamais depuis la base de données, et utilisez une clé distincte par environnement pour qu’une fuite en staging ne consomme pas vos credits de production.
Étape 3
Soumettre dans une tâche
Les vues doivent répondre immédiatement. La tâche Celery envoie le job en POST et écrit l’id retourné sur la ligne.
Étape 4
Effectuer le polling avec backoff
Effectuez un polling toutes les 3 secondes jusqu’à ce que le statut soit completed ou failed. En cas de 429, attendez les secondes indiquées par Retry-After au lieu de réessayer immédiatement.
Étape 5
Persister le fichier
Téléchargez l’image finale dans votre propre stockage. L’URL fournit un job ; votre bucket assure la diffusion sur la page produit pendant des années.
Étape 6
Exposer le statut
Votre front-end interroge votre ligne, pas l’API Photta. Cela permet de garder la clé sur le serveur et d’afficher la progression comme vous le souhaitez.
Modèle, tâche, erreurs, stockage
Le mécanisme de soumission-polling et la gestion des erreurs sont issus de la documentation de l’API.
# 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"])Questions fréquentes des développeurs
Questions fréquentes des développeurs Django
Pourquoi un worker plutôt qu’une vue ?
Une génération prend plus de temps qu’une requête ne le devrait. Un worker survit à la fermeture d’un onglet, à un déploiement et à un timeout ; une vue ne survit à aucun des trois.
Combien de temps la tâche doit-elle effectuer le polling ?
Suffisamment longtemps pour couvrir la latence documentée de l’endpoint, avec un plafond. Les exemples présentés ici s’arrêtent après un nombre fixe de tentatives plutôt que d’effectuer un polling indéfini.
Puis-je utiliser Django asynchrone à la place de Celery ?
Oui — les appels sont du simple HTTP. L’essentiel est que le traitement survive à la requête, quelle que soit la solution retenue.
Comment tester sans dépenser de credits ?
Enregistrez une réponse réelle et rejouez-la dans vos tests. Les endpoints de ressources et l’analyse détaillée des photos sont gratuits, les vérifications de clé et de connectivité ne coûtent donc rien.
Une même clé peut-elle servir pour tous les environnements ?
C’est possible, mais chaque clé consomme le même solde : une boucle en staging viderait donc les credits de production. Des clés séparées rendent l’incident visible et facile à stopper.
Pages associées
Lectures connexes
Fonctionnalités
API de try-on virtuel pour les vêtements
API REST qui positionne des vêtements sur des mannequins générés par IA. Traitement asynchrone, génération en 1.5–4 minutes, 5 crédits en 2K, 6 en 4K. Exemples en cURL, Node.js et Python.
LireDémarrage rapide
Première image Photta en moins de cinq minutes.
Inscrivez-vous, générez une clé d'API, lancez votre premier appel cURL, effectuez le polling jusqu'à la complétion, persistez le résultat. Cinq étapes documentées de bout en bout.
LireLangages
Python walkthrough
LireErrors
Lire
Chaque chiffre sur cette page provient de la documentation de l'API Photta · vérifié
Un worker, une ligne et un fichier
Le mécanisme de soumission-polling présenté sur cette page est issu de la documentation de l’API.