jueves, 28 de marzo de 2024

30.- Representación y Caché del Contenido (diferente contenido, cache de datos)

 Representación y Caché del Contenido


En el post anterior, utilizamos la herencia de modelos y las relaciones genéricas para crear modelos de contenido de curso flexibles. Implementamos un campo de modelo personalizado y construimos un sistema de gestión de cursos utilizando vistas basadas en clases. Finalmente, creamos una funcionalidad de arrastrar y soltar JavaScript utilizando solicitudes HTTP asíncronas para ordenar los módulos del curso y su contenido.

En este capítulo, construirás la funcionalidad para acceder al contenido del curso, crear un sistema de registro de estudiantes y gestionar la inscripción de estudiantes en los cursos. También veremos cómo almacenar en caché datos utilizando el marco de trabajo de caché de Django.


En este post abarcaremos lo siguiente:


- Crear vistas públicas para mostrar información de cursos

- Construir un sistema de registro de estudiantes

- Gestionar la inscripción de estudiantes en cursos

- Representar contenido diverso para los módulos del curso

- Instalar y configurar Memcached

- Almacenar en caché contenido utilizando el marco de trabajo de caché de Django

- Utilizar los backends de caché de Memcached y Redis

- Monitorear tu servidor Redis en el sitio de administración de Django

Comencemos creando un catálogo de cursos para que los estudiantes puedan buscar cursos existentes e inscribirse en ellos.

Puedes encontrar el código fuente para este capítulo en https://github.com/PacktPublishing/Django-4-by-example/tree/main/Chapter14.


- Mostrando los Cursos


Para nuestro catálogo de cursos, necesitaremos implementar las siguientes funcionalidades:

- Listar todos los cursos disponibles, opcionalmente filtrados por materia.

- Mostrar una visión general de un solo curso.


Para lograr esto, edita el archivo views.py de la aplicación de cursos y agrega el siguiente código:

Elearning/educa/courses/views.py

from django.db.models import Count
from .models import Subject

class CourseListView(TemplateResponseMixin, View):
    model = Course
    template_name = 'courses/course/list.html'

    def get(self, request, subject=None):
        subjects = Subject.objects.annotate(
            total_courses=Count('courses'))
        courses = Course.objects.annotate(
            total_modules=Count('modules'))
        if subject:
            subject = get_object_or_404(Subject, slug=subject)
            courses = courses.filter(subject=subject)
        return self.render_to_response({'subjects': subjects,
                                        'subject': subject,
                                        'courses': courses})

Esta es la vista CourseListView. Hereda de TemplateResponseMixin y View. En esta vista, realizas las siguientes tareas:

1. Recuperas todas las materias, utilizando el método annotate() del ORM con la función de agregación Count() para incluir el número total de cursos para cada materia.

2. Recuperas todos los cursos disponibles, incluyendo el número total de módulos contenidos en cada curso.

3. Si se proporciona un parámetro de URL slug de materia, recuperas el objeto de materia correspondiente y limitas la consulta a los cursos que pertenecen a la materia dada.

4. Utilizas el método render_to_response() proporcionado por TemplateResponseMixin para renderizar los objetos en una plantilla y devolver una respuesta HTTP.

Ahora, creemos una vista detallada para mostrar una visión general de un solo curso. Agrega el siguiente código al archivo views.py:

Elearning/educa/courses/views.py

from django.views.generic.detail import DetailView

class CourseDetailView(DetailView):
    model = Course
    template_name = 'courses/course/detail.html'
Esta vista hereda de DetailView genérica proporcionada por Django. Especificamos los atributos model y template_name. La DetailView de Django espera un parámetro de URL de clave primaria (pk) o slug para recuperar un único objeto para el modelo dado. La vista renderiza la plantilla especificada en template_name, incluyendo el objeto Course en la variable de contexto de la plantilla object.

Edita el archivo principal urls.py del proyecto educa y agrega el siguiente patrón de URL a él:

Elearning/educa/educa/urls.py

from courses.views import CourseListView

urlpatterns = [
# ...
path('', CourseListView.as_view(), name='course_list'),
]

Añadimos el patrón de URL `course_list` al archivo principal urls.py del proyecto porque queremos mostrar la lista de cursos en la URL http://127.0.0.1:8000/, y todas las demás URLs para la aplicación de cursos tienen el prefijo /course/.

Edita el archivo urls.py de la aplicación courses y añade los siguientes patrones de URL:

Elearning/educa/courses/urls.py

path('subject/<slug:subject>/',
    views.CourseListView.as_view(),
    name='course_list_subject'),
path('<slug:slug>/',
    views.CourseDetailView.as_view(),
    name='course_detail'),

Defines los siguientes patrones de URL:

- course_list_subject: Para mostrar todos los cursos para una materia.

- course_detail: Para mostrar una vista general de un solo curso.

Ahora, construyamos las plantillas para las vistas CourseListView y CourseDetailView.

Crea la siguiente estructura de archivos dentro del directorio templates/courses/ de la aplicación courses:

```

courses/

    course/

        list.html

        detail.html

```

Edita la plantilla courses/course/list.html de la aplicación courses y escribe el siguiente código:

Elearning/educa/courses/templates/courses/course/list.html

{% extends "base.html" %}
{% block title %}

{% if subject %}
{{ subject.title }} courses
{% else %}
All courses
{% endif %}
{% endblock %}
{% block content %}
<h1>
    {% if subject %}
    {{ subject.title }} courses
    {% else %}
    All courses
    {% endif %}
</h1>
<div class="contents">
    <h3>Subjects</h3>
    <ul id="modules">
        <li {% if not subject %}class="selected" {% endif %}>
            <a href="{% url 'course_list' %}">All</a>
        </li>
        {% for s in subjects %}
        <li {% if subject == s %}class="selected" {% endif %}>
            <a href="{% url 'course_list_subject' s.slug %}">
                {{ s.title }}
                <br>
                <span>
                    {{ s.total_courses }} course{{ s.total_courses|pluralize }}
                </span>
            </a>
        </li>
        {% endfor %}
    </ul>
</div>
<div class="module">
    {% for course in courses %}
    {% with subject=course.subject %}
    <h3>
        <a href="{% url 'course_detail' course.slug %}">
            {{ course.title }}
        </a>
    </h3>

    <p>
        <a href="{% url 'course_list_subject' subject.slug %}">{{ subject }}</a>.
        {{ course.total_modules }} modules.
        Instructor: {{ course.owner }}
    </p>
    {% endwith %}
    {% endfor %}
</div>
{% endblock %}

Asegúrate de que ninguna etiqueta de plantilla se divida en múltiples líneas.

