Рубрики
Без рубрики

¿Cómo Crag un Историальный de productos VisitaDos Con Django Y Redis?

Estás Navegando en uncommerce, un producto llama tu atención y haces Нажмите PARA VERLO, NO TE ALL … Теги с Джанго, учебником, испанским, Python.

Estás Navegando ru ООН Ecommerce, Llama Llama un Producto Tu atención y Нажмите Para Verlo, нет TE Convence. Решит от Otrass Otrass Opciones, гасс щелчок en un nuevo producto y, Cuando Hages Scroll Al Fondo de la Página, La Página Te Muestra EL Primer Producto Que Viste БАЙДЕ ЛЕ ЛЕЙЕНДА “ВИСТОС Recientemente”. Tú поуэдит Хейсеру Л.М. МИММО У Джанго Й Редис.

django y redis.

Agrogar Una Sección de Productos VisitaDos Aummerce Las Ventas en ecommerce y mantiene Al Usuario Más Tiempo En La Página. ES Normal Añadir Este Historial A US USUARIO Que Ya Está en la Base de datos. Los Encargados de la Página Web Tienen ООН истории de los productos que vemos, los que compramos, cuanto tiempo pasamos viéndolos y mouseos otros datos pero … ¿y los usuarios annónimos que no tienen una cuenta?

Историал де Сирта Пагина де электронной коммерции que ya Нет Necesita Más Pubouldididad.

A lo mejor no te interesa (o tu empresa) Tener Guardados en una base de ditos elistorial de millones de productos visitados por cada usuario anónimo que visita el sitio, Перо Аун Así Te Gustaría Mastrarle CADA USUARIO, REGIGNADO O NO, LOS Productos que ha visto.

Redis es en motor de base de datos muy eficatione, trabaja con datos volátiles, Pues Almacena Su Información en Memoria, POR LO Que Su Acceso Es Casi Instantáneo, Aunque Volátil. Sin Embargo ES Positible Volcar SU Información A un Medio Parstary, COMO MySQL, Postgres U Otra Base DE DECOS, СОПОРМЕНЕНИЯ Отказ Seguramente PodeMos Usar Redis Pero … ¿Cómo Vamos Diferenciar Usuario Anónimo de Otro?

HAHE MOSSAS MANERAS DE ABRORDAR ESE SCRELYA, PUEDES ASOCIAR US USUARIO (Y SU ИСТОРИИ) CONA Cookie, IP O Hasta Unace Defiliado и т. Д. EL TIPO DE DETO QUE DESSEES VINCUL CONEDEDEE DE LAS Intenciones del Negocio. Para Este EJEMPLO USAREMOS SESSARE SESSION KEY DEL Sistema de Sesiones que ya viene incluido en django de manera predeterminada.

Instalar Redis EN GNU/Linux

Antes de Empezar A USAR Django Y Redis Hay Que Instalar Este último En Nuestro Sistema Operativo GNU/Linux. Si no tienes ni Idey de los comandos básicos en antero antero linux te sugiero Visitar Mi Entrada Que Habla de Los Comandos Más Comunes.

sudo apt install redis-server
redis-server

Instalar Redis Para Pishon

Continuación Vamos A Instalar El Paquete Que Vincula Redis Con Python.

pip install redis

EN EL Archivo settings.py De Nuestra Appleación, Agrogamos Los Valores Por Defecto Que Vamos Usar. ESTOS PuEDEN SER DIFERENTES SI TU SERVISTOR DE REDIS ESTA EN OTRA UBICACION O SI ELEVISTE OTRO PUERTO EN LUGAR DEL PREDETEMINADO.

# settings.py
# ...
REDIS_HOST = 'localhost'
REDIS_PORT = 6379
REDIS_DB = 0

Además le pediremos a django que harde La Sesión Con Cada Petición Y Nos Aseguraremos Que Esté Activeo El Mardware Para Sesiones.

# settings.py
MIDDLEWARES = [
    'django.contrib.sessions.middleware.SessionMiddleware',
    # ...
]
# ...
SESSION_SAVE_EVERY_REQUEST = True

Para Este Ejemplo Uso Un Modelo Llamado Продукт, Pero Tu Peedes Sustituirlo POR EL EQUIVALENTE RU TU ALPACACIón.

 # app/models.py
from django.db import models

class Product(models.Model):
    # ...

