머리 없이 꼬리를 흔드는 데 무슨 통증이 있습니까?

25451 단어 djangocmsnextjswagtail
지난해 성숙한 NCC에서 우리는 웨이타일로 다시 썼다RIPE Labs. 웨이타일은 Django 기반의 CMS이다.이것은 최종적으로 Django 템플릿으로 서버에서 보여준 HTML을 출력하는 전체적인 응용 프로그램이 되었다.
올해 우리는 Wagtail을 다시 복습해서 우리의 main site을 다시 쓸 것이다. 나는 백엔드와 전단을 분리하고 머리 없는 CMS와 자바스크립트의 전단 구조를 결합시키는 것을 제안한다.
내가 이런 구조를 제시한 이유는 리액트와 넥스트 같은 원 구조를 이용하기 위해서다.js 또는 게이츠비.저는 주로 React의 측면에서 볼 때 이것은 제가 비교적 익숙한 것이기 때문에 Vue에 대해 평가를 했고 아래의 내용도 똑같이 적용됩니다.
이런 프로젝트를 어떻게 만드는지 더 잘 이해하기 위해 저는 마이클 린The Definitive Guide to Next.js and Wagtail과Frojdboilerplate repo를 통과했습니다.나는 네가 먼저 환매 협의를 살펴보고, 진일보한 설명이 필요할 때만 이 책을 받을 수 있다고 건의한다.
이 일련의 게시물에서, 나는 당신에게 Wagtail을 머리 없는 CMS와Next로 사용해서 프로젝트를 만드는 방법을 보여 드리겠습니다.js가 앞쪽에 있습니다.나는 네가 이 설정에 대해 성실한 평가를 할 수 있도록 최선을 다할 것이다.
Here's a link to the companion repo that contains all the code from these posts .
성숙한 NCC의 선택에 대해 궁금하시다면 이번에도 Django 템플릿을 사용하겠습니다.

JavaScript 프레임을 사용하는 이유
우선, Django 템플릿 대신 JavaScript 프레임워크를 사용해야 하는 이유는 무엇입니까?이 두 가지 방법은 모두 좋은 이유가 있다.
UI를 재사용 가능한 구성 요소로 분해하면 일관된 UI를 쉽게 개발할 수 있다고 생각합니다.또한 패키지된 구성 요소는 개별적으로 테스트하기 쉽고 TypeScript의 도움말 아래 구축할 때 Django 템플릿 엔진에서 영원히 포착할 수 없는 크기 오류를 포착할 수 있습니다.
다른 한편, 이것은 약간의 복잡성을 증가시켰다. Django 템플릿은 Django의 모든 다른 기능(예를 들어 세션과 신분 검증)과 함께 상자를 열면 바로 사용할 수 있다.
둘째, 원 프레임, 예를 들어Next.js나 Gatsby는 로컬 개발 전단에 좋은 DX를 제공하여 Django 템플릿보다 훨씬 좋습니다.더 중요한 것은 페이지를 미리 보여주고 정적 구축을 만드는 능력이 머리 없는 CMS에 적합하다는 것이다.

Wagtail 서비스 JSON에서
됐어, 그만해!너는 너의 꼬리 설정에서 어떤 변화를 해야만 그것을 완전히 무두화시킬 수 있니?
Wagtail 포함 an optional module 은 공용, 읽기 전용, JSON 형식의 API를 공개했다.그러나 이것은 가장 직관적인 것이 아닙니다. 모든 페이지에 HTML 템플릿이 있다고 가정합니다.
기본적으로 wagtail.core.Page모델에서 페이지를 보여주는 방법은 serve 인데 이 방법은 Wagtail 2.16.1부터 되돌아오는 것이다TemplateResponse.
class Page(...):
    ...
    def serve(self, request, *args, **kwargs):
        request.is_preview = getattr(request, "is_preview", False)

        return TemplateResponse(
            request,
            self.get_template(request, *args, **kwargs),
            self.get_context(request, *args, **kwargs),
        )