Esta es la plantilla para listar los cursos disponibles. Creamos una lista HTML para mostrar todos los objetos Subject y construyes un enlace a la URL course_list_subject para cada uno de ellos. También incluyes el número total de cursos para cada materia y utilizas el filtro de plantilla pluralize para añadir un sufijo plural a la palabra "curso" cuando el número es diferente de 1, para mostrar 0 cursos, 1 curso, 2 cursos, etc. Añades una clase HTML seleccionada para resaltar la materia actual si una materia está seleccionada. Iteras sobre cada objeto Course, mostrando el número total de módulos y el nombre del instructor.

Ejecuta el servidor de desarrollo y abre http://127.0.0.1:8000/ en tu navegador. Deberías ver una página similar a la siguiente:


mostrando la lista de todos los cursos

La barra lateral izquierda contiene todas las materias, incluyendo el número total de cursos para cada una de ellas. Puedes hacer clic en cualquier materia para filtrar los cursos mostrados.

Edita la plantilla courses/course/detail.html y añade el siguiente código:

Elearning/educa/courses/templates/courses/course/detail.html

{% extends "base.html" %}
{% block title %}
{{ object.title }}
{% endblock %}
{% block content %}
{% with subject=object.subject %}
<h1>
    {{ object.title }}
</h1>
<div class="module">
    <h2>Overview</h2>
    <p>
        <a href="{% url 'course_list_subject' subject.slug %}">
            {{ subject.title }}</a>.
        {{ object.modules.count }} modules.
        Instructor: {{ object.owner }}
    </p>
    {{ object.overview|linebreaks }}
</div>
{% endwith %}
{% endblock %}

En esta plantilla, se muestra la descripción general y los detalles de un solo curso. Abre http://127.0.0.1:8000/ en tu navegador y haz clic en uno de los cursos. Deberías ver una página con la siguiente estructura:

The course overview page


Añadiendo un registro para los estudiantes.


Para agregar el registro de estudiantes, primero necesitas crear una nueva aplicación en tu proyecto Django. Puedes hacerlo ejecutando el siguiente comando en tu terminal:

python manage.py startapp students

Esto creará una nueva aplicación llamada "students" en tu proyecto Django. Una vez que la aplicación se haya creado, podrás comenzar a agregar las vistas, modelos y plantillas necesarias para manejar el registro de estudiantes y la inscripción en cursos.

Edita el archivo settings.py del proyecto educa y registra la aplicación de la siguiente forma:

/home/chema/PycharmProjects/Elearning/educa/educa/settings.py

INSTALLED_APPS = [
# ...
'students.apps.StudentsConfig',
]


Crear una vista para el registro de los estudiantes.

Edita el archivo views.py de la aplicación students y añade el siguiente código.

Elearning/educa/students/views.py

from django.shortcuts import render
from django.urls import reverse_lazy
from django.views.generic.edit import CreateView
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth import authenticate, login

# Create your views here.

class StudentRegistrationView(CreateView):
    template_name = 'students/student/registration.html'
    form_class = UserCreationForm
    success_url = reverse_lazy('student_course_list')
    def form_valid(self, form):
        result = super().form_valid(form)
        cd = form.cleaned_data
        user = authenticate(username=cd['username'], password=cd['password1'])
        login(self.request, user)
        return result

Esta es la vista que permite a los estudiantes registrarse en tu web. Utilizamos el módulo genérico CreateView, el cual nos proporciona la funcionalidad para crear modelos de objetos. Esta vista necesita los siguientes atributos:

  • template_name - la ruta de la plantilla que renderizará esta vista.
  • form_class - el formulario para crear los objetos, el cual tiene que heradar de ModelForm. Usaremos UserCreationForm como el formulario de registro para crear los objetos.
  • success_url - la URL a la que redirigir al usuario cuando el formulario haya sido enviado exitosamente. Para ello utilizaremos 'student_course_list' que construiremos en breve para mostrar los cursos en los que está apuntado un estudiante.
El método form_valid se ejecuta cuando un formulario que sea válido ha sido enviado. Sobreescribimos este método para loguear al usuario después de que haya sido satisfactoriamente registrado.

Crea un nuevo archivo dentro de la aplicación students llamado urls.py. Añadele el siguiente código:

Elearning/educa/students/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('register/',
         views.StudentRegistrationView.as_view(),
         name='student_registration'),
]

A continuación edita el archivo urls.py de la aplicación principal para registrar este patrón URLs de la siguiente manera.

Elearning/educa/educa/urls.py

urlpatterns = [
# ...
path('students/', include('students.urls')),
]

Crea la siguiente estructura de directorios dentro de la aplicación students.

templates /

        students /

                student/

                        registration.html

Edita este último archivo creado y añade el siguiente código:

Elearning/educa/students/templates/students/student/registration.html

{% extends "base.html" %}
{% block title %}
Sign up
{% endblock %}
{% block content %}
<h1>
    Sign up
</h1>
<div class="module">
    <p>Enter your details to create an account:</p>
    <form method="post">
        {{ form.as_p }}
        {% csrf_token %}
        <p><input type="submit" value="Create my account"></p>
    </form>
</div>
{% endblock %}

Ejecuta el servidor de desarrollo y abre la siguiente dirección http://127.0.0.1:8000/students/register/ en el navegador. Deberías ver un formulario de registro como el que se ve a continuación.


The student registration form

Ten en cuenta que el URL student_course_list especificado en al atributo success_url de la vista StudentRegistrationView aun no existe. Si enviaste el formulario, Django no podrá encontrar la URL  a la que redirigirte después de registrarte con éxito. (La crearemos más adelante)


Apuntándose a los cursos.


Después de que los usuarios creen una cuenta, tenemos que hacer que se puedan apuntar a los cursos que deseen. Para ello necesitaremos crear una relación many to many entre los modelos Course y User, ya que un usuario puede apuntarse a varios cursos y a la vez en un curso pueden apuntarse diferentes estudiantes.

Edita el archivo models.py de la aplicación courses y añade el siguiente campo al modelo Course.

Elearning/educa/courses/models.py

students = models.ManyToManyField(User, related_name='courses_joined', blank=True)

Desde el shell ejecuta el siguiente comando para realizar la migración de este cambio:

python manage.py makemigrations

y luego ejecuta la migración con:

python manage.py migrate

Ahora podemos asociar cada estudiante con el curso al que se haya apuntado. Creemos el código para que los usuarios puedan apuntarse a los cursos.

Crea un nuevo archivo dentro de la aplicación students y llámalo forms.py. Añade el siguiente código:

Elearning/educa/students/forms.py

from django import forms
from courses.models import Course

class CourseEnrollForm(forms.Form):
    course = forms.ModelChoiceField(queryset=Course.objects.all(),
                                    widget=forms.HiddenInput)

