Configuración de Wagtail CMS en Django

Para iniciar un proyecto con Wagtail, se requiere un entorno virtual de Python 3 y la insatlación del paquete a través de pip. La estructura inicial del proyecto se genera mediante el comando wagtail start, seguido de la ejecución de migraciones y creación de un usuario administrador.

# Crear entorno aislado con Python 3
python -m venv entorno_wagtail

# Instalar el framework
pip install wagtail

# Inicializar estructura del proyecto
wagtail start mi_portal
cd mi_portal
pip install -r requirements.txt
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver

La localización al idioma chino simplificado se configura en el archivo settings/base.py, estableciendo la zona horaria de Shanghái. Se recomienda desactivar el uso de zonas horarias en base de datos (USE_TZ = False) si no se requiere soporte para múltiples zonas.

# Configuración regional en settings/base.py
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False

Para el sistema de publicaciones, se definen modelos jerárquicos que heredan de Page. El modelo principal del índice incluye un campo de introducción enriquecido y un método personalizado de contexto para ordenar las publicaciones descendientemente por fecha.

from django.db import models
from modelcluster.fields import ParentalKey
from wagtail.core.models import Page, Orderable
from wagtail.core.fields import RichTextField
from wagtail.admin.edit_handlers import FieldPanel, InlinePanel
from wagtail.images.edit_handlers import ImageChooserPanel
from wagtail.search import index


class SectionIndex(Page):
    description = RichTextField(blank=True)

    content_panels = Page.content_panels + [
        FieldPanel('description', classname="full")
    ]

    class Meta:
        verbose_name = "Sección principal"
        verbose_name_plural = "Secciones principales"

    def get_context(self, request):
        ctx = super().get_context(request)
        articles = self.get_children().live().order_by('-first_published_at')
        ctx['published_articles'] = articles
        return ctx


class ArticlePage(Page):
    publication_date = models.DateField("Fecha de publicación")
    source = models.CharField("Fuente", max_length=250)
    content = RichTextField("Contenido principal", blank=True)
    attachment = models.FileField("Adjunto multimedia", blank=True, upload_to="uploads")

    search_fields = Page.search_fields + [
        index.SearchField('source'),
        index.SearchField('content'),
    ]

    class Meta:
        verbose_name = "Artículo"
        verbose_name_plural = "Artículos"

    content_panels = Page.content_panels + [
        FieldPanel('publication_date'),
        FieldPanel('source'),
        InlinePanel('media_gallery', label="Galería de medios"),
        FieldPanel('attachment'),
        FieldPanel('content', classname="full"),
    ]

    def primary_image(self):
        first_item = self.media_gallery.first()
        return first_item.media_file if first_item else None


class MediaItem(Orderable):
    parent_page = ParentalKey(ArticlePage, on_delete=models.CASCADE, related_name='media_gallery')
    media_file = models.ForeignKey(
        'wagtailimages.Image', on_delete=models.CASCADE, related_name='+'
    )
    description = models.CharField('Descripción', blank=True, max_length=250)

    panels = [
        ImageChooserPanel('media_file'),
        FieldPanel('description'),
    ]

Las plantillas se estructuran heredando de un template base. En la página principal, se muestra un mapa interactivo incrustado y listas de categorías de contenido. Los artículos se presentan mediante tarjetas que incluyen imagen principal, extracto y enlace a la publicación completa.

{% extends "base.html" %}
{% load wagtailcore_tags wagtailimages_tags %}