모든 Wagtail URL 앞에 /api/를 추가하고 JSON으로 돌아가려면 이 방법을 다시 쓰는 것이 좋습니다.이렇게 하면 프런트엔드 경로가 헤드 없는 CMS와 1:1로 매핑됩니다.또는 다른 하위 도메인에서 Wagtail을 실행할 수 있습니다.
예를 들어 경로/careers가 있는 페이지에는 일치하는 경로/api/careers가 있고 이 경로는 JSON 형식으로 Wagtail에서 페이지 내용을 되돌려줍니다.
아래의 실현에서 표두Content-Type: application/json부터/api/careers까지의 요청은 JSON의 응답을 받고 JSON의 요청을 받아들이지 않으면 /careers로 변경됩니다.
# models.py
class BasePage(Page):
    class Meta:
        abstract = True

    ...

    def serve(self, request, *args, **kwargs):
        """
        If the request accepts JSON, return an object with all
        the page's data. Otherwise redirect to the rendered frontend.
        """
        if request.content_type == "application/json":
            # this is very important, we'll see why later
            response = self.serialize_page()
            return JsonResponse(response)
        else:
            full_path = request.get_full_path()
            return HttpResponseRedirect(
                urllib.parse.urljoin(
                    settings.BASE_URL,
                    full_path.replace("/api", ""),
                )
            )
그 전에 Django REST 프레임워크를 사용하여 사용자 정의 시리얼을 정의해야 합니다.대부분의 경우, 이것은 그리 대단한 것이 아니다. last section 에서 보듯이, 풍부한 텍스트에 대해서는 약간의 조정이 필요하다.

Wagtail의 풍부한 텍스트 렌더링
Wagtail에 내장된 JSON API 모듈이 있기 때문에 서열화 프로그램을 다시 사용할 수 있다고 생각할 수도 있습니다. StreamField그러나 이렇게 하면 풍부한 텍스트의 내부 데이터베이스 표시를 되돌려줍니다.
내부 객체는 다음과 같이 표시되어 있으므로 문제가 될 수 있습니다.
<!-- Link to another page -->
<a linktype="page" id="3">Contact us</a>

<!-- Embedded image -->
<embed embedtype="image" id="10" alt="A pied wagtail" format="left" />
|richtext 템플릿 필터나 expand_db_htmlwagtail.core.rich_text 함수로 HTML을 전달할 때만 실제 URL과 이미지가 표시됩니다.
따라서 다음과 같은 방법으로 get_api_representation를 덮어쓰는 RichTextBlock 방법이 필요합니다.
# blocks.py
from wagtail.core.blocks import RichTextBlock
from wagtail.core.rich_text import expand_db_html

class CustomRichTextBlock(RichTextBlock):
    def get_api_representation(self, value, context=None):
        return expand_db_html(value.source).replace("/api", "")
RichTextField와 마찬가지로 다음과 같은 사용자 정의 필드 서열화 프로그램을 사용해야 합니다.
# fields.py
from rest_framework.fields import Field
from wagtail.core.rich_text import expand_db_html


class CustomRichTextField(Field):
    def to_representation(self, value):
        return expand_db_html(value).replace("/api", "")
Wagtail URL/api이 미리 설정되어 있으면 앞에 나타날 때 접두사를 삭제해야 합니다.

Django REST 프레임워크를 사용하여 페이지 시리얼을 작성합니다.
JSON을 출력하기 전에 우리가 가장 필요로 하는 것은 서열화 프로그램이다.무엇이 서열화 프로그램이고, 그것들은 무엇을 하는 것입니까?
Serializers allow complex data such as querysets and model instances to be converted to native Python datatypes that can then be easily rendered into JSON

요약 BasePage 을 위한 서열화 프로그램을 만들어서 실제 상황을 알아보겠습니다.모든 모델이 이 클래스에서 계승되기 때문에, 모든 페이지가 같은 메타데이터를 되돌려받을 수 있도록 공공 필드를 추가할 수 있습니다.
# serializers.py
from rest_framework import serializers

from .models import BasePage


class BasePageSerializer(serializers.ModelSerializer):
    class Meta:
        model = BasePage
        fields = ("id", "slug", "title", "url", "first_published_at")
지금까지 이 필드들은 문자열, 정수, 날짜, 시간의 대상이기 때문에, Django REST는 자동으로 처리할 수 있다.
그러나 Wagtail 페이지의 주요 구축 블록 중 하나는 StreamField입니다. Django REST는 그것을 직접 처리할 수 없지만 다행히도 Wagtail은 할 수 있습니다.필드를 처리하기 위해 서열화 프로그램을 업데이트합시다.
# serializers.py
from rest_framework import serializers
from wagtail.api.v2 import serializers as wagtail_serializers
from wagtail.core import fields

from .models import BasePage


class BasePageSerializer(serializers.ModelSerializer):
    serializer_field_mapping = serializers.ModelSerializer.serializer_field_mapping.copy()
    serializer_field_mapping.update({
       fields.StreamField: wagtail_serializers.StreamField,
    })

    class Meta:
        model = BasePage
        fields = ("id", "slug", "title", "url", "first_published_at")