Si имеет Llegado Hasta Aquí, Pero No Tienes Idey De Como Funciona Django Tengo Unas EntraDas Donde Reseño Un Par de Libros que Pueden Servirte: El libro indepitivo de django (Gratuito) o Django по примеру

Eligiendo el valor que usaremos como llave en django y redis

Primero Hay Que Elegir El Momento En Que Redis Guardará Nuestro Acceso Al Producto. La Vista que devualve los утолщёны de un producto sería lo идеально. De esta manera, cada que unsuario del sitio web acceda a los detalles del producto agregaremos la información del Producto AdiledAdor de Usuario.

# app/views.py
from .models import Product
from django.shortcuts import get_object_or_404

def product_details(request, product_id):
    products = Product.objects.all() # O el queryset que prefieras
    product = get_object_or_404(products, product_id)
    # ... más código 

Primeramente, Vamos A obtener El objeto Cuyos разветвляет Estamos Conssorado, Para Eso Usamos get_object_or_404 Al Que Le Pasamos un Queryset o un modelo y el ID Que Buscará.

Ahora Vamos a Crage Una Serie de Funciones Para Facilitar Nuestro Trabajo, Puedes Crarlas en Archivo Searado, Yo Le lllamaré usils.py.

Conectar Redis Y Python

En El Archivo de Utils.py Vamos Ettome Enty Ena Conexión Entre Python Y Redis. El Método Strictredis Recibirá Los Valores, Estos Son Los Mismos Que Escesificalamos En Nuestro Archivo de Configuración, POR LO Que Podemos Importarlos Directamente de Ahí.

# app/utils.py
import redis
from django.conf import settings

r = redis.StrictRedis(host=settings.REDIS_HOST,
port=settings.REDIS_PORT,
db=settings.REDIS_DB)

CREAR undeificator de usaario para usar como llave en redis

Queremos asociar cada llave de redis un usuario, por lo que, necesitamos una función que nos devuelva una manera de Идентифицирован Cada Usuario de Nuestra Página. PARA USUARIOS ANONIMOS LO IDEAL SERIA USAR SESSAR SESSION SESSION, SI QUEREMOS ELCOR USUARIOS CON CUETO PODEMOS ASOCIARLOS DIREATAMESTE CON SU USUARIO.

Nuestra Función Guarda La Sesión Si No Essee Una session_key. de esta manera nos aseguraremos de siempre contar con una. La Función nos devolverá la Session_key Si El Usuario es Anónimo O El Usuario Si Este Ya Esta Loggeado.

Para Esto Es Necesario Que reciba El Objeto запрос COMO ARDUCTO.

# app/utils.py

def get_user_id_for_redis(request):
    if not request.session.session_key:
        request.session.save()
    return request.session.session_key if request.user.is_anonymous else request.user

Gravelar Valores en redis con lpush o rpush

La Manera en la que redis los datos es vinculándolos con una llave, esa llave tiene una lista asociada que será la que antendrá la información.

Es bastante похожая un diccionario que tiene una lista como доблесть. El Equivalente En Código Python Se Vería Más o Menos así:

{"id_de_usuario_unico_1": [34, 22, 100, 5, 6], "id_de_usuario_unico_2": [112, 444, 3]}

Los números que googenaremos serán los id o llaves primarias de los productos.

Para Penirle a redis que extienda esa lista por El Commartio Usaremos Lpush Отказ

Эль Методо Lpush заливка; El Nombre de la llave que Grougaremos, Como Primer Purging; ООН Valor Que Agregará A Al Inicio Lista de Valores Asociada, Como Segundo Puranto. En caso de que La Llave No Essea, La Creará. Además Lpush Retorna El Tamaño de la lista asociada a lay llave que que que le pasamos como pragingo. El Método Rpush Hace Lo Mismo Pero Por El Final.

Sabiendo Esto Creemos Una Función Que Tome unsuario y un und de producto y se los pase redis para que los garde, nuestra función recornará el valor que devualve Lpush Отказ

# app/utils.py

def create_product_history_by_user(user_id, product_id):
    product_history_length = r.lpush(user_id, product_id)
    return product_history_length

Continuanno Con La Analogía Annoherior, Lpush Haría Algo Parecido Esto.