{% block content %}
<div class="container-fluid">
  <div class="map-container text-center my-4">
    <iframe src="https://example.com/map/" 
            width="90%" height="600" frameborder="0">
    </iframe>
  </div>

  <div class="row mt-4">
    {% for article in published_articles %}
    <div class="col-md-4 mb-4">
      <div class="card h-100">
        {% with article.primary_image as main_img %}
          {% if main_img %}
            {% image main_img fill-400x300 as thumb %}
            <img src="{{ thumb.url }}" class="card-img-top" alt="">
          {% endif %}
        {% endwith %}
        <div class="card-body">
          <h5 class="card-title">
            <a href="{% pageurl article %}">{{ article.title }}</a>
          </h5>
          <p class="card-text">
            {{ article.source|richtext|truncatewords_html:30 }}
          </p>
        </div>
      </div>
    </div>
    {% endfor %}
  </div>

  <div class="row mt-4">
    <div class="col-md-6">
      <h4>Documentos relevantes</h4>
      <ul class="list-group">
        {% for item in documents_list %}
        <a href="{% pageurl item %}" class="list-group-item list-group-item-action">
          {{ item.title }}
        </a>
        {% endfor %}
      </ul>
    </div>
  </div>
</div>
{% endblock %}

La plantilla de listado de artículos incluye manejo condicional de medios: muestra imágenes si existen, o reproductores de video para archivos adjuntos de tipo video. Los extractos se truncarán para mantener la vista concisa.

{% extends "base.html" %}
{% load wagtailcore_tags wagtailimages_tags %}

{% block content %}
<div class="container">
  {% for item in published_articles %}
    <div class="article-preview mb-5">
      <h2><a href="{% pageurl item %}">{{ item.title }}</a></h2>
      <p class="text-muted">
        {{ item.publication_date }} - {{ item.source }}
      </p>
      
      {% if item.primary_image %}
        {% image item.primary_image width-800 %}
      {% elif item.attachment %}
        <div class="ratio ratio-16x9">
          <video controls class="embed-responsive-item">
            <source src="{{ item.attachment.url }}" type="video/mp4">
            Su navegador no soporta la etiqueta de video.
          </video>
        </div>
      {% endif %}
      
      <div class="mt-3">
        {{ item.content|richtext|truncatewords_html:50 }}
      </div>
    </div>
  {% endfor %}
</div>
{% endblock %}

Para la vista detallada de artículos, se implementan múltiples tipos de medios: imágenes de cabecera, videos con controles nativos y galería de imágenes con leyendas. Se incluyen navegación contextual para regresar a la sección padre y botón de impresión.

{% extends "base.html" %}
{% load wagtailcore_tags wagtailimages_tags %}

{% block content %}
<article class="container py-5">
  <header class="mb-5 text-center">
    {% image page.cover_image width-1200 as hero_img %}
    <img src="{{ hero_img.url }}" class="img-fluid rounded mb-4" alt="">
    <h1>{{ page.title }}</h1>
    <div class="d-flex justify-content-center gap-4 text-muted">
      <span>Publicado: {{ page.publication_date }}</span>
      <span>Fuente: {{ page.source }}</span>
    </div>
  </header>

  <div class="content-body">
    {{ page.content|richtext }}
    
    {% if page.attachment %}
      <div class="my-4 ratio ratio-21x9">
        <video controls class="rounded">
          <source src="{{ page.attachment.url }}" type="video/mp4">
          <source src="{{ page.attachment.url }}" type="video/webm">
          Formato no soportado. Por favor actualice su navegador.
        </video>
      </div>
    {% endif %}

    {% if page.media_gallery.all %}
      <section class="gallery mt-5">
        <h3 class="mb-4">Galería de imágenes</h3>
        <div class="row">
          {% for media in page.media_gallery.all %}
          <div class="col-md-6 mb-4">
            {% image media.media_file width-1000 %}
            <p class="mt-2 text-center">{{ media.description }}</p>
          </div>
          {% endfor %}
        </div>
      </section>
    {% endif %}
  </div>

  <footer class="mt-5 pt-4 border-top text-center">
    <a href="{{ page.get_parent.url }}" class="btn btn-outline-secondary me-3">
      Volver al listado
    </a>
    <button onclick="window.print()" class="btn btn-outline-primary">
      Imprimir
    </button>
  </footer>
</article>
{% endblock %}

Etiquetas: wagtail Django Python templates models

Publicado el 7-26 14:46