Vamos a usar este formulario para que los estudiantes se puedan apuntar a los cursos. El campo course se usará para seleccionar el curso que quiere apuntarse el usuario. Por tanto tiene que ser un campo ModelChoiceField. Usaremos un widget HiddenInput porque no mostraremos este campo al usuario. Usaremos este formulario en la vista CourseDetailView que mostrará un botón para apuntarse al curso.

Edita el archivo views.py de la aplicación students y añade el siguiente código.

Elearning/educa/students/views.py

from django.views.generic.edit import FormView
from django.contrib.auth.mixins import LoginRequiredMixin
from .forms import CourseEnrollForm

class StudentEnrollCourseView(LoginRequiredMixin,
                              FormView):
    course = None
    form_class = CourseEnrollForm

    def form_valid(self, form):
        self.course = form.cleaned_data['course']
        self.course.students.add(self.request.user)
        return super().form_valid(form)

    def get_success_url(self):
        return reverse_lazy('student_course_detail', args=[self.course.id])

Esta es la vista StudentEnrollCourseView. Manejará el proceso de que los estudiantes se apunten al curso deseado. La vista hereda de LoginRequiredMixin por lo que solamente los usuarios que estén logueados pueden acceder a la vista. También hereda de la vista FormView ya que maneja el envío del formulario. También usamos el formulario CourseEnrollForm para el atributo form_class y también el atributo course para guardar posteriormente el objeto Course. Cuando el formulario es válido, se añade el usuario actual a los estudiantes apuntados al curso.

El método get_success_url() devuelve la URL a la que se redirigirá al usuario si el formulario fue enviado exitosamente. Este método es equivalente al atributo success_url. Luego, inviertes la URL llamada student_course_detail.

Edita el archivo urls.py de la aplicación students y añade el siguiente patrón URLs:

Elearning/educa/students/urls.py

path('enroll-course/',
         views.StudentEnrollCourseView.as_view(),
         name='student_enroll_course'),

Agreguemos el formulario del botón de inscripción a la página de descripción general del curso. Edita el archivo views.py de la aplicación courses y modifica CourseDetailView para que tenga el siguiente aspecto:

Elearning/educa/courses/views.py

from students.forms import CourseEnrollForm

class CourseDetailView(DetailView):
    model = Course
    template_name = 'courses/course/detail.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['enroll_form'] = CourseEnrollForm(
            initial={'course': self.object})
        return context

Utilizamos el método get_context_data() para incluir el formulario de inscripción en el contexto para la representación de las plantillas. Inicializamos el campo de curso oculto del formulario con el objeto de curso actual para que se puede enviar directamente.

Edita la plantilla courses/course/detail.html y localiza la siguiente línea:

{{ object.overview|linebreaks }}

Remplázala con el siguiente código:

Elearning/educa/courses/templates/courses/course/detail.html

{{ object.overview|linebreaks }}
    {% if request.user.is_authenticated %}
    <form action="{% url " student_enroll_course" %}" method="post">
        {{ enroll_form }}
        {% csrf_token %}
        <input type="submit" value="Enroll now">
    </form>
    {% else %}
    <a href="{% url 'student_registration' %}" class="button">
        Register to enroll
    </a>

Este código crea el botón para la inscripción en los cursos. Si el usuario está autentificado, se mostrará el botón de inscripción, incluyendo el formulario oculto que apunta a la URL student_enroll_course. Si no lo está, se mostrará un link para registrarte en la plataforma.

Asegúrate de que tienes en funcionamiento el servidor de desarrollo, abre la dirección http://127.0.0.1:8000/ en el navegador y haz clic en un curso. Si te has logueado verás el botón ENROLL NOW situado justo debajo de la descripción del curso, como puedes ver en la imagen siguiente:


The course overview page, including an ENROLL NOW button

Si no estás logueado verás el botón REGISTER TO ENROLL en su lugar.


Accediendo al contenido de los cursos.


Vamos a necesitar una vista para mostrar los cursos en los que los estudiantes están inscritos y una otra vista para acceder al contenido actual del curso. Edita el archivo views.py de la aplicación students y añade el siguiente código.

Elearning/educa/students/views.py

from django.views.generic.list import ListView
from courses.models import Course

class StudentCourseListView(LoginRequiredMixin, ListView):
    model = Course
    template_name = 'students/course/list.html'

    def get_queryset(self):
        qs = super().get_queryset()
        return qs.filter(students__in=[self.request.user])
Esta es la vista que nos mostrará los cursos en los que están apuntados los estudiantes. Hereda de LoginRequiredMixin para asegurarnos que solo los alumnos logueados puedan acceder a la vista. También hereda de las vista genéricas, ListView. Nos mostrará una lista de todos los objetos Course. Sobrescribimos el método get_queryset() de la superclase para que nos devuelva solo los cursos en los que el alumno está inscrito; para ello, filtra el QuerySet por el campo ManyToManyField del estudiante.

Continuamos añadiendo más código al mismo archivo:

Elearning/educa/students/views.py

from django.views.generic.detail import DetailView

class StudentCourseDetailView(DetailView):
    model = Course
    template_name = 'students/course/detail.html'

    def get_queryset(self):
        qs = super().get_queryset()
        return qs.filter(students__in=[self.request.user])
        
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # get course object
        course = self.get_object()
        if 'module_id' in self.kwargs:
            # get current module
            context['module'] = course.modules.get(id=self.kwargs['module_id'])
        else:
            # get first module
            context['module'] = course.modules.all()[0]
        return context
Esta es la vista StudentCourseDetailView. Sobreescribes el método get_queryset() para limitar el QuerySet base a los cursos en los que el estudiante está inscrito. También sobreescribes el método get_context_data() para establecer un módulo del curso en el contexto si se proporciona el parámetro de URL module_id. De lo contrario, estableces el primer módulo del curso. De esta manera, los estudiantes podrán navegar a través de los módulos dentro de un curso.

Edita el archivo urls.py de la aplicación de estudiantes y agrega los siguientes patrones de URL a él.

Elearning/educa/students/views.py

    path('courses/',
         views.StudentCourseListView.as_view(),
         name='student_course_list'),
    path('course/<pk>/',
         views.StudentCourseDetailView.as_view(),
         name='student_course_detail'),
    path('course/<pk>/<module_id>/',
         views.StudentCourseDetailView.as_view(),
         name='student_course_detail_module'),

Ahora crea la siguiente estructura de archivos dentro del directorio templates/students/ de la aplicación students:

course/

        detail.html

        list.html

Edita la plantilla students/course/list.html y añade el siguiente código:

Elearning/educa/students/templates/students/course/list.html

