(wagtailsnippets_features)= # 선택적 기능 기본적으로 스니펫은 미리 보기, 개정, 워크플로와 같은 페이지의 많은 기능이 부족합니다. 이러한 기능은 적절한 믹스인 클래스에서 상속하여 각 스니펫 모델에 개별적으로 추가할 수 있습니다. (wagtailsnippets_making_snippets_previewable)= ## 스니펫 미리 보기 가능하게 만들기 스니펫 모델이 {class}`~wagtail.models.PreviewableMixin` 에서 상속하는 경우 Wagtail은 편집기에 라이브 미리 보기 패널을 자동으로 추가합니다. 믹스인을 상속하는 것 외에도 모델은 {meth}`~wagtail.models.PreviewableMixin.get_preview_template` 또는 {meth}`~wagtail.models.PreviewableMixin.serve_preview` 를 재정의해야 합니다. 예를 들어, `Advert` 스니펫은 다음과 같이 미리 보기 가능하게 만들 수 있습니다. ```python # ... from wagtail.models import PreviewableMixin # ... class Advert(PreviewableMixin, models.Model): url = models.URLField(null=True, blank=True) text = models.CharField(max_length=255) panels = [ FieldPanel('url'), FieldPanel('text'), ] def get_preview_template(self, request, mode_name): return "demo/previews/advert.html" ``` 다음 `demo/previews/advert.html` 템플릿: ```html+django {{ object.text }} {{ object.text }} ``` 기본 컨텍스트에서 사용할 수 있는 변수는 `request`(가짜 {class}`~django.http.HttpRequest` 객체) 및 `object`(스니펫 인스턴스)입니다. 컨텍스트를 사용자 지정하려면 {meth}`~wagtail.models.PreviewableMixin.get_preview_context` 메서드를 재정의할 수 있습니다. 기본적으로 `serve_preview` 메서드는 요청 객체, `get_preview_template` 에서 반환된 템플릿, `get_preview_context` 에서 반환된 컨텍스트 객체를 사용하여 렌더링되는 {class}`~django.template.response.TemplateResponse` 를 반환합니다. 렌더링 및/또는 라우팅 논리를 사용자 지정하려면 `serve_preview` 메서드를 재정의할 수 있습니다. 페이지와 유사하게 {attr}`~wagtail.models.PreviewableMixin.preview_modes` 속성을 재정의하여 여러 미리 보기 모드를 정의할 수 있습니다. 예를 들어, 다음 `Advert` 스니펫에는 두 가지 미리 보기 모드가 있습니다. ```python # ... from wagtail.models import PreviewableMixin # ... class Advert(PreviewableMixin, models.Model): url = models.URLField(null=True, blank=True) text = models.CharField(max_length=255) panels = [ FieldPanel('url'), FieldPanel('text'), ] @property def preview_modes(self): return PreviewableMixin.DEFAULT_PREVIEW_MODES + [("alt", "대체")] def get_preview_template(self, request, mode_name): templates = { "": "demo/previews/advert.html", # 기본 미리 보기 모드 "alt": "demo/previews/advert_alt.html", # 대체 미리 보기 모드 } return templates.get(mode_name, templates[""]) def get_preview_context(self, request, mode_name): context = super().get_preview_context(request, mode_name) if mode_name == "alt": context["extra_context"] = "대체 미리 보기 모드" return context ``` (wagtailsnippets_making_snippets_searchable)= ## 스니펫 검색 가능하게 만들기 스니펫 모델이 [](wagtailsearch_indexing_models)에 설명된 대로 `wagtail.search.index.Indexed` 에서 상속하는 경우 Wagtail은 해당 스니펫 유형에 대한 선택기 인터페이스에 검색 상자를 자동으로 추가합니다. 예를 들어, `Advert` 스니펫은 다음과 같이 검색 가능하게 만들 수 있습니다. ```python # ... from wagtail.search import index # ... class Advert(index.Indexed, models.Model): url = models.URLField(null=True, blank=True) text = models.CharField(max_length=255) panels = [ FieldPanel('url'), FieldPanel('text'), ] search_fields = [ index.SearchField('text'), index.AutocompleteField('text'), ] ``` (wagtailsnippets_saving_revisions_of_snippets)= ## 스니펫 개정 저장 스니펫 모델이 {class}`~wagtail.models.RevisionMixin` 에서 상속하는 경우 Wagtail은 스니펫 관리자에서 변경 사항을 저장할 때 자동으로 개정을 저장합니다. 믹스인은 스니펫 인스턴스의 모든 개정에 대한 쿼리셋을 제공하는 `revisions` 속성을 정의합니다. 또한 {class}`~wagtail.models.Revision` 모델에 대한 기본 {class}`~django.contrib.contenttypes.fields.GenericRelation` 이 함께 제공되어 스니펫 인스턴스가 삭제될 때 개정이 올바르게 정리됩니다. 기본 `GenericRelation` 에는 {attr}`~django.contrib.contenttypes.fields.GenericRelation.related_query_name` 이 없으므로 `Revision` 모델에서 스니펫 모델로 쿼리하고 필터링할 수 없습니다. 이 기능이 필요한 경우 사용자 지정 `related_query_name` 으로 자체 `GenericRelation` 을 정의할 수 있습니다. 자세한 내용은 기본 `GenericRelation` {attr}`~wagtail.models.RevisionMixin._revisions` 및 속성 {attr}`~wagtail.models.RevisionMixin.revisions` 를 참조하십시오. ```{versionadded} 7.1 기본 `GenericRelation` {attr}`~wagtail.models.RevisionMixin._revisions` 가 추가되었습니다. ``` 예를 들어, `Advert` 스니펫은 다음과 같이 개정 가능하게 만들 수 있습니다. ```python # ... from django.contrib.contenttypes.fields import GenericRelation from wagtail.models import RevisionMixin # ... class Advert(RevisionMixin, models.Model): url = models.URLField(null=True, blank=True) text = models.CharField(max_length=255) # 사용자 지정 논리가 필요하지 않은 경우 `revisions` 로 직접 정의할 수 있습니다. _revisions = GenericRelation("wagtailcore.Revision", related_query_name="advert") panels = [ FieldPanel('url'), FieldPanel('text'), ] @property def revisions(self): # 필요한 경우 여기에 사용자 지정 논리, 예: 다중 테이블 상속 처리. # 믹스인은 이미 상속을 처리하므로 선택 사항입니다. return self._revisions.all() ``` 스니펫 모델이 Django의 {class}`~django.db.models.ManyToManyField` 를 사용하여 관계를 정의하는 경우 모델 클래스를 `django.models.Model` 대신 `modelcluster.models.ClusterableModel` 에서 상속하도록 변경하고 `ManyToManyField` 를 `ParentalManyToManyField` 로 바꿔야 합니다. 인라인 모델은 `ForeignKey` 대신 `ParentalKey` 를 계속 사용해야 합니다. 이는 관계를 개정에 저장할 수 있도록 하는 데 필요합니다. 자세한 내용은 튜토리얼의 [](tutorial_categories) 섹션을 참조하십시오. 예를 들어: ```python # ... from django.db import models from modelcluster.fields import ParentalKey, ParentalManyToManyField from modelcluster.models import ClusterableModel from wagtail.models import RevisionMixin # ... class ShirtColour(models.Model): name = models.CharField(max_length=255) panels = [FieldPanel("name")] class ShirtCategory(models.Model): name = models.CharField(max_length=255) panels = [FieldPanel("name")] class Shirt(RevisionMixin, ClusterableModel): name = models.CharField(max_length=255) colour = models.ForeignKey("shirts.ShirtColour", on_delete=models.SET_NULL, blank=True, null=True) categories = ParentalManyToManyField("shirts.ShirtCategory", blank=True) revisions = GenericRelation("wagtailcore.Revision", related_query_name="shirt") panels = [ FieldPanel("name"), FieldPanel("colour"), FieldPanel("categories", widget=forms.CheckboxSelectMultiple), InlinePanel("images", label="이미지"), ] class ShirtImage(models.Model): shirt = ParentalKey("shirts.Shirt", related_name="images") image = models.ForeignKey("wagtailimages.Image", on_delete=models.CASCADE, related_name="+") caption = models.CharField(max_length=255, blank=True) panels = [ FieldPanel("image"), FieldPanel("caption"), ] ``` `RevisionMixin` 에는 데이터베이스 테이블에 추가해야 하는 `latest_revision` 필드가 포함되어 있습니다. 위의 변경 사항을 데이터베이스에 적용하려면 `makemigrations` 및 `migrate` 관리 명령을 실행해야 합니다. `RevisionMixin` 이 적용되면 스니펫 관리자에서 변경된 모든 내용은 스니펫 인스턴스의 상태를 포함하는 `Revision` 모델 인스턴스를 생성합니다. 개정 인스턴스는 편집 작업의 [감사 로그](audit_log) 항목에 첨부되어 스니펫 기록 페이지에서 이전 개정으로 되돌리거나 개정 간의 변경 사항을 비교할 수 있습니다. `{meth}`~wagtail.models.RevisionMixin.save_revision` 메서드를 호출하여 프로그래밍 방식으로 개정을 저장할 수도 있습니다. 믹스인을 적용한 후 `latest_revision` 필드가 데이터베이스 테이블에 채워지도록 이미 존재하는 스니펫 인스턴스(있는 경우)에 대해 이 메서드를 (또는 관리자에서 스니펫을 저장) 한 번 이상 호출하는 것이 좋습니다. 예약된 게시 기능을 사용하는 경우 [`publish_scheduled`](publish_scheduled) 관리 명령을 주기적으로 실행해야 합니다. 자세한 내용은 [](scheduled_publishing)을 참조하십시오. 스니펫 인스턴스를 게시하려면 스니펫 모델에 대한 `publish` 권한이 필요합니다. `DraftStateMixin` 이 적용된 모델의 경우 Wagtail은 해당 `publish` 권한을 자동으로 생성하고 Wagtail 관리자 인터페이스의 '그룹' 영역에 표시합니다. 권한을 구성하는 방법에 대한 자세한 내용은 [](permissions_overview)를 참조하십시오. ```{warning} Wagtail은 아직 게시되지 않은("초안") 스니펫을 페이지에 포함하는 것을 막는 메커니즘이 없습니다. `DraftStateMixin` 이 활성화된 스니펫을 페이지에 포함할 때 초안 스니펫이 어떻게 렌더링되어야 하는지 처리하기 위한 필요한 검사를 추가해야 합니다(예: `live` 필드 확인). 향후 이를 개선할 계획입니다. ``` (wagtailsnippets_saving_draft_changes_of_snippets)= ## 스니펫 초안 변경 사항 저장 스니펫 모델이 {class}`~wagtail.models.DraftStateMixin` 에서 상속하는 경우 Wagtail은 스니펫 관리자에서 변경 사항을 초안으로 저장하고 별도로 게시할 수 있는 기능을 자동으로 추가합니다. 이 믹스인은 {class}`~wagtail.models.RevisionMixin` 이 적용되어야 합니다. 예를 들어, `Advert` 스니펫은 다음과 같이 초안 변경 사항을 지원하도록 만들 수 있습니다: ```python # ... from wagtail.models import DraftStateMixin, RevisionMixin # ... class Advert(DraftStateMixin, RevisionMixin, models.Model): url = models.URLField(null=True, blank=True) text = models.CharField(max_length=255) revisions = GenericRelation("wagtailcore.Revision", related_query_name="advert") panels = [ FieldPanel('url'), FieldPanel('text'), ] ``` `DraftStateMixin` 에는 데이터베이스 테이블에 추가해야 하는 추가 필드가 포함되어 있습니다. 위의 변경 사항을 데이터베이스에 적용하려면 `makemigrations` 및 `migrate` 관리 명령을 실행해야 합니다. 스니펫 인스턴스를 게시하려면 스니펫 모델에 대한 `publish` 권한이 필요합니다. `DraftStateMixin` 이 적용된 모델의 경우 Wagtail은 해당 `publish` 권한을 자동으로 생성하고 Wagtail 관리자 인터페이스의 '그룹' 영역에 표시합니다. 권한을 구성하는 방법에 대한 자세한 내용은 [](permissions_overview)를 참조하십시오. ```{warning} Wagtail은 아직 게시되지 않은("초안") 스니펫을 페이지에 포함하는 것을 막는 메커니즘이 없습니다. `DraftStateMixin` 이 활성화된 스니펫을 페이지에 포함할 때 초안 스니펫이 어떻게 렌더링되어야 하는지 처리하기 위한 필요한 검사를 추가해야 합니다(예: `live` 필드 확인). 향후 이를 개선할 계획입니다. ``` (wagtailsnippets_locking_snippets)= ## 스니펫 잠금 스니펫 모델이 {class}`~wagtail.models.LockableMixin` 에서 상속하는 경우 Wagtail은 모델 인스턴스를 잠글 수 있는 기능을 자동으로 추가합니다. 편집할 때 Wagtail은 "상태" 사이드 패널에 잠금 정보를 표시하고, 사용자가 잠금/잠금 해제 권한이 있는 경우 인스턴스를 잠금/잠금 해제하는 버튼을 표시합니다. 모델이 예약된 게시를 갖도록 구성된 경우(위의 [](wagtailsnippets_saving_draft_changes_of_snippets) 참조) Wagtail은 게시 예정인 모든 인스턴스를 잠급니다. 페이지와 유사하게 스니펫을 잠근 사용자는 `WAGTAILADMIN_GLOBAL_EDIT_LOCK` 이 `True` 로 설정되지 않는 한 여전히 편집할 수 있습니다. 예를 들어, `Advert` 스니펫 인스턴스는 다음과 같이 정의하여 잠글 수 있습니다. ```python # ... from wagtail.models import LockableMixin # ... class Advert(LockableMixin, models.Model): url = models.URLField(null=True, blank=True) text = models.CharField(max_length=255) panels = [ FieldPanel('url'), FieldPanel('text'), ] ``` 다른 믹스인을 사용하는 경우 `LockableMixin` 을 다른 믹스인 다음에, 그러나 `RevisionMixin` 이전에 적용해야 합니다(왼쪽에서 오른쪽 순서). 예를 들어, `DraftStateMixin` 및 `RevisionMixin` 과 함께 모델의 올바른 상속은 `class MyModel(DraftStateMixin, LockableMixin, RevisionMixin)` 이 됩니다. 믹스인의 순서를 강제하는 시스템 검사가 있습니다. `LockableMixin` 에는 데이터베이스 테이블에 추가해야 하는 추가 필드가 포함되어 있습니다. 위의 변경 사항을 데이터베이스에 적용하려면 `makemigrations` 및 `migrate` 관리 명령을 실행해야 합니다. 스니펫 인스턴스를 잠그고 잠금 해제하려면 스니펫 모델에 대한 `lock` 및 `unlock` 권한이 각각 필요합니다. `LockableMixin` 이 적용된 모델의 경우 Wagtail은 해당 `lock` 및 `unlock` 권한을 자동으로 생성하고 Wagtail 관리자 인터페이스의 '그룹' 영역에 표시합니다. 권한을 구성하는 방법에 대한 자세한 내용은 [](permissions_overview)를 참조하십시오. (wagtailsnippets_enabling_workflows)= ## 스니펫에 워크플로 활성화 스니펫 모델이 {class}`~wagtail.models.WorkflowMixin` 에서 상속하는 경우 Wagtail은 모델에 워크플로를 할당하는 기능을 자동으로 추가합니다. 스니펫 모델에 워크플로가 할당되면 편집기에 "중재를 위해 제출" 및 기타 워크플로 작업 메뉴 항목이 표시됩니다. 상태 사이드 패널에는 현재 워크플로에 대한 정보도 표시됩니다. `WorkflowMixin` 은 Wagtail의 개정 및 게시 메커니즘을 사용하므로 이 믹스인을 상속하려면 `RevisionMixin` 및 `DraftStateMixin` 도 상속해야 합니다. 또한 `LockableMixin` 에서 상속하여 잠금을 활성화하는 것이 좋습니다. 이렇게 하면 스니펫 인스턴스가 워크플로에 있을 때 잠겨서 검토자만 편집할 수 있습니다. 자세한 내용은 위 섹션을 참조하십시오. 믹스인은 스니펫 인스턴스의 모든 워크플로 상태에 대한 쿼리셋을 제공하는 `workflow_states` 속성을 정의합니다. 또한 {class}`~wagtail.models.WorkflowState` 모델에 대한 기본 {class}`~django.contrib.contenttypes.fields.GenericRelation` 이 함께 제공되어 스니펫 인스턴스가 삭제될 때 워크플로 상태가 올바르게 정리됩니다. 기본 `GenericRelation` 에는 {attr}`~django.contrib.contenttypes.fields.GenericRelation.related_query_name` 이 없으므로 `WorkflowState` 모델에서 스니펫 모델로 쿼리하고 필터링할 수 없습니다. 이 기능이 필요한 경우 사용자 지정 `related_query_name` 으로 자체 `GenericRelation` 을 정의할 수 있습니다. 자세한 내용은 기본 `GenericRelation` {attr}`~wagtail.models.WorkflowMixin._workflow_states` 및 속성 {attr}`~wagtail.models.WorkflowMixin.workflow_states` 를 참조하십시오. ```{versionadded} 7.1 기본 `GenericRelation` {attr}`~wagtail.models.WorkflowMixin._workflow_states` 가 추가되었습니다. ``` 예를 들어, `Advert` 스니펫에 워크플로(잠금 포함)를 다음과 같이 정의하여 활성화할 수 있습니다. ```python # ... from wagtail.models import DraftStateMixin, LockableMixin, RevisionMixin, WorkflowMixin # ... class Advert(WorkflowMixin, DraftStateMixin, LockableMixin, RevisionMixin, models.Model): url = models.URLField(null=True, blank=True) text = models.CharField(max_length=255) _revisions = GenericRelation("wagtailcore.Revision", related_query_name="advert") workflow_states = GenericRelation( "wagtailcore.WorkflowState", content_type_field="base_content_type", object_id_field="object_id", related_query_name="advert", for_concrete_model=False, ) panels = [ FieldPanel('url'), FieldPanel('text'), ] @property def revisions(self): return self._revisions ``` `WorkflowMixin` 에 필요한 다른 믹스인에는 데이터베이스 테이블에 추가해야 하는 추가 필드가 포함되어 있습니다. 위의 변경 사항을 데이터베이스에 적용하려면 `makemigrations` 및 `migrate` 관리 명령을 실행해야 합니다. 믹스인을 활성화한 후 워크플로 설정을 통해 스니펫 모델에 워크플로를 할당할 수 있습니다. 자세한 내용은 [중재를 위한 워크플로 구성](https://guide.wagtail.org/en-latest/how-to-guides/configure-workflows-for-moderation/) 방법을 참조하십시오. 관리자 대시보드 및 워크플로 보고서에는 워크플로에 제출된 스니펫(페이지와 함께)도 표시됩니다. ## 스니펫 태그 지정 스니펫에 태그를 추가하는 것은 페이지에 태그를 추가하는 것과 매우 유사합니다. 유일한 차이점은 `RevisionMixin` 이 적용되지 않은 경우 `modelcluster.contrib.taggit.ClusterTaggableManager` 대신 `taggit.manager.TaggableManager` 를 사용해야 한다는 것입니다. ```python # ... from modelcluster.fields import ParentalKey from modelcluster.models import ClusterableModel from taggit.models import TaggedItemBase from taggit.managers import TaggableManager # ... class AdvertTag(TaggedItemBase): content_object = ParentalKey('demo.Advert', on_delete=models.CASCADE, related_name='tagged_items') class Advert(ClusterableModel): # ... tags = TaggableManager(through=AdvertTag, blank=True) panels = [ # ... FieldPanel('tags'), ] ``` [페이지 태그 지정에 대한 문서](tagging)에는 뷰에서 태그를 사용하는 방법에 대한 자세한 정보가 있습니다. (wagtailsnippets_inline_models)= ## 스니펫 내 인라인 모델 페이지와 유사하게 스니펫 내에 다른 모델을 중첩할 수 있습니다. 이를 위해서는 스니펫 모델이 `django.models.Model` 대신 `modelcluster.models.ClusterableModel` 에서 상속해야 합니다. ```python from django.db import models from modelcluster.fields import ParentalKey from modelcluster.models import ClusterableModel from wagtail.models import Orderable class BandMember(Orderable): band = ParentalKey("music.Band", related_name="members", on_delete=models.CASCADE) name = models.CharField(max_length=255) @register_snippet class Band(ClusterableModel): name = models.CharField(max_length=255) panels = [ FieldPanel("name"), InlinePanel("members") ] ``` [페이지에서 인라인 모델을 사용하는 방법에 대한 문서](inline_models)는 스니펫에도 적용되는 더 많은 정보를 제공합니다.