{"id_de_usuario_unico_1": [34, 22, 100, 5, 6]}
r.lpush("id_de_usuario_unico_1", 1000) 
{"id_de_usuario_unico_1": [1000, 34, 22, 100, 5, 6]}

Obtener una lista de valores asociada a una llave con lrange

Ahora Que Ya Hemos Creado Una Función Para Extender Una lista Asociada A unsuario, Creamos Una Función Que nos devuelva esa lista.

USEMOS лишать

Эль Методо lrange NOS PASARLE UNA LLAVE ( user_id ) y nos devolverá la lista que tiene asignada , Desde El Valor Eniacy (0), Hasta El Final (4), Contando Desde El Commaribied. ES DECIR, LOS ELEALION CON INDICS DEL 0 AL 4. COMO Нет Queremos Repetir Valores, Recurriremos Una Установить понимание Para Transformar Los Valores en Enterop.

# app/utils.py

def get_products_ids_by_user(user_id):
    last_viewed_products_ids = r.lrange(user_id, 0, -1) # Devuelve [b'2', b'4', ...]
    last_viewed_products_ids_list = {int(id) for id in last_viewed_products_ids}
    return last_viewed_products_ids_list

ESTA Función Nos Devolverá únicamente Esos Valores de Redis, Para Que Podamos Saber Que Productos Retornaremos de la Base de Datos.

Creando un Queryset Partir de Valores de Redis

La Función Ansiory Nos Devualve Una Lista de Valores, переписывает ID O Llaves Primarias de Productos En Nuestra Base de Datos.

USAREMOS ESA LISTA DE VALORES PARA FILTRAR Productos EN Nuestra Base DE DETOS, UN USO BASTANTE COMúN DEL ORM DE DJANGO CON EL QUE NO DEBERYAS TENERAS.

BásiCamente COOTA: COUTÉN TODOS LOS Productos Y Luego Fíltralos de Manera Que Solo Queden Aquellos Productos Cuyo I d o llave primaria se encientre en la lista llamada product_ids.

# app/utils.py
from .models import Product

def get_product_history_queryset_by_user(product_ids, product_id):
    last_viewed_products_queryset = Product.objects.all().filter(
        id__in=product_ids).exclude(id=product_id)
    return last_viewed_products_queryset

Toma en cuenta que puedes sustituir la query Product.Objects.all () пункт La Queryset Que Tú. Quizás Prefieras no masturar todos los productos ru tu base de ditos, Sino Solo Los Activeos, Los Que Tengan Inventario O Custerquier Otra Combinación.

Evitemos Harritar Валкомреорки Repetidos

COMO No Queremos que en la la lista de productos vistos se repitan productos, vamos a asgurarnaros de que el id o llave primaria del producto no se Encuentre en la lista que estamos obteniendo angregarlo.

Para Hacerlo Solo Revisamos que EL ID del Producto SE Entuentre Fuera de la lista que nos rovesea la función get_products_ids_by_user que escribimos insormente.

# app/views.py
from .models import Product
from django.shortcuts import get_object_or_404

def product_details(request, product_id):
    # ...
    user_id = get_user_id_for_redis(request)
    product_ids = get_products_ids_by_user(user_id)
    if int(product_id) not in product_ids:
        create_product_history_by_user(user_id, product_id)
    product_history_queryset = get_product_history_queryset_by_user(product_ids)
    # ... más código 

Sin Embargo Ahora Nos Topamos Con Otro Проблема, Que Pasa Si Nuestra Tienda Tiene Decenas de Male de Productos y nuestro trafico diario son decenas de miles de usuarios, ¿de verdad queremos mantener en memoria una lista tan grande de productos visitados? La Mayoría de Los Usuarios Нет Revisarán Sus últimos Mil Productos Para Ver Si Se Les Antoja Comprar Allgo.

ES Innecesario Mantener Una Lista Tan Larga Si Solo Vamos Acceder A anced a unos cuantos productos.

¿Cómo Lo Solucionamos? Necesitamos Crage Una Función Que Nos Ayude Mantener La Lista de productos Asociados A una llave en redis en límite.

RPOP Y LPOP de Redis Remueven Un Elemento de Una lista

El Método RPOP SE Encarga de Remover El último Elemento de Una losa asociada a una llave y lo devualve. EL MÉTODO LPOP HACE LO MIMSO, PERO CON EL PRIMER ELEVELEO