이제 우리는 최초의 비추상적인 모델을 만드는 데 필요한 모든 것을 갖추고 있으며, Wagtail 인터페이스에서 이 모델을 편집하여 JSON으로 가져올 수 있다.

이 모든 것을 함께 놓아라

이렇게 많이 말했으니, 우리는 페이지 모델을 만들고, 우리 사이트에 글을 추가하자.우리의 글은 현재 매우 간단할 것이다. 요약과 풍부한 텍스트와 이미지를 포함하는 본문이 있을 것이다.
# models.py
from wagtail.core.fields import StreamField
from wagtail.images.blocks import ImageChooserBlock

from .blocks import CustomRichTextBlock

...
class ArticlePage(BasePage):
    summary = models.CharField(max_length=300)
    body = StreamField(
        [
            ("paragraph", CustomRichTextBlock()),
            ("image", ImageChooserBlock(icon="image")),
        ],
    )

    content_panels = [
        FieldPanel("title"),
        FieldPanel("summary", widget=forms.Textarea(attrs={"rows": "4"})),
        StreamFieldPanel("body"),
    ]
우리가 추가한 모든 모델은 자신의 서열화 프로그램이 필요하다는 것을 기억하십시오.그럼 하나 추가StreamField하겠습니다.모델은 ArticlePageSerializer에서 계승되기 때문에 우리는 BasePage에서 두 개의 새로운 필드를 사용하여 확장할 수 있다Meta.fields.ArticlePage는 문자열이기 때문에 Django REST가 자동으로 처리합니다.summarybody이기 때문에StreamField그것을 어떻게 처리하는지 이미 알고 있다.
# serializers.py
from .models import ArticlePage
...
class ArticlePageSerializer(BasePageSerializer):
    class Meta:
        model = ArticlePage
        fields = BasePageSerializer.Meta.fields + (
            "summary",
            "body",
        )
만약 네가 지금 이것을 운행해 보려고 한다면, 너는 오류를 얻게 될 것이다.우리는 아직 BasePageSerializerserialize_page 방법을 정의하지 않았다.우리는 지금 써야 한다. 이것은 퍼즐의 가장 중요한 부분이다.
# models.py
class BasePage(Page):
    ...
     serializer_class = None

     def serialize_page(self):
         if not self.serializer_class:
             raise Exception(
                  f"serializer_class is not set {self.__class__.__name__}",
              )
         serializer_class = import_string(self.serializer_class)
         return {
             "type": self.__class__.__name__,
             "data": serializer_class(self).data,
         }
    ...


class ArticlePage(BasePage):
      serializer_class = "your_app.serializers.ArticlePageSerializer"
    ...
여기 많은 일들이 일어났으니 제가 설명해 드릴게요.
  • 우리는 BasePage 속성을 추가했는데 기본적으로 serializer_class입니다. 서열화 프로그램은 추상 모델을 처리할 수 없기 때문입니다.
  • 우리는 Noneserializer_class 속성을 앞에 추가된 ArticlePage의 경로로 설정할 것이다.
  • 에서 우리는 동적 가져오는 서열화 프로그램을 사용해서 현재 대상을 서열화하려고 시도했다.
  • 클래스 이름은 ArticlePageSerializer로 반환되며 자세한 내용은 이 설명서의 다음 섹션에 설명되어 있습니다.
  • 최종 결과serialize_page는 다음과 같은 사전을 반환합니다.
    {
      "type": "ArticlePage",
      "data": {
        "id": 3,
        "slug": "my-article",
        "title": "My article",
        "url": "/api/my-article/",
        "first_published_at": "2022-02-25T15:14:13.030300Z",
        "summary": "My article's summary",
        "body": [
          {
            "type": "paragraph",
            "value": "<p data-block-key=\"itzwt\">This is my article</p>",
            "id": "4519e337-b467-44dd-a075-d2db4df0f0c8"
          }
        ]
      }
    }
    
    만약 우리가 "type"에 요청을 보내면 serialize_page JSON 형식으로 그것을 되돌려줍니다.

    결론
    이것은 Wagtail을 헤드 없는 CMS로 실행하는 것에 관한 나의 안내서의 첫 번째 부분의 끝이다.첫 번째 부분에서 Wagtail의 기본 동작을 템플릿이 아닌 JSON으로 되돌리는 방법을 보았습니다.
    이 설명서의 다음 부분에서는 이 JSON API를 사용하기 위해 프런트엔드를 설정하는 방법에 대해 자세히 설명합니다.
    내 블로그 최초 게시: https://tommasoamici.com/blog/headless-wagtail-what-are-the-pain-points

    좋은 웹페이지 즐겨찾기