{% extends "base.html" %}
{% block title %}My courses{% endblock %}
{% block content %}
<h1>My courses</h1>
<div class="module">
    {% for course in object_list %}
    <div class="course-info">
        <h3>{{ course.title }}</h3>
        <p><a href="{% url 'student_course_detail' course.id %}">
                Access contents</a></p>
    </div>
    {% empty %}
    <p>
        You are not enrolled in any courses yet.
        <a href="{% url 'course_list' %}">Browse courses</a>
        to enroll on a course.
    </p>
    {% endfor %}
</div>
{% endblock %}

Este modelo muestra los cursos en los que el estudiante está inscrito. Recuerda que cuando un nuevo estudiante se registre exitosamente en la plataforma, será redirigido a la URL student_course_list. También redireccionemos a los estudiantes a esta URL cuando inicien sesión en la plataforma. 

Edita el archivo settings.py del proyecto educa y añade el siguiente código:

Elearning/educa/educa/settings.py

from django.urls import reverse_lazy

LOGIN_REDIRECT_URL = reverse_lazy('student_course_list')

Este es el ajuste utilizado por el módulo de autenticación para redirigir al estudiante después de iniciar sesión correctamente si no hay ningún parámetro "next" presente en la solicitud. Después de iniciar sesión correctamente, un estudiante será redirigido a la URL student_course_list para ver los cursos en los que está inscrito. 

Edita la plantilla students/course/detail.html y añade el siguiente código a ella:

Elearning/educa/students/templates/students/course/detail.html

{% extends "base.html" %}

{% block title %}
{{ object.title }}
{% endblock %}

{% block content %}
<h1>
    {{ module.title }}
</h1>
<div class="contents">
    <h3>Modules</h3>
    <ul id="modules">
        {% for m in object.modules.all %}
        <li data-id="{{ m.id }}" {% if m == module %}class="selected" {% endif %}>
            <a href="{% url 'student_course_detail_module' object.id m.id %}">
                <span>
                    Module <span class="order">{{ m.order|add:1 }}</span>
                </span>
                <br>
                {{ m.title }}
            </a>
        </li>
        {% empty %}
        <li>No modules yet.</li>
        {% endfor %}
    </ul>
</div>
<div class="module">
    {% for content in module.contents.all %}
    {% with item=content.item %}
    <h2>{{ item.title }}</h2>
    {{ item.render }}
    {% endwith %}
    {% endfor %}
</div>
{% endblock %}
Asegúrate de que ninguna etiqueta de plantilla esté dividida en varias líneas. Esta es la plantilla para que los estudiantes inscritos accedan al contenido de un curso. Primero, construyes una lista HTML que incluye todos los módulos del curso y resalta el módulo actual. Luego, iteras sobre el contenido del módulo actual y accedes a cada elemento de contenido para mostrarlo usando {{ item.render }}. A continuación, agregarás el método render() a los modelos de contenido. Este método se encargará de renderizar correctamente el contenido.

Ahora puedes acceder a http://127.0.0.1:8000/students/register/, registrarte como un nuevo estudiante e inscribirte en cualquier curso.


Representar diferentes tipos de contenido.


Para mostrar el contenido del curso, necesitarnos representar los diferentes tipos de contenido que hemos creado, ya se texto, imágenes, videos o un archivo. 

Edita el archivo models.py de la aplicación courses y añade el método render() al modelo ItemBase:

Elearning/educa/courses/models.py

from django.template.loader import render_to_string

class ItemBase(models.Model):
    # ...
    def render(self):
        return render_to_string(
            f'courses/content/{self._meta.model_name}.html',
            {'item': self})
Este método usa la función render_to_string() para representar una plantilla y y delvolver el contenido renderizado como una cadena de texto. Cada tipo de contenido se representa utilizando una plantilla con el nombre del modelo de contenido. Utiliza self._meta.model_name para generar dinámicamente el nombre de la plantilla adecuada para cada modelo de contenido. El método render() proporciona una interfaz común para representar diversos contenidos. 

Crea la siguiente estructura de archivos dentro del directorio templates/courses/ de la aplicación de courses:

content/
        text.html
        file.html
        file.html
        video.html

Edita la plantilla courses/content/text.html y escribe este código:

{{ item.content|linebreaks }}

Esta es la plantilla para mostrar el contenido de texto. El filtro de plantilla linebreaks reemplaza los saltos de línea en texto plano con saltos de línea en html. 

Edita la plantilla courses/content/file.html y añade los siguiente:

<p>
<a href="{{ item.file.url }}" class="button">Download file</a>
</p>

Esta es la plantilla para mostrar los archivos. Generamos un enlace para descargar el archivo.

Edita la plantilla courses/content/image.html y escribe:

<p>
<img src="{{ item.file.url }}" alt="{{ item.title }}">
</p>

Esta es la plantilla para mostrar las imágenes. 

También debemos crear una plantilla para representar objetos de Video. Utilizaremos django-embed-video para incrustar contenido de video. django-embed-video es una aplicación de Django de terceros que te permite incrustar videos en tus plantillas, desde fuentes como YouTube o Vimeo, simplemente proporcionando su URL pública.

Instala el paquete con el siguiente comando:

pip install django-embed-video

Como siempre que se instala una aplicación hay que registrarle, para ello edita el archivo settings.py del proyecto y añade lo siguiente:

Elearning/educa/educa/settings.py

INSTALLED_APPS = [
# ...
'embed_video',
]

Si quieres más información puedes encontrar la documentación de la aplicación en https://django-embed-video.readthedocs.io/en/latest/

Edita la plantilla courses/content/video.html y escribe el siguiente código:

{% load embed_video_tags %}

{% video item.url "small" %}

Esta es la plantilla para renderizar videos.

Ahora, ejecuta el servidor de desarrollo y accede a http://127.0.0.1:8000/course/mine/ en tu navegador. Accede al sitio con un usuario que pertenezca al grupo de Instructores, y añade varios contenidos a un curso. Para incluir contenido de video, simplemente copia cualquier URL de YouTube, como https://www.youtube.com/watch?v=bgV39DlmZ2U, y inclúyela en el campo de la URL del formulario.

Después de añadir contenido al curso, abre http://127.0.0.1:8000/, haz clic en el curso y luego haz clic en el botón ENROLL NOW (Inscribirse ahora). Deberías estar inscrito en el curso y ser redirigido a la URL del detalle del curso para estudiantes. La Figura 14.5 muestra una página de ejemplo de contenidos de curso:


A course contents page

Y con esto ya tenemos una interfaz para mostrar diferentes tipos de contenido.


Utilizando el marco de caché