PodeMos USAR RPOP PARA PARA REMOVER EL ELEVENTO Más Antiguo E IR DEPURANDO LOS ELEVELES Más Viejos. Si Con La última Incerción La Lista Crece Más Allá de Nuestro Limite (En Este Caso 5) Quitaremos El Elemento Más Antiguo.

# app/utils.py
from .models import Product

def limit_product_history_length(user_id, product_history_length):
    if product_history_length > 5:
        r.pop(user_id)

ltrim es una альтернатива y rpop y lpop y

La Función ЛТРИМ de redis se encarga de cortar los valores siveres de losa asociada a una llave Le indimos El índice Seiacty Y SU INDICE Final Como Argingos.

La Diferencia Que Tiene Con Ltrim Es Que Su Tiempo de Ejecución Es O (n), Puesto Ceence Coneed Coneedover, MientArs Que Para RPOP ES DE O (1). Si no tienes Idea de que te estoy hablando tissa Mi entrada donde hablo un poco sobre la notación большой o o quédate con la Idea de que si solo vamos a Eliminar un remention rpop es mejor. Sin Embargo Puede Que quieras un comportamiento diferente y sirva más usar ловкий .

Redis Tiene información del Tiempo de Ejecución de Cada Función en do Documentación Y Pude Ser Muy útil Si El Rendimiento de Tu Alplicación de django Es.

# app/utils.py
from .models import Product

def limit_product_history_length(user_id, product_history_length):
    if product_history_length > 5:
        r.ltrim(user_id, 0, 5)

Ahora Agrogamos La Función Para Que Se ejecute SOLO Si Ha Habido Una Inserción de un premento en redis, es decir, si el producto Фактический без SE Encuentra En Nuestra Lista.

# app/views.py
from .models import Product
from django.shortcuts import get_object_or_404

def product_details(request, product_id):
    # ...
    user_id = get_user_id_for_redis(request)
    product_ids = get_products_ids_by_user(user_id)
    if product_id not in product_ids:
        product_history_length = create_product_history_by_user(user_id, product_id)
        limit_product_history_length(user_id, product_history_length)
    product_history_queryset = get_product_history_queryset_by_user(request.user, product_ids)
    # ... más código 

AHORA QUE TENEMOS EL QUERYSET CON LOS DETOS DE REDIS, PODEMOS RECORNARLO Y RENDERIZARLO EN UNA PLOTILLA DE DE DJANGO, PRECESARLO PARA DEVOLVER UNA RESSUESTA JSON o lo que tu queplación requeivera.

# app/views.py
from django.shortcuts import get_object_or_404
from django.template.response import TemplateResponse


def product_details(request, product_id):
    # ...
    context = {"visited_products": visited_products} 
    return TemplateResponse(
        request, 'product/details.html', context) 

Asignar Una Fecha de Expiración a los datos en redis

Перо, que tal si No Nos Interesa Que Tantos Productos Vea un Cliente Anónimo, Sino El Tiempo Que Los Guardamos.

Quizás Hoy El Clients quiera Comprar Comprar Producto En Esperial, Pero A Lo Mejor Solidamos Inútil MaStarle ESE Mismo Producto Tres Meses Después. ¿ Побуждение Нет ponerle una fecha de expiración a la lista que estamos guardando? Si El Usuario Нет Vuelve Visager Nuevo Producto Tras Transcurrir Cierta Cantidad de Tiempo Se Borrará La Información.

R.Expire оставлять La llave que queremos que Caduque y El Tiempo Para Su Eliminación, En Enes Orden, Como Sus Argugeos. Para Este Ejemplo Le He He asignado Tres Meses. Y así se Иран Борряндо Лос Историал Де Лас-Сезиойс Инактивас Квер Хан Эстадо Инактивас Пор Ларго Tiempo.

# app/utils.py
from .models import Product

def create_product_history_by_user(user_id, product_id):
    product_history_length = r.lpush(user_id, product_id)
    r.expire(user_id, 60*60*24*90)
    return product_history_length

Redis Tiene Mucho Para ofrecer, Y Vincularlo Con Django Te Permiterá Hacer Luceo. Тэхо Ла Documentación de Redis POR SI Q QUIERES PROFUNDIZAR ELLE ELLA Y SUS Привязки en python.

Оригинал: “https://dev.to/silicosis/como-crear-un-historial-de-productos-visitados-con-django-y-redis-291l”