# Obrazki OG w Django

Sygnał post_save, transaction.on_commit, adres zapisany przez update().

## Gdzie to wpiąć

Strony w Django zwykle rysują karty OG ręcznie w Pillow, piksel po pikselu. Z szablonem w HTML i CSS zmieniasz wygląd w arkuszu stylów, a po stronie Pythona zostaje jedno żądanie po zapisaniu wpisu.

`blog/signals.py`

```python
import requests
from django.conf import settings
from django.db import transaction
from django.db.models.signals import post_save
from django.dispatch import receiver

from .models import Post

API_URL = "https://shareglint.com/v1/images?wait=true"


def generate_og_image(post_id: int) -> None:
    post = Post.objects.select_related("author").get(pk=post_id)
    response = requests.post(
        API_URL,
        headers={"Authorization": f"Bearer {settings.SHAREGLINT_KEY}"},
        json={
            "template": "blog-og",
            "modifications": {
                "title": post.title,
                "author": post.author.get_full_name(),
                "date": post.published_at.date().isoformat(),
                "site": "yourblog.com",
            },
        },
        timeout=20,
    )
    response.raise_for_status()
    # .update() instead of .save(): it does not fire post_save again.
    Post.objects.filter(pk=post_id).update(og_image_url=response.json()["url"])


@receiver(post_save, sender=Post)
def on_post_saved(sender, instance, update_fields=None, **kwargs):
    if not instance.is_published:
        return
    # Run after the transaction commits; with Celery, call .delay() here instead.
    transaction.on_commit(lambda: generate_og_image(instance.pk))
```

## Pułapka

Zapisuj adres przez `QuerySet.update()`, nie `post.save()`. `save()` w obsłudze `post_save` odpala sygnał ponownie — i dostajesz pętlę, która renderuje ten sam obrazek, aż coś przekroczy limit czasu.

## Ile to kosztuje

Sto obrazków miesięcznie za darmo, potem $9 za dwa tysiące. Ten sam wpis wyrenderowany drugi raz zwraca istniejący adres i nie zużywa limitu — więc przebudowanie strony nic nie kosztuje.