El procesamiento de solicitudes HTTP a tu aplicación web generalmente implica acceso a la base de datos, manipulación de datos y renderizado de plantillas. Es mucho más costoso en términos de procesamiento que simplemente servir un sitio web estático. El sobrecosto en algunas solicitudes puede ser significativo cuando tu sitio comienza a recibir más tráfico. Aquí es donde la caché se vuelve valiosa. Al almacenar en caché consultas, resultados de cálculos o contenido renderizado en una solicitud HTTP, evitarás operaciones costosas en términos de proceso en las siguientes solicitudes que necesitan devolver los mismos datos. Esto se traduce en tiempos de respuesta más cortos y menos procesamiento en el lado del servidor.

Django incluye un sistema de caché robusto que te permite almacenar en caché datos con diferentes niveles de granularidad. Puedes almacenar en caché una única consulta, la salida de una vista específica, partes del contenido de la plantilla renderizada o todo tu sitio. Los elementos se almacenan en el sistema de caché durante un tiempo predeterminado, pero puedes especificar el tiempo de espera al almacenar en caché los datos.

Así es como normalmente utilizarás el marco de caché cuando tu aplicación procese una solicitud HTTP:

1. Intenta encontrar los datos solicitados en la caché.

2. Si se encuentra, devuelve los datos almacenados en caché.

3. Si no se encuentra, realiza los siguientes pasos:

   1. Realiza la consulta a la base de datos o el procesamiento necesario para generar los datos.

   2. Guarda los datos generados en la caché.

   3. Devuelve los datos.

Puedes obtener información detallada sobre el sistema de caché de Django en https://docs.djangoproject.com/en/5.0/topics/cache/.


Backends de caché disponibles


Django viene con los siguientes backends de caché:

- backends.memcached.PyMemcacheCache o backends.memcached.PyLibMCCache: Backends de Memcached. Memcached es un servidor de caché basado en memoria rápida y eficiente. El backend a utilizar depende de las bibliotecas de enlace de Memcached que elijas.

- backends.redis.RedisCache: Un backend de caché de Redis. Este backend se ha añadido en Django 4.0.

- backends.db.DatabaseCache: Utiliza la base de datos como sistema de caché.

- backends.filebased.FileBasedCache: Utiliza el sistema de almacenamiento de archivos. Esto serializa y almacena cada valor de caché como un archivo separado.

- backends.locmem.LocMemCache: Un backend de caché de memoria local. Este es el backend de caché predeterminado.

- backends.dummy.DummyCache: Un backend de caché falso destinado únicamente para desarrollo. Implementa la interfaz de caché sin almacenar realmente nada en él. Esta caché es por proceso y segura para hilos.

Nota: para un resultado óptimo, usa el cache basado en memoria como Redis o Memcached.


Instalando Memcached.

Es un servidor de cache basado en memoria muy popular y de alto rendimiento. Vamos a utilizar Memcached y el backend de Memcached PyMemcacheCache.


Instalación de la imagen Docker de Memcached

Ejecute el siguiente comando desde la terminal para descargar la imagen Docker de Memcached:

sudo docker pull memcached

Esto descargará la imagen Docker de Memcached en tu máquina local. Si no deseas utilizar Docker, también puedes descargar Memcached desde https://memcached.org/downloads.

Ejecuta el contenedor Docker de Memcached con el siguiente comando de bash:

sudo docker run -it --rm --name memcached -p 11211:11211 memcached -m 64

Memcached se ejecuta por defecto en el puerto 11211. La opción -p se utiliza para publicar el puerto 11211 en la misma interfaz de host. La opción -m se utiliza para limitar la memoria del contenedor a 64 MB. Memcached se ejecuta en memoria y se le asigna una cantidad específica de RAM. Cuando la RAM asignada está llena, Memcached comienza a eliminar los datos más antiguos para almacenar nuevos datos. Si deseas ejecutar el comando en modo desvinculado (en segundo plano de tu terminal), puedes utilizar la opción -d.

Puedes encontrar más información sobre Memcached en https://memcached.org.


Instalación del enlace de Memcached para Python

Después de instalar Memcached, debes instalar un enlace de Memcached para Python. Vamos a instalar pymemcache, que es un cliente Memcached rápido y puro en Python. Ejecuta el siguiente comando en la terminal de bash:

pip install pymemcache

Puedes obtener más información sobre la biblioteca pymemcache en https://github.com/pinterest/pymemcache.

Configuración de la caché en Django

Django proporciona la siguiente configuración de caché:

- CACHES: Un diccionario que contiene todas las cachés disponibles para el proyecto.

- CACHE_MIDDLEWARE_ALIAS: El alias de caché a utilizar para el almacenamiento.

- CACHE_MIDDLEWARE_KEY_PREFIX: El prefijo a utilizar para las claves de caché. Establece un prefijo para evitar colisiones de claves si compartes la misma caché entre varios sitios.

- CACHE_MIDDLEWARE_SECONDS: El número predeterminado de segundos para almacenar en caché las páginas.


El sistema de caché para el proyecto se puede configurar utilizando la configuración CACHES. Esta configuración te permite especificar la configuración para múltiples cachés. Cada caché incluida en el diccionario CACHES puede especificar los siguientes datos:

- BACKEND: El backend de caché a utilizar.

- KEY_FUNCTION: Una cadena que contiene una ruta punteada a un llamable que toma un prefijo, versión y clave como argumentos y devuelve una clave de caché final.

- KEY_PREFIX: Un prefijo de cadena para todas las claves de caché, para evitar colisiones.

- LOCATION: La ubicación de la caché. Dependiendo del backend de caché, esto podría ser un directorio, un host y un puerto, o un nombre para el backend en memoria.

- OPTIONS: Cualquier parámetro adicional que se deba pasar al backend de caché.

- TIMEOUT: El tiempo de espera predeterminado, en segundos, para almacenar las claves de caché. Por defecto, son 300 segundos, que equivalen a 5 minutos. Si se establece en None, las claves de caché no caducarán.

- VERSION: El número de versión predeterminado para las claves de caché. Útil para el versionado de caché.


Agregando Memcached a tu proyecto

Vamos a configurar la caché para tu proyecto. Edita el archivo settings.py del proyecto educa y agrega el siguiente código a él:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',                'LOCATION': '127.0.0.1:11211',
    }
}

Estamos utilizando el backend PyMemcacheCache. Especificamos su ubicación utilizando la notación dirección:puerto. Si tienes múltiples instancias de Memcached, puedes usar una lista para LOCATION.

Hemos configurado Memcached para nuestro proyecto. ¡Comencemos a cachear datos!

Niveles de caché

Django proporciona los siguientes niveles de caché, enumerados aquí en orden ascendente de granularidad:

  • API de caché de bajo nivel: Proporciona la mayor granularidad. Te permite cachear consultas específicas o cálculos.
  • Caché de plantilla: Te permite cachear fragmentos de plantilla.
  • Caché por vista: Proporciona caché para vistas individuales.
  • Caché por sitio: La caché de nivel más alto. Cachéa todo tu sitio.

