선택적 기능¶
기본적으로 스니펫은 미리 보기, 개정, 워크플로와 같은 페이지의 많은 기능이 부족합니다. 이러한 기능은 적절한 믹스인 클래스에서 상속하여 각 스니펫 모델에 개별적으로 추가할 수 있습니다.
스니펫 미리 보기 가능하게 만들기¶
스니펫 모델이 PreviewableMixin 에서 상속하는 경우 Wagtail은 편집기에 라이브 미리 보기 패널을 자동으로 추가합니다. 믹스인을 상속하는 것 외에도 모델은 get_preview_template() 또는 serve_preview() 를 재정의해야 합니다. 예를 들어, Advert 스니펫은 다음과 같이 미리 보기 가능하게 만들 수 있습니다.
# ...
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 템플릿:
<!DOCTYPE html>
<html>
<head>
<title>{{ object.text }}</title>
</head>
<body>
<a href="{{ object.url }}">{{ object.text }}</a>
</body>
</html>
기본 컨텍스트에서 사용할 수 있는 변수는 request(가짜 HttpRequest 객체) 및 object(스니펫 인스턴스)입니다. 컨텍스트를 사용자 지정하려면 get_preview_context() 메서드를 재정의할 수 있습니다.
기본적으로 serve_preview 메서드는 요청 객체, get_preview_template 에서 반환된 템플릿, get_preview_context 에서 반환된 컨텍스트 객체를 사용하여 렌더링되는 TemplateResponse 를 반환합니다. 렌더링 및/또는 라우팅 논리를 사용자 지정하려면 serve_preview 메서드를 재정의할 수 있습니다.
페이지와 유사하게 preview_modes 속성을 재정의하여 여러 미리 보기 모드를 정의할 수 있습니다. 예를 들어, 다음 Advert 스니펫에는 두 가지 미리 보기 모드가 있습니다.
# ...
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
스니펫 검색 가능하게 만들기¶
스니펫 모델이 사용자 지정 모델 색인화에 설명된 대로 wagtail.search.index.Indexed 에서 상속하는 경우 Wagtail은 해당 스니펫 유형에 대한 선택기 인터페이스에 검색 상자를 자동으로 추가합니다. 예를 들어, Advert 스니펫은 다음과 같이 검색 가능하게 만들 수 있습니다.
# ...
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'),
]
스니펫 개정 저장¶
스니펫 모델이 RevisionMixin 에서 상속하는 경우 Wagtail은 스니펫 관리자에서 변경 사항을 저장할 때 자동으로 개정을 저장합니다.
믹스인은 스니펫 인스턴스의 모든 개정에 대한 쿼리셋을 제공하는 revisions 속성을 정의합니다. 또한 Revision 모델에 대한 기본 GenericRelation 이 함께 제공되어 스니펫 인스턴스가 삭제될 때 개정이 올바르게 정리됩니다.
기본 GenericRelation 에는 related_query_name 이 없으므로 Revision 모델에서 스니펫 모델로 쿼리하고 필터링할 수 없습니다. 이 기능이 필요한 경우 사용자 지정 related_query_name 으로 자체 GenericRelation 을 정의할 수 있습니다.
자세한 내용은 기본 GenericRelation _revisions 및 속성 revisions 를 참조하십시오.
Added in version 7.1: 기본 GenericRelation _revisions 가 추가되었습니다.
예를 들어, Advert 스니펫은 다음과 같이 개정 가능하게 만들 수 있습니다.
# ...
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의 ManyToManyField 를 사용하여 관계를 정의하는 경우 모델 클래스를 django.models.Model 대신 modelcluster.models.ClusterableModel 에서 상속하도록 변경하고 ManyToManyField 를 ParentalManyToManyField 로 바꿔야 합니다. 인라인 모델은 ForeignKey 대신 ParentalKey 를 계속 사용해야 합니다. 이는 관계를 개정에 저장할 수 있도록 하는 데 필요합니다. 자세한 내용은 튜토리얼의 작성자 섹션을 참조하십시오. 예를 들어:
# ...
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 모델 인스턴스를 생성합니다. 개정 인스턴스는 편집 작업의 감사 로그 항목에 첨부되어 스니펫 기록 페이지에서 이전 개정으로 되돌리거나 개정 간의 변경 사항을 비교할 수 있습니다.
{meth}~wagtail.models.RevisionMixin.save_revision메서드를 호출하여 프로그래밍 방식으로 개정을 저장할 수도 있습니다. 믹스인을 적용한 후latest_revision` 필드가 데이터베이스 테이블에 채워지도록 이미 존재하는 스니펫 인스턴스(있는 경우)에 대해 이 메서드를 (또는 관리자에서 스니펫을 저장) 한 번 이상 호출하는 것이 좋습니다.
예약된 게시 기능을 사용하는 경우 publish_scheduled 관리 명령을 주기적으로 실행해야 합니다. 자세한 내용은 예약된 게시을 참조하십시오.
스니펫 인스턴스를 게시하려면 스니펫 모델에 대한 publish 권한이 필요합니다. DraftStateMixin 이 적용된 모델의 경우 Wagtail은 해당 publish 권한을 자동으로 생성하고 Wagtail 관리자 인터페이스의 ‘그룹’ 영역에 표시합니다. 권한을 구성하는 방법에 대한 자세한 내용은 권한를 참조하십시오.
경고
Wagtail은 아직 게시되지 않은(“초안”) 스니펫을 페이지에 포함하는 것을 막는 메커니즘이 없습니다. DraftStateMixin 이 활성화된 스니펫을 페이지에 포함할 때 초안 스니펫이 어떻게 렌더링되어야 하는지 처리하기 위한 필요한 검사를 추가해야 합니다(예: live 필드 확인). 향후 이를 개선할 계획입니다.
스니펫 초안 변경 사항 저장¶
스니펫 모델이 DraftStateMixin 에서 상속하는 경우 Wagtail은 스니펫 관리자에서 변경 사항을 초안으로 저장하고 별도로 게시할 수 있는 기능을 자동으로 추가합니다. 이 믹스인은 RevisionMixin 이 적용되어야 합니다.
예를 들어, Advert 스니펫은 다음과 같이 초안 변경 사항을 지원하도록 만들 수 있습니다:
# ...
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 관리자 인터페이스의 ‘그룹’ 영역에 표시합니다. 권한을 구성하는 방법에 대한 자세한 내용은 권한를 참조하십시오.
경고
Wagtail은 아직 게시되지 않은(“초안”) 스니펫을 페이지에 포함하는 것을 막는 메커니즘이 없습니다. DraftStateMixin 이 활성화된 스니펫을 페이지에 포함할 때 초안 스니펫이 어떻게 렌더링되어야 하는지 처리하기 위한 필요한 검사를 추가해야 합니다(예: live 필드 확인). 향후 이를 개선할 계획입니다.
스니펫 잠금¶
스니펫 모델이 LockableMixin 에서 상속하는 경우 Wagtail은 모델 인스턴스를 잠글 수 있는 기능을 자동으로 추가합니다. 편집할 때 Wagtail은 “상태” 사이드 패널에 잠금 정보를 표시하고, 사용자가 잠금/잠금 해제 권한이 있는 경우 인스턴스를 잠금/잠금 해제하는 버튼을 표시합니다.
모델이 예약된 게시를 갖도록 구성된 경우(위의 스니펫 초안 변경 사항 저장 참조) Wagtail은 게시 예정인 모든 인스턴스를 잠급니다.
페이지와 유사하게 스니펫을 잠근 사용자는 WAGTAILADMIN_GLOBAL_EDIT_LOCK 이 True 로 설정되지 않는 한 여전히 편집할 수 있습니다.
예를 들어, Advert 스니펫 인스턴스는 다음과 같이 정의하여 잠글 수 있습니다.
# ...
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 관리자 인터페이스의 ‘그룹’ 영역에 표시합니다. 권한을 구성하는 방법에 대한 자세한 내용은 권한를 참조하십시오.
스니펫에 워크플로 활성화¶
스니펫 모델이 WorkflowMixin 에서 상속하는 경우 Wagtail은 모델에 워크플로를 할당하는 기능을 자동으로 추가합니다. 스니펫 모델에 워크플로가 할당되면 편집기에 “중재를 위해 제출” 및 기타 워크플로 작업 메뉴 항목이 표시됩니다. 상태 사이드 패널에는 현재 워크플로에 대한 정보도 표시됩니다.
WorkflowMixin 은 Wagtail의 개정 및 게시 메커니즘을 사용하므로 이 믹스인을 상속하려면 RevisionMixin 및 DraftStateMixin 도 상속해야 합니다. 또한 LockableMixin 에서 상속하여 잠금을 활성화하는 것이 좋습니다. 이렇게 하면 스니펫 인스턴스가 워크플로에 있을 때 잠겨서 검토자만 편집할 수 있습니다. 자세한 내용은 위 섹션을 참조하십시오.
믹스인은 스니펫 인스턴스의 모든 워크플로 상태에 대한 쿼리셋을 제공하는 workflow_states 속성을 정의합니다. 또한 WorkflowState 모델에 대한 기본 GenericRelation 이 함께 제공되어 스니펫 인스턴스가 삭제될 때 워크플로 상태가 올바르게 정리됩니다.
기본 GenericRelation 에는 related_query_name 이 없으므로 WorkflowState 모델에서 스니펫 모델로 쿼리하고 필터링할 수 없습니다. 이 기능이 필요한 경우 사용자 지정 related_query_name 으로 자체 GenericRelation 을 정의할 수 있습니다.
자세한 내용은 기본 GenericRelation _workflow_states 및 속성 workflow_states 를 참조하십시오.
Added in version 7.1: 기본 GenericRelation _workflow_states 가 추가되었습니다.
예를 들어, Advert 스니펫에 워크플로(잠금 포함)를 다음과 같이 정의하여 활성화할 수 있습니다.
# ...
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 관리 명령을 실행해야 합니다.
믹스인을 활성화한 후 워크플로 설정을 통해 스니펫 모델에 워크플로를 할당할 수 있습니다. 자세한 내용은 중재를 위한 워크플로 구성 방법을 참조하십시오.
관리자 대시보드 및 워크플로 보고서에는 워크플로에 제출된 스니펫(페이지와 함께)도 표시됩니다.
스니펫 태그 지정¶
스니펫에 태그를 추가하는 것은 페이지에 태그를 추가하는 것과 매우 유사합니다. 유일한 차이점은 RevisionMixin 이 적용되지 않은 경우 modelcluster.contrib.taggit.ClusterTaggableManager 대신 taggit.manager.TaggableManager 를 사용해야 한다는 것입니다.
# ...
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'),
]
페이지 태그 지정에 대한 문서에는 뷰에서 태그를 사용하는 방법에 대한 자세한 정보가 있습니다.
스니펫 내 인라인 모델¶
페이지와 유사하게 스니펫 내에 다른 모델을 중첩할 수 있습니다. 이를 위해서는 스니펫 모델이 django.models.Model 대신 modelcluster.models.ClusterableModel 에서 상속해야 합니다.
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")
]
페이지에서 인라인 모델을 사용하는 방법에 대한 문서는 스니펫에도 적용되는 더 많은 정보를 제공합니다.