Antes de implementar la caché, es importante pensar en tu estrategia de caché. Enfócate primero en las consultas o cálculos costosos que no se calculan de manera individual para cada usuario.

Algunas consideraciones importantes para tu estrategia de caché pueden incluir:

1. **Identificar consultas costosas**: Revisa tu código para identificar consultas de base de datos u operaciones computacionales que consumen muchos recursos y que no cambian con frecuencia.

2. **Análisis de patrones de acceso**: Observa cómo los usuarios interactúan con tu aplicación y determina qué datos o páginas se acceden con más frecuencia.

3. **Tiempo de vida de la caché**: Decide cuánto tiempo deseas mantener los datos en la caché antes de que se consideren obsoletos. Esto puede variar según el tipo de datos y la frecuencia de actualización.

4. **Tamaño de la caché**: Considera cuánta memoria o espacio en disco estás dispuesto a asignar para la caché y ajusta en consecuencia.

5. **Invalidación de caché**: Implementa un mecanismo para invalidar la caché cuando los datos subyacentes cambien. Esto asegura que los usuarios siempre vean información actualizada.

6. **Monitoreo y ajuste**: Realiza un seguimiento del rendimiento de la caché y ajusta tu estrategia según sea necesario. Es posible que necesites modificar los tiempos de vida de la caché o la granularidad de la caché según la evolución de tu aplicación.

Comencemos aprendiendo cómo utilizar la API de caché de bajo nivel en tu código Python.

Uso de la API de caché de bajo nivel

La API de caché de bajo nivel te permite almacenar objetos en la caché con cualquier granularidad. Se encuentra en django.core.cache. Puedes importarlo de la siguiente manera:

from django.core.cache import cache

Piensa en tu estrategia de caché antes de implementarla. Enfócate primero en consultas o cálculos costosos que no se calculan a nivel de usuario.

Esto utiliza la caché predeterminada. Es equivalente a caches['default']. También es posible acceder a una caché específica a través de su alias.

Vamos a echar un vistazo a cómo funciona la API de caché. Abre la consola de Django con el siguiente comando:


python manage.py shell

Ejecuta el siguiente código:

>>> from django.core.cache import cache
>>> cache.set('musician', 'Django Reinhardt', 20)

Esto accede al backend de caché predeterminado y utiliza set(key, value, timeout) para almacenar una clave llamada 'musician' con un valor que es la cadena 'Django Reinhardt' durante 20 segundos. Si no especificas un tiempo de espera, Django utiliza el tiempo de espera predeterminado especificado para el backend de caché en la configuración CACHES. Ahora, ejecuta el siguiente código:

>>> cache.get('musician')
'Django Reinhardt'

Recuperas la clave de la caché. Espera 20 segundos y ejecuta el mismo código nuevamente:

>>> cache.get('musician')

Esta vez no se devuelve ningún valor. La clave de caché 'musician' ha caducado y el método get() devuelve None porque la clave ya no está en la caché.

NOTA: Siempre evita almacenar un valor None en una clave de caché porque no podrás distinguir entre el valor real y una falta de coincidencia en la caché. 

Vamos a cachear un QuerySet con el siguiente código:

>>> from courses.models import Subject
>>> subjects = Subject.objects.all()
>>> cache.set('my_subjects', subjects)

Realizas una consulta QuerySet en el modelo Subject y almacenas los objetos devueltos en la clave 'my_subjects'. Ahora, vamos a recuperar los datos en caché:

>>> cache.get('my_subjects')
<QuerySet [<Subject: Matemáticas>, <Subject: Música>, <Subject: Física>, <Subject: Programación>]>

Ahora, vas a cachear algunas consultas en tus vistas. Edita el archivo views.py de la aplicación courses y agrega la siguiente importación:

from django.core.cache import cache

En el método get() de CourseListView, encuentra las siguientes líneas:

subjects = Subject.objects.annotate( total_courses=Count('courses'))

Reemplaza esas líneas con las siguientes:

subjects = cache.get('all_subjects') if not subjects: subjects = Subject.objects.annotate( total_courses=Count('courses')) cache.set('all_subjects', subjects)

En este código, intentas obtener la clave all_subjects de la caché utilizando cache.get(). Esto devuelve None si la clave dada no se encuentra. Si no se encuentra ninguna clave (aún no está en caché o estaba en caché pero ha expirado), realizas la consulta para recuperar todos los objetos de Subject y su número de cursos, y luego cachéas el resultado usando cache.set().


Comprobando las peticiones al cache con Django Debug Toolbar.


Vamos a añadir esta herramienta al proyecto para comprobar el funcionamiento de las peticiones al caché. Si recuerdas ya vimos como se usaba en el post 21.- Barra de Herramientas de Depuración de Django. 

Primeramente lo instalaremos con:

pip install django-debug-toolbar

Luego, como cada vez que se instala una aplicación nueva, la registraremos en la aplicación dentro del archivo settings.py del proyecto:

Elearning/educa/educa/settings.py

INSTALLED_APPS = [
# ...
'debug_toolbar',
]

y en el mismo archivo añade el código resaltado en azul dentro del apartado MIDDELWARE.

Elearning/educa/educa/settings.py

MIDDLEWARE = [
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

Recuerda que DebugToolbarMiddleware debe colocarse antes que cualquier otro middleware, excepto el middleware que codifica el contenido de la respuesta, como GZipMiddleware, que, si está presente, debe colocarse primero.

Agrega las siguientes líneas al final del archivo settings.py:

INTERNAL_IPS = [ '127.0.0.1', ]

Django Debug Toolbar solo se mostrará si tu dirección IP coincide con una entrada en la configuración INTERNAL_IPS.

Edita el archivo urls.py principal del proyecto y agrega el siguiente patrón de URL a urlpatterns:

path('__debug__/', include('debug_toolbar.urls')),

Ejecuta el servidor de desarrollo y abre http://127.0.0.1:8000/ en tu navegador.

Ahora deberías ver la barra de herramientas de depuración de Django en el lado derecho de la página. (Recuerda tener activado el docker de Memcached porque sino no djagno te mostrará un error). Haz clic en "Cache" en el menú lateral. Verás el siguiente panel:


The Cache panel of Django Debug Toolbar including cache requests for CourseListView on a cache miss

Debajo de "Total calls" deberías ver 2. La primera vez que se ejecuta la vista CourseListView hay dos solicitudes de caché. Bajo "Commands" verás que el comando "get" se ha ejecutado una vez, y que el comando "set" también se ha ejecutado una vez. El comando "get" corresponde a la llamada que recupera la clave de caché "all_subjects". Esta es la primera llamada que se muestra en "Calls". La primera vez que se ejecuta la vista, ocurre un fallo de caché porque aún no hay datos en caché. Por eso hay un 1 bajo "Cache misses". Luego, el comando "set" se utiliza para almacenar los resultados del conjunto de consultas "subjects" en la caché utilizando la clave de caché "all_subjects". Esta es la segunda llamada que se muestra en "Calls".

En el menú "SQL" de Django Debug Toolbar, verás el número total de consultas SQL ejecutadas en esta solicitud. Esto incluye la consulta para recuperar todos los temas que luego se almacenan en la caché.


SQL queries executed for CourseListView on a cache miss

Recarga la página en el navegador y haz clic en "Cache" en el menú lateral:


The Cache panel of Django Debug Toolbar, including cache requests for CourseListView view on a cache hit

Ahora, solo hay una solicitud de caché. Debajo de "Total calls" deberías ver 1. Y bajo "Commands" puedes ver que la solicitud de caché corresponde a un comando "get". En este caso, hay un acierto de caché (ver "Cache hits") en lugar de un fallo de caché porque los datos se han encontrado en la caché. Bajo "Calls" puedes ver la solicitud "get" para recuperar la clave de caché "all_subjects".

Consulta el elemento de menú "SQL" de la barra de herramientas de depuración. Deberías ver que hay una consulta SQL menos en esta solicitud. Estás ahorrando una consulta SQL porque la vista encuentra los datos en la caché y no necesita recuperarlos de la base de datos:


SQL queries executed for CourseListView on a cache hit

En este ejemplo, para una solicitud única, lleva más tiempo recuperar el elemento de la caché que el tiempo ahorrado en la consulta SQL adicional. Sin embargo, cuando tienes muchos usuarios accediendo a tu sitio, encontrarás reducciones significativas de tiempo al recuperar los datos de la caché en lugar de acceder a la base de datos, y podrás atender el sitio a más usuarios concurrentes.

Las solicitudes sucesivas a la misma URL recuperarán los datos de la caché. Dado que no especificamos un tiempo de espera al almacenar en caché los datos con `cache.set('all_subjects', subjects)` en la vista CourseListView, se utilizará el tiempo de espera predeterminado (300 segundos por defecto, que son 5 minutos). Cuando se alcanza el tiempo de espera, la siguiente solicitud a la URL generará un fallo de caché, se ejecutará el QuerySet y los datos se almacenarán en caché durante otros 5 minutos. Puedes definir un tiempo de espera predeterminado diferente en el elemento TIMEOUT de la configuración CACHES.


Caché basada en datos dinámicos


A menudo, querrás cachear algo que se basa en datos dinámicos. En estos casos, debes construir claves dinámicas que contengan toda la información necesaria para identificar de manera única los datos en caché.

Edita el archivo views.py de la aplicación courses y modifica la vista CourseListView para que se vea así:

Elearning/educa/courses/views.py

class CourseListView(TemplateResponseMixin, View):
    model = Course
    template_name = 'courses/course/list.html'

    def get(self, request, subject=None):
        # subjects = Subject.objects.annotate(
        #     total_courses=Count('courses'))
        subjects = cache.get('all_subjects')
        if not subjects:
            subjects = Subject.objects.annotate(total_courses=Count('courses'))
            cache.set('all_subjects', subjects)
        all_courses = Course.objects.annotate(
            total_modules=Count('modules'))
        if subject:
            subject = get_object_or_404(Subject, slug=subject)
            key = f'subject_{subject.id}_courses'
            courses = cache.get(key)
            if not courses:
                courses = all_courses.filter(subject=subject)
                cache.set(key, courses)
        else:
            courses = cache.get('all_courses')
            if not courses:
                courses = all_courses
                cache.set('all_courses', courses)
            
        return self.render_to_response({'subjects': subjects,
                                        'subject': subject,
                                        'courses': courses})

En este caso, también se almacenan en caché tanto todos los cursos, como los cursos filtrados por materia. Se utiliza la clave de caché "all_courses" para almacenar todos los cursos si no se proporciona ninguna materia. Si hay una materia, se construye la clave dinámicamente con f'subject_{subject.id}_courses'.

Es importante tener en cuenta que no puedes usar un QuerySet en caché para construir otros QuerySets, ya que lo que has almacenado en caché son realmente los resultados del QuerySet. Por lo tanto, no puedes hacer lo siguiente:

courses = cache.get('all_courses') courses.filter(subject=subject)

En su lugar, debes crear el QuerySet base Course.objects.annotate(total_modules=Count('modules')), que no se ejecutará hasta que se fuerce, y se use para restringir aún más el QuerySet con all_courses.filter(subject=subject) en caso de que los datos no se encuentren en la caché.


Almacenamiento en cache de fragmentos de plantilla.

El almacenamiento en caché de fragmentos de plantilla es un enfoque de nivel superior. Necesitas cargar las etiquetas de caché de plantilla en tu plantilla usando {% load cache %}. Luego, podrás usar la etiqueta de plantilla {% cache %} para almacenar en caché fragmentos de plantilla específicos. Normalmente, usarás la etiqueta de plantilla de la siguiente manera:

{% cache 300 fragment_name %}
...
{% endcache %}

La etiqueta de plantilla {% cache %} tiene dos argumentos requeridos: el tiempo de espera en segundos y un nombre para el fragmento. Si necesitas almacenar en caché contenido que depende de datos dinámicos, puedes hacerlo pasando argumentos adicionales a la etiqueta de plantilla {% cache %} para identificar de manera única el fragmento.

Edita la plantilla de la aplicación students, /students/course/detail.html. Añade el siguiente código en la parte superior, justo después de la etiqueta {% extends %}

{% load cache %}

Luego encuentra las siguientes líneas:

{% for content in module.contents.all %}
    {% with item=content.item %}
        <h2>{{ item.title }}</h2>
        {{ item.render }}
    {% endwith %}
{% endfor %}

Reemplázalas con las siguientes:

Elearning/educa/courses/views.py

{% cache 600 module_contents module %}
    {% for content in module.contents.all %}
        {% with item=content.item %}
            <h2>{{ item.title }}</h2>
            {{ item.render }}
        {% endwith %}
    {% endfor %}
{% endcache %}

Has cachéado este fragmento de plantilla utilizando el nombre `module_contents` y pasas el objeto module actual a él. De esta manera, identificas de forma única el fragmento. Esto es importante para evitar cachear el contenido de un módulo y servir el contenido incorrecto cuando se solicita un módulo diferente.

Si el ajuste USE_I18N está configurado como True, el caché del middleware para el sitio respetará el idioma activo. Si usas la etiqueta de plantilla {% cache %}, debes usar una de las variables específicas de traducción disponibles en las plantillas para lograr el mismo resultado, como {% cache 600 name request.LANGUAGE_CODE %}.


Caché de vistas


Puedes cachear la salida de vistas individuales utilizando el decorador cache_page ubicado en django.views.decorators.cache. El decorador requiere un argumento de tiempo de espera (en segundos).

Vamos a usarlo en las vistas. Edita el archivo urls.py de la aplicación students y agrega la siguiente importación y aplica el decorador cache_page a los patrones de URL student_course_detail y student_course_detail_module, de la siguiente manera:

Elearning/educa/students/urls.py

from django.views.decorators.cache import cache_page

#...
    path('course/<pk>/',
         cache_page(60 * 15)(views.StudentCourseDetailView.as_view()),
         name='student_course_detail'),
    path('course/<pk>/<module_id>/',
         cache_page(60 * 15)(views.StudentCourseDetailView.as_view()),
         name='student_course_detail_module'),

Ahora, el contenido completo devuelto por StudentCourseDetailView se cacheará durante 15 minutos.

Nota: El caché por vista utiliza la URL para construir la clave de caché. Las múltiples URLs que apuntan a la misma vista se cachearán por separado. Esto significa que si tienes varias URLs que apuntan a la misma vista, cada una de ellas generará una entrada de caché independiente en función de su URL específica.


Usando de per-site cache

Para utilizar el caché por sitio, que es el nivel de caché más alto y te permite cachear todo tu sitio, sigue estos pasos:

  1. 1.- Edita el archivo settings.py de tu proyecto.

  2. 2.- Añade las clases UpdateCacheMiddleware y FetchFromCacheMiddleware al ajuste MIDDLEWARE, como se muestra a continuación:

Elearning/educa/educa/settings.py

MIDDLEWARE = [
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',    
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

Recuerda que el middleware se ejecuta en el orden dado durante la fase de solicitud y en orden inverso durante la fase de respuesta. UpdateCacheMiddleware se coloca antes de CommonMiddleware porque se ejecuta durante el tiempo de respuesta, cuando el middleware se ejecuta en orden inverso. FetchFromCacheMiddleware se coloca después de CommonMiddleware intencionalmente porque necesita acceder a los datos de solicitud establecidos por este último.

A continuación, agrega las siguientes configuraciones al archivo settings.py:

CACHE_MIDDLEWARE_ALIAS = 'default'
CACHE_MIDDLEWARE_SECONDS = 60 * 15 # 15 minutos
CACHE_MIDDLEWARE_KEY_PREFIX = 'educa'

En estas configuraciones, utilizas la caché predeterminada para tu middleware de caché y estableces el tiempo de espera global de la caché en 15 minutos. También especificas un prefijo para todas las claves de caché para evitar colisiones en caso de que utilices el mismo backend de Memcached para varios proyectos. Tu sitio ahora cacheará y devolverá contenido en caché para todas las solicitudes GET.

Puedes acceder a las diferentes páginas y verificar las solicitudes de caché utilizando Django Debug Toolbar. El caché por sitio no es viable para muchos sitios porque afecta a todas las vistas, incluso a aquellas que es posible que no desees cachear, como las vistas de gestión donde deseas que los datos se devuelvan desde la base de datos para reflejar los últimos cambios.

Para desactivar el caché por sitio en tu proyecto y mantener las vistas de gestión de contenido para instructores sin ningún caché, puedes comentar las clases UpdateCacheMiddleware y FetchFromCacheMiddleware en la configuración MIDDLEWARE de tu archivo settings.py. Aquí tienes cómo hacerlo:

MIDDLEWARE = [
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    # 'django.middleware.cache.UpdateCacheMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.cache.FetchFromCacheMiddleware',    
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

Comentar estas líneas desactivará el middleware de caché por sitio. Esto significa que las vistas que estaban siendo cacheadas ya no lo estarán. Sin embargo, las vistas que no estaban siendo cacheadas, como las vistas de gestión de contenido para instructores, seguirán sin estar en caché.


Usando de Redis cache backend.


Django 4.0 introdujo un backend de caché de Redis. Vamos a cambiar la configuración para utilizar Redis en lugar de Memcached como backend de caché para el proyecto. Recuerda que ya has utilizado Redis en el post 26, "Construyendo un motor de recomendaciones de productos".

Instala `redis-py` en tu entorno utilizando el siguiente comando:

pip install redis

Luego, edita el archivo `settings.py` del proyecto `educa` y modifica la configuración `CACHES`, de la siguiente manera:

Elearning/educa/educa/settings.py

CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379',
}
}

El proyecto ahora utilizará RedisCache como backend de caché. La ubicación se define en el formato redis:// [host]:[puerto]. Utilizas 127.0.0.1 para apuntar al host local y 6379, que es el puerto predeterminado para Redis. Inicializa el contenedor de Docker de Redis usando el siguiente comando:

docker run -it --rm --name redis -p 6379:6379 redis

Si deseas ejecutar el comando en segundo plano (en modo desacoplado), puedes usar la opción -d.

Ejecuta el servidor de desarrollo y abre http://127.0.0.1:8000/ en tu navegador. Verifica las solicitudes de caché en el panel de Cache de Django Debug Toolbar. Ahora estás utilizando Redis como backend de caché de tu proyecto en lugar de Memcached.


Monitoreo de Redis con Django Redisboard


Puedes monitorear tu servidor Redis usando Django Redisboard. Django Redisboard añade estadísticas de Redis al sitio de administración de Django. Puedes encontrar más información sobre Django Redisboard en https://github.com/ionelmc/django-redisboard.

Instala django-redisboard en tu entorno utilizando el siguiente comando: pip install django-redisboard Instala la biblioteca Python attrs utilizada por django-redisboard en tu entorno con el siguiente comando: pip install attrs Edita el archivo settings.py de tu proyecto y añade la aplicación al ajuste INSTALLED_APPS, de la siguiente manera:

INSTALLED_APPS = [ # ... 'redisboard', ]

Ejecuta el siguiente comando desde el directorio de tu proyecto para ejecutar las migraciones de Django Redisboard:

python manage.py migrate redisboard

Inicia el servidor de desarrollo y abre http://127.0.0.1:8000/admin/redisboard/redisserver/add/ en tu navegador para agregar un servidor Redis para monitorear. En el campo "Etiqueta", ingresa "redis", y en el campo "URL", ingresa redis://localhost:6379/0, como se muestra en la Figura 14.10:


The form to add a Redis server for Django Redisboard in the administration site

Vamos a monitorear la instancia de Redis que se está ejecutando en nuestro host local, la cual utiliza el puerto 6379 y la base de datos Redis numerada como 0. Haz clic en GUARDAR. La información se guardará en la base de datos y podrás ver la configuración y las métricas de Redis en el sitio de administración de Django.


The Redis monitoring of Django Redisboard on the administration site

Puedes encontrar el código del capitulo en este enlace de GITHUB.