আপনার অনুরোধ অনুযায়ী, আমি একটি অত্যন্ত সরলীকৃত Django প্রজেক্ট তৈরি করব। এই প্রজেক্টে শুধুমাত্র একটি অ্যাপ (data_demo) থাকবে, এবং দুটি মডেল (Product ও Order) থাকবে যেখানে কোনো ForeignKey ব্যবহার করা হবে না। প্রতিটি মডেলের জন্য আমরা Django REST Framework ব্যবহার করে ক্লাস-ভিত্তিক CRUD (Create, Retrieve, Update, Delete) ভিউ তৈরি করব এবং দুটি ভিন্ন Redis ইনস্ট্যান্সে ডেটা ক্যাশ করার বিস্তারিত ব্যাখ্যা দেব।
আমরা ধরে নিচ্ছি আপনার দুটি Redis ইনস্ট্যান্স আপনার লোকালহোস্টে যথাক্রমে 6379 এবং 6380 পোর্টে চলছে।
প্রজেক্ট ওভারভিউ:
- একটি Django অ্যাপ:
data_demo - দুটি মডেল:
ProductএবংOrder। কোনোForeignKeyথাকবে না। - ক্যাশিং কৌশল:
Productডেটাredis://127.0.0.1:6379/0এ ক্যাশ হবে (product_cache)।Orderডেটাredis://127.0.0.1:6380/0এ ক্যাশ হবে (order_cache)।- API এন্ডপয়েন্ট: Django REST Framework-এর ক্লাস-ভিত্তিক ভিউ ব্যবহার করে
ProductএবংOrderএর জন্য সম্পূর্ণ CRUD অপারেশন। - বিস্তারিত ব্যাখ্যা:
cache_utils.pyএবংviews.pyএর প্রতিটি ফাংশন এবং ক্লাসের বিস্তারিত ব্যাখ্যা।
ধাপ ১: প্রজেক্ট সেটআপ এবং প্রয়োজনীয় প্যাকেজ ইনস্টল
প্রথমে একটি নতুন প্রজেক্ট ডিরেক্টরি তৈরি করুন এবং প্রয়োজনীয় প্যাকেজগুলো ইনস্টল করুন।
# 1. একটি নতুন ডিরেক্টরি তৈরি করুন
mkdir django_simple_crud_redis
cd django_simple_crud_redis
# 2. একটি ভার্চুয়াল এনভায়রনমেন্ট তৈরি করুন এবং অ্যাক্টিভেট করুন
python -m venv venv
# Windows ব্যবহারকারীদের জন্য:
# venv\Scripts\activate
# macOS/Linux ব্যবহারকারীদের জন্য:
# source venv/bin/activate
# 3. প্রয়োজনীয় প্যাকেজ ইনস্টল করুন
pip install django djangorestframework redis django-redis
ধাপ ২: Django প্রজেক্ট এবং অ্যাপ তৈরি
এখন Django প্রজেক্ট এবং একটি মাত্র অ্যাপ তৈরি করুন।
# 1. Django প্রজেক্ট তৈরি করুন
django-admin startproject my_project .
# 2. অ্যাপ তৈরি করুন
python manage.py startapp data_demo
ধাপ ৩: settings.pyকনফিগারেশন
my_project/settings.py ফাইলটি ওপেন করুন এবং নিম্নলিখিত পরিবর্তনগুলো করুন।
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SECRET_KEY = 'django-insecure-your-secret-key' # এটি একটি র্যান্ডম স্ট্রিং দিয়ে প্রতিস্থাপন করুন
DEBUG = True
ALLOWED_HOSTS = []
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth', # Auth অ্যাপটি রাখা হয়েছে কারণ এটি Django REST Framework এর জন্য প্রয়োজন হতে পারে, যদিও CustomUser ব্যবহার করা হচ্ছে না।
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Third-party apps
'rest_framework',
'django_redis',
# Your custom app
'data_demo',
]
MIDDLEWARE = [
'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',
]
ROOT_URLCONF = 'my_project.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'my_project.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# CACHES: দুটি ভিন্ন Redis ইনস্ট্যান্সের জন্য ক্যাশে কনফিগারেশন।
CACHES = {
"product_cache": { # Product ডেটা ক্যাশ করার জন্য।
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/0", # প্রথম Redis সার্ভার (পোর্ট 6379)।
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"COMPRESSOR": "django_redis.compressors.zlib.ZlibCompressor",
"IGNORE_EXCEPTIONS": True, # Redis ডাউন হলেও অ্যাপ্লিকেশন ক্র্যাশ করবে না।
"TIMEOUT": 300, # 5 মিনিট TTL (Time To Live)। Product ডেটা তুলনামূলকভাবে স্থিতিশীল।
}
},
"order_cache": { # Order ডেটা ক্যাশ করার জন্য।
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6380/0", # দ্বিতীয় Redis সার্ভার (পোর্ট 6380)।
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"COMPRESSOR": "django_redis.compressors.zlib.ZlibCompressor",
"IGNORE_EXCEPTIONS": True,
"TIMEOUT": 60, # 1 মিনিট TTL। Order ডেটা ঘন ঘন পরিবর্তিত হতে পারে।
}
}
}
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'Asia/Dhaka'
USE_I18N = True
USE_L10N = True
USE_TZ = True
STATIC_URL = '/static/'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
ধাপ ৪: মডেল তৈরি (data_demo/models.py)
আপনার data_demo/models.py ফাইলটি নিম্নলিখিত কোড দিয়ে আপডেট করুন। এখানে Product এবং Order মডেল থাকবে, যেখানে কোনো ForeignKey ব্যবহার করা হবে না।
from django.db import models
# Product মডেল: পণ্যের মাস্টার ডেটা ধারণ করে।
# কোনো ForeignKey নেই। product_id একটি ইউনিক আইডেন্টিফায়ার হিসেবে কাজ করবে।
class Product(models.Model):
product_id = models.CharField(max_length=50, unique=True, db_index=True, verbose_name="Product ID")
name = models.CharField(max_length=200, verbose_name="Product Name")
description = models.TextField(blank=True, verbose_name="Description")
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Unit Price")
is_active = models.BooleanField(default=True, verbose_name="Is Active")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = "Product"
verbose_name_plural = "Products"
def __str__(self):
return f"{self.product_id} - {self.name}"
# Order মডেল: অর্ডারের ট্রানজেকশন ডেটা ধারণ করে।
# কোনো ForeignKey নেই। product_id এবং product_name এখানে denormalized field হিসেবে থাকবে।
class Order(models.Model):
order_id = models.CharField(max_length=50, unique=True, db_index=True, verbose_name="Order ID")
product_id = models.CharField(max_length=50, db_index=True, verbose_name="Product ID (Denormalized)")
product_name = models.CharField(max_length=200, verbose_name="Product Name (Denormalized)")
quantity = models.PositiveIntegerField(verbose_name="Quantity")
total_amount = models.DecimalField(max_digits=12, decimal_places=2, verbose_name="Total Amount")
order_date = models.DateField(auto_now_add=True, verbose_name="Order Date")
status = models.CharField(max_length=20, default='pending', verbose_name="Status")
customer_name = models.CharField(max_length=200, blank=True, verbose_name="Customer Name")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = "Order"
verbose_name_plural = "Orders"
def __str__(self):
return f"Order {self.order_id} for {self.product_name}"
ধাপ ৫: সিরিয়ালাইজার তৈরি করুন (data_demo/serializers.py)
Django REST Framework এর ক্লাস-ভিত্তিক ভিউ ব্যবহার করার জন্য সিরিয়ালাইজার অপরিহার্য। data_demo ফোল্ডারে serializers.py নামে একটি নতুন ফাইল তৈরি করুন।
touch data_demo/serializers.py
data_demo/serializers.py:
from rest_framework import serializers
from .models import Product, Order
# ProductSerializer: Product মডেলকে JSON ফরম্যাটে রূপান্তর করার জন্য।
# এটি API অনুরোধ থেকে ডেটা ভ্যালিডেট এবং ডেটাবেসে সেভ করতেও ব্যবহৃত হয়।
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product # কোন মডেলের জন্য সিরিয়ালাইজার তা নির্দিষ্ট করে।
fields = '__all__' # মডেলের সমস্ত ফিল্ড সিরিয়ালাইজ করবে।
# অথবা, আপনি নির্দিষ্ট ফিল্ড তালিকাভুক্ত করতে পারেন:
# fields = ['product_id', 'name', 'description', 'price', 'is_active']
# OrderSerializer: Order মডেলকে JSON ফরম্যাটে রূপান্তর করার জন্য।
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order # কোন মডেলের জন্য সিরিয়ালাইজার তা নির্দিষ্ট করে।
fields = '__all__' # মডেলের সমস্ত ফিল্ড সিরিয়ালাইজ করবে।
# অথবা, আপনি নির্দিষ্ট ফিল্ড তালিকাভুক্ত করতে পারেন:
# fields = ['order_id', 'product_id', 'product_name', 'quantity', 'total_amount', 'status', 'customer_name']
ধাপ ৬: ক্যাশিং ইউটিলিটি ফাংশন (data_demo/cache_utils.py)
আপনার data_demo/cache_utils.py ফাইলটি নিম্নলিখিত কোড দিয়ে আপডেট করুন।
import json
from django.core.cache import caches
from django.conf import settings
def make_cache_key(prefix, *args):
"""
ক্যাশে কী তৈরি করে।
এই ফাংশনটি একটি নির্দিষ্ট প্যাটার্নে ক্যাশে কী তৈরি করে, যা ডেটা টাইপ এবং আইডি দ্বারা গঠিত।
উদাহরণ: make_cache_key('product_detail', 'P001') -> 'product_detail:P001'
:param prefix: ক্যাশে কী এর জন্য একটি স্ট্রিং প্রিফিক্স (যেমন 'product_detail', 'order_list')।
:param *args: কী এর অংশ হিসেবে ব্যবহৃত হতে পারে এমন ভেরিয়েবল আর্গুমেন্ট (যেমন product_id, order_id)।
:return: একটি সম্পূর্ণ ক্যাশে কী স্ট্রিং।
"""
return f"{prefix}:" + ":".join(map(str, args))
def get_from_cache_or_db(cache_key, fetch_func, cache_alias, ttl=None):
"""
নির্দিষ্ট ক্যাশে থেকে ডেটা আনার চেষ্টা করে। যদি ডেটা ক্যাশে না পাওয়া যায় (Cache Miss),
তাহলে fetch_func কল করে ডেটাবেস থেকে ডেটা এনে ক্যাশে রাখে।
:param cache_key: ক্যাশের জন্য ব্যবহৃত ইউনিক কী।
:param fetch_func: একটি কলব্যাক ফাংশন যা ডেটাবেস থেকে ডেটা আনার জন্য ব্যবহৃত হয়।
এই ফাংশনটি কোনো আর্গুমেন্ট নেয় না এবং ডেটা বা None রিটার্ন করে।
এটি ডেটাবেস কোয়েরি শুধুমাত্র তখনই চালায় যখন ক্যাশে মিস হয়।
:param cache_alias: settings.py এ সংজ্ঞায়িত ক্যাশে ইনস্ট্যান্সের নাম (যেমন 'product_cache', 'order_cache')।
:param ttl: ক্যাশে ডেটা কতক্ষণ থাকবে (সেকেন্ডে)। যদি None হয়, তাহলে settings.py থেকে ডিফল্ট TTL ব্যবহার করা হবে।
:return: ক্যাশ থেকে বা ডেটাবেস থেকে প্রাপ্ত ডেটা। যদি ডেটা না পাওয়া যায়, None।
"""
current_cache = caches[cache_alias] # নির্দিষ্ট ক্যাশে ইনস্ট্যান্স অ্যাক্সেস করা।
cached_data = current_cache.get(cache_key) # ক্যাশে থেকে ডেটা আনার চেষ্টা করা।
if cached_data:
print(f"Cache Hit ({cache_alias}) for key: {cache_key}") # কনসোলে ক্যাশ হিট মেসেজ প্রিন্ট করা।
return json.loads(cached_data) # JSON স্ট্রিং থেকে Python অবজেক্টে রূপান্তর করে ডেটা রিটার্ন করা।
print(f"Cache Miss ({cache_alias}) for key: {cache_key}, fetching from DB...") # কনসোলে ক্যাশ মিস মেসেজ প্রিন্ট করা।
data = fetch_func() # ক্যাশে মিস হলে ডেটাবেস থেকে ডেটা আনার জন্য fetch_func কল করা।
if data is not None:
# যদি ttl নির্দিষ্ট না করা হয়, settings.py থেকে ডিফল্ট TTL নেওয়া হয়।
if ttl is None:
ttl = settings.CACHES[cache_alias]['OPTIONS'].get('TIMEOUT', 300)
current_cache.set(cache_key, json.dumps(data), ttl) # ডেটা JSON স্ট্রিং এ রূপান্তর করে ক্যাশে রাখা।
print(f"Data cached ({cache_alias}) for key: {cache_key} with TTL: {ttl}s") # ক্যাশে রাখার মেসেজ প্রিন্ট করা।
return data
def invalidate_cache(cache_key, cache_alias):
"""
নির্দিষ্ট ক্যাশে কী মুছে ফেলে।
যখন ডেটাবেসে কোনো ডেটা আপডেট বা ডিলিট করা হয়, তখন সংশ্লিষ্ট ক্যাশে ডেটা ইনভ্যালিডেট করা হয়
যাতে পরবর্তী অনুরোধে নতুন ডেটা ডেটাবেস থেকে আনা হয়।
:param cache_key: যে ক্যাশে কী মুছে ফেলতে হবে।
:param cache_alias: কোন ক্যাশে ইনস্ট্যান্স থেকে মুছে ফেলতে হবে।
"""
current_cache = caches[cache_alias] # নির্দিষ্ট ক্যাশে ইনস্ট্যান্স অ্যাক্সেস করা।
current_cache.delete(cache_key) # ক্যাশে থেকে নির্দিষ্ট কী এর ডেটা মুছে ফেলা।
print(f"Cache invalidated ({cache_alias}) for key: {cache_key}") # ইনভ্যালিডেশন মেসেজ প্রিন্ট করা।
def set_cache(cache_key, data, cache_alias, ttl=None):
"""
নির্দিষ্ট ক্যাশে সরাসরি ডেটা সেট করে।
এটি এমন পরিস্থিতিতে উপযোগী যেখানে আপনি ডেটাবেস কোয়েরি ছাড়াই সরাসরি ক্যাশে ডেটা রাখতে চান।
:param cache_key: ক্যাশের জন্য ব্যবহৃত কী।
:param data: ক্যাশে রাখার জন্য ডেটা (Python অবজেক্ট)।
:param cache_alias: কোন ক্যাশে ইনস্ট্যান্সে সেট করতে হবে।
:param ttl: ক্যাশে ডেটা কতক্ষণ থাকবে (সেকেন্ডে)।
"""
current_cache = caches[cache_alias] # নির্দিষ্ট ক্যাশে ইনস্ট্যান্স অ্যাক্সেস করা।
if ttl is None:
ttl = settings.CACHES[cache_alias]['OPTIONS'].get('TIMEOUT', 300) # ডিফল্ট TTL ব্যবহার করা।
current_cache.set(cache_key, json.dumps(data), ttl) # ডেটা JSON স্ট্রিং এ রূপান্তর করে ক্যাশে রাখা।
print(f"Data explicitly set in cache ({cache_alias}) for key: {cache_key} with TTL: {ttl}s") # সেট করার মেসেজ প্রিন্ট করা।
def invalidate_all_list_caches(cache_alias, prefix):
"""
নির্দিষ্ট প্রিফিক্স সহ সমস্ত লিস্ট ক্যাশে ইনভ্যালিডেট করে।
যখন একটি নতুন আইটেম তৈরি করা হয় বা একটি আইটেম ডিলিট করা হয়, তখন লিস্ট ভিউ এর ক্যাশে ইনভ্যালিডেট করা প্রয়োজন।
Redis এর KEYS কমান্ড ব্যবহার করে নির্দিষ্ট প্রিফিক্স সহ সমস্ত কী খুঁজে বের করে মুছে ফেলা হয়।
এটি প্রোডাকশনে বড় ডেটাসেটের জন্য পারফরম্যান্স সমস্যা তৈরি করতে পারে, তাই সাবধানে ব্যবহার করা উচিত।
বিকল্প হিসেবে, আপনি একটি নির্দিষ্ট 'list_cache_version' কী ব্যবহার করতে পারেন এবং প্রতিটি লিস্ট ক্যাশে কী এর সাথে এই ভার্সন যোগ করতে পারেন।
যখন লিস্ট ক্যাশে ইনভ্যালিডেট করার প্রয়োজন হয়, তখন শুধু 'list_cache_version' কী এর মান পরিবর্তন করুন।
:param cache_alias: কোন ক্যাশে ইনস্ট্যান্স থেকে মুছে ফেলতে হবে।
:param prefix: ক্যাশে কী এর প্রিফিক্স (যেমন 'product_list', 'order_list')।
"""
current_cache = caches[cache_alias]
# Redis ক্লায়েন্ট অ্যাক্সেস করা।
# django-redis এর get_client() মেথড ব্যবহার করে সরাসরি Redis ক্লায়েন্ট অবজেক্ট পাওয়া যায়।
redis_client = current_cache.get_client()
# KEYS কমান্ড ব্যবহার করে নির্দিষ্ট প্রিফিক্স সহ সমস্ত কী খুঁজে বের করা।
# সতর্কতা: প্রোডাকশনে KEYS কমান্ড ব্যবহার করা এড়িয়ে চলা উচিত কারণ এটি একটি ব্লক করা অপারেশন এবং বড় ডেটাসেটে পারফরম্যান্স সমস্যা করতে পারে।
# এর পরিবর্তে, আপনি একটি হ্যাশ সেট বা সেট ব্যবহার করে ক্যাশে কীগুলো ট্র্যাক করতে পারেন।
keys_to_invalidate = redis_client.keys(f"{prefix}:*")
if keys_to_invalidate:
redis_client.delete(*keys_to_invalidate) # খুঁজে পাওয়া সমস্ত কী মুছে ফেলা।
print(f"Invalidated {len(keys_to_invalidate)} list caches ({cache_alias}) with prefix: {prefix}")
else:
print(f"No list caches found to invalidate ({cache_alias}) with prefix: {prefix}")
ধাপ ৭: API ভিউ তৈরি করুন (data_demo/views.py) – ক্লাস-ভিত্তিক CRUD ভিউ
আপনার data_demo/views.py ফাইলটি নিম্নলিখিত কোড দিয়ে আপডেট করুন। এখানে Product এবং Order মডেলের জন্য ক্লাস-ভিত্তিক API ভিউ থাকবে, যা ভিন্ন ভিন্ন ক্যাশে ব্যবহার করবে।
from rest_framework import generics # Django REST Framework এর জেনেরিক ক্লাস-ভিত্তিক ভিউ ইম্পোর্ট করা হয়েছে।
from rest_framework.response import Response
from rest_framework import status
from django.shortcuts import get_object_or_404
from django.db import transaction
from .models import Product, Order
from .serializers import ProductSerializer, OrderSerializer # সিরিয়ালাইজার ইম্পোর্ট করা হয়েছে।
from .cache_utils import get_from_cache_or_db, invalidate_cache, set_cache, make_cache_key, invalidate_all_list_caches
import json
# ==================== Product Views ====================
class ProductListCreateView(generics.ListCreateAPIView):
"""
Product এর তালিকা দেখায় (GET) এবং নতুন Product তৈরি করে (POST)।
GET অনুরোধের জন্য 'product_cache' ব্যবহার করে।
POST অনুরোধের পর 'product_cache' এর সমস্ত লিস্ট ক্যাশে ইনভ্যালিডেট করে।
"""
queryset = Product.objects.all() # এই ভিউ দ্বারা পরিচালিত কোয়েরিসেট।
serializer_class = ProductSerializer # এই ভিউ দ্বারা ব্যবহৃত সিরিয়ালাইজার।
def list(self, request, *args, **kwargs):
"""
সমস্ত Product এর তালিকা আনে।
ক্যাশে থেকে ডেটা আনার চেষ্টা করে। যদি না পাওয়া যায়, ডেটাবেস থেকে এনে ক্যাশে রাখে।
"""
cache_key = make_cache_key('product_list') # সমস্ত প্রোডাক্টের তালিকার জন্য একটি সাধারণ ক্যাশে কী।
def fetch_products_from_db():
# ডেটাবেস থেকে সমস্ত প্রোডাক্ট আনা হয়।
products = self.get_queryset()
# সিরিয়ালাইজার ব্যবহার করে ডেটা JSON ফরম্যাটে রূপান্তর করা হয়।
# many=True কারণ এটি একটি কোয়েরিসেট (একাধিক অবজেক্ট)।
return ProductSerializer(products, many=True).data
# 'product_cache' ব্যবহার করে ক্যাশ থেকে বা ডেটাবেস থেকে ডেটা আনা হয়।
product_data = get_from_cache_or_db(cache_key, fetch_products_from_db, cache_alias='product_cache')
return Response(product_data)
def create(self, request, *args, **kwargs):
"""
একটি নতুন Product তৈরি করে।
সফলভাবে তৈরি হওয়ার পর 'product_cache' এর সমস্ত প্রোডাক্ট লিস্ট ক্যাশে ইনভ্যালিডেট করে।
"""
serializer = self.get_serializer(data=request.data) # অনুরোধের ডেটা দিয়ে সিরিয়ালাইজার ইনিশিয়ালাইজ করা হয়।
serializer.is_valid(raise_exception=True) # ডেটা ভ্যালিডেট করা হয়। ভ্যালিড না হলে ত্রুটি উত্থাপন করা হয়।
with transaction.atomic(): # ডেটাবেস অপারেশন একটি লেনদেনের মধ্যে রাখা হয়।
self.perform_create(serializer) # সিরিয়ালাইজার ব্যবহার করে ডেটাবেসে নতুন অবজেক্ট তৈরি করা হয়।
# নতুন প্রোডাক্ট তৈরি হওয়ার পর, সমস্ত প্রোডাক্ট লিস্ট ক্যাশে ইনভ্যালিডেট করা হয়।
# এটি নিশ্চিত করে যে পরবর্তী GET অনুরোধে নতুন প্রোডাক্ট সহ আপডেট করা তালিকা ডেটাবেস থেকে আনা হবে।
invalidate_all_list_caches(cache_alias='product_cache', prefix='product_list')
# যদি নির্দিষ্ট প্রোডাক্টের ডিটেইল ক্যাশে থাকে, সেটিও ইনভ্যালিডেট করা যেতে পারে (যদি product_id জানা থাকে)।
# product_id = serializer.instance.product_id
# invalidate_cache(make_cache_key('product_detail', product_id), cache_alias='product_cache')
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
class ProductRetrieveUpdateDestroyView(generics.RetrieveUpdateDestroyAPIView):
"""
একটি নির্দিষ্ট Product এর বিস্তারিত তথ্য দেখায় (GET), আপডেট করে (PUT/PATCH), এবং মুছে ফেলে (DELETE)।
GET অনুরোধের জন্য 'product_cache' ব্যবহার করে।
PUT/PATCH/DELETE অনুরোধের পর 'product_cache' এর সংশ্লিষ্ট ডিটেইল ক্যাশে এবং সমস্ত লিস্ট ক্যাশে ইনভ্যালিডেট করে।
"""
queryset = Product.objects.all()
serializer_class = ProductSerializer
lookup_field = 'product_id' # URL এ product_id ব্যবহার করে অবজেক্ট খুঁজে বের করার জন্য।
def retrieve(self, request, *args, **kwargs):
"""
একটি নির্দিষ্ট Product এর বিস্তারিত তথ্য আনে।
ক্যাশে থেকে ডেটা আনার চেষ্টা করে। যদি না পাওয়া যায়, ডেটাবেস থেকে এনে ক্যাশে রাখে।
"""
product_id = self.kwargs['product_id'] # URL থেকে product_id নেওয়া হয়।
cache_key = make_cache_key('product_detail', product_id) # নির্দিষ্ট প্রোডাক্টের জন্য ক্যাশে কী তৈরি করা হয়।
def fetch_product_from_db():
try:
# ডেটাবেস থেকে নির্দিষ্ট প্রোডাক্ট আনা হয়।
product = self.get_object() # get_object() মেথড lookup_field ব্যবহার করে অবজেক্ট খুঁজে বের করে।
return ProductSerializer(product).data # সিরিয়ালাইজার ব্যবহার করে ডেটা JSON ফরম্যাটে রূপান্তর করা হয়।
except Product.DoesNotExist:
return None
product_data = get_from_cache_or_db(cache_key, fetch_product_from_db, cache_alias='product_cache')
if product_data:
return Response(product_data)
return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)
def update(self, request, *args, **kwargs):
"""
একটি নির্দিষ্ট Product আপডেট করে।
আপডেটের পর সংশ্লিষ্ট ডিটেইল ক্যাশে এবং সমস্ত লিস্ট ক্যাশে ইনভ্যালিডেট করে।
"""
partial = kwargs.pop('partial', False) # PATCH অনুরোধের জন্য আংশিক আপডেট সমর্থন করে।
instance = self.get_object() # আপডেটের জন্য বর্তমান অবজেক্ট আনা হয়।
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
with transaction.atomic():
self.perform_update(serializer) # ডেটাবেসে অবজেক্ট আপডেট করা হয়।
# আপডেটের পর সংশ্লিষ্ট ডিটেইল ক্যাশে ইনভ্যালিডেট করা হয়।
cache_key = make_cache_key('product_detail', instance.product_id)
invalidate_cache(cache_key, cache_alias='product_cache')
# সমস্ত প্রোডাক্ট লিস্ট ক্যাশে ইনভ্যালিডেট করা হয়।
invalidate_all_list_caches(cache_alias='product_cache', prefix='product_list')
return Response(serializer.data)
def destroy(self, request, *args, **kwargs):
"""
একটি নির্দিষ্ট Product মুছে ফেলে।
মুছে ফেলার পর সংশ্লিষ্ট ডিটেইল ক্যাশে এবং সমস্ত লিস্ট ক্যাশে ইনভ্যালিডেট করে।
"""
instance = self.get_object() # মুছে ফেলার জন্য বর্তমান অবজেক্ট আনা হয়।
product_id = instance.product_id # ক্যাশে কী ইনভ্যালিডেট করার জন্য product_id নেওয়া হয়।
with transaction.atomic():
self.perform_destroy(instance) # ডেটাবেস থেকে অবজেক্ট মুছে ফেলা হয়।
# মুছে ফেলার পর সংশ্লিষ্ট ডিটেইল ক্যাশে ইনভ্যালিডেট করা হয়।
cache_key = make_cache_key('product_detail', product_id)
invalidate_cache(cache_key, cache_alias='product_cache')
# সমস্ত প্রোডাক্ট লিস্ট ক্যাশে ইনভ্যালিডেট করা হয়।
invalidate_all_list_caches(cache_alias='product_cache', prefix='product_list')
return Response(status=status.HTTP_204_NO_CONTENT)
# ==================== Order Views ====================
class OrderListCreateView(generics.ListCreateAPIView):
"""
Order এর তালিকা দেখায় (GET) এবং নতুন Order তৈরি করে (POST)।
GET অনুরোধের জন্য 'order_cache' ব্যবহার করে।
POST অনুরোধের পর 'order_cache' এর সমস্ত লিস্ট ক্যাশে ইনভ্যালিডেট করে।
"""
queryset = Order.objects.all()
serializer_class = OrderSerializer
def list(self, request, *args, **kwargs):
"""
সমস্ত Order এর তালিকা আনে।
ক্যাশে থেকে ডেটা আনার চেষ্টা করে। যদি না পাওয়া যায়, ডেটাবেস থেকে এনে ক্যাশে রাখে।
"""
cache_key = make_cache_key('order_list') # সমস্ত অর্ডারের তালিকার জন্য ক্যাশে কী।
def fetch_orders_from_db():
orders = self.get_queryset()
return OrderSerializer(orders, many=True).data
# 'order_cache' ব্যবহার করে ক্যাশ থেকে বা ডেটাবেস থেকে ডেটা আনা হয়।
order_data = get_from_cache_or_db(cache_key, fetch_orders_from_db, cache_alias='order_cache')
return Response(order_data)
def create(self, request, *args, **kwargs):
"""
একটি নতুন Order তৈরি করে।
সফলভাবে তৈরি হওয়ার পর 'order_cache' এর সমস্ত অর্ডার লিস্ট ক্যাশে ইনভ্যালিডেট করে।
"""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
with transaction.atomic():
self.perform_create(serializer)
# নতুন অর্ডার তৈরি হওয়ার পর, সমস্ত অর্ডার লিস্ট ক্যাশে ইনভ্যালিডেট করা হয়।
invalidate_all_list_caches(cache_alias='order_cache', prefix='order_list')
# যদি নির্দিষ্ট অর্ডারের ডিটেইল ক্যাশে থাকে, সেটিও ইনভ্যালিডেট করা যেতে পারে।
# order_id = serializer.instance.order_id
# invalidate_cache(make_cache_key('order_detail', order_id), cache_alias='order_cache')
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
class OrderRetrieveUpdateDestroyView(generics.RetrieveUpdateDestroyAPIView):
"""
একটি নির্দিষ্ট Order এর বিস্তারিত তথ্য দেখায় (GET), আপডেট করে (PUT/PATCH), এবং মুছে ফেলে (DELETE)।
GET অনুরোধের জন্য 'order_cache' ব্যবহার করে।
PUT/PATCH/DELETE অনুরোধের পর 'order_cache' এর সংশ্লিষ্ট ডিটেইল ক্যাশে এবং সমস্ত লিস্ট ক্যাশে ইনভ্যালিডেট করে।
"""
queryset = Order.objects.all()
serializer_class = OrderSerializer
lookup_field = 'order_id' # URL এ order_id ব্যবহার করে অবজেক্ট খুঁজে বের করার জন্য।
def retrieve(self, request, *args, **kwargs):
"""
একটি নির্দিষ্ট Order এর বিস্তারিত তথ্য আনে।
ক্যাশে থেকে ডেটা আনার চেষ্টা করে। যদি না পাওয়া যায়, ডেটাবেস থেকে এনে ক্যাশে রাখে।
"""
order_id = self.kwargs['order_id'] # URL থেকে order_id নেওয়া হয়।
cache_key = make_cache_key('order_detail', order_id) # নির্দিষ্ট অর্ডারের জন্য ক্যাশে কী তৈরি করা হয়।
def fetch_order_from_db():
try:
order = self.get_object()
return OrderSerializer(order).data
except Order.DoesNotExist:
return None
order_data = get_from_cache_or_db(cache_key, fetch_order_from_db, cache_alias='order_cache')
if order_data:
return Response(order_data)
return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)
def update(self, request, *args, **kwargs):
"""
একটি নির্দিষ্ট Order আপডেট করে।
আপডেটের পর সংশ্লিষ্ট ডিটেইল ক্যাশে এবং সমস্ত লিস্ট ক্যাশে ইনভ্যালিডেট করে।
"""
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
with transaction.atomic():
self.perform_update(serializer)
# আপডেটের পর সংশ্লিষ্ট ডিটেইল ক্যাশে ইনভ্যালিডেট করা হয়।
cache_key = make_cache_key('order_detail', instance.order_id)
invalidate_cache(cache_key, cache_alias='order_cache')
# সমস্ত অর্ডার লিস্ট ক্যাশে ইনভ্যালিডেট করা হয়।
invalidate_all_list_caches(cache_alias='order_cache', prefix='order_list')
return Response(serializer.data)
def destroy(self, request, *args, **kwargs):
"""
একটি নির্দিষ্ট Order মুছে ফেলে।
মুছে ফেলার পর সংশ্লিষ্ট ডিটেইল ক্যাশে এবং সমস্ত লিস্ট ক্যাশে ইনভ্যালিডেট করে।
"""
instance = self.get_object()
order_id = instance.order_id
with transaction.atomic():
self.perform_destroy(instance)
# মুছে ফেলার পর সংশ্লিষ্ট ডিটেইল ক্যাশে ইনভ্যালিডেট করা হয়।
cache_key = make_cache_key('order_detail', order_id)
invalidate_cache(cache_key, cache_alias='order_cache')
# সমস্ত অর্ডার লিস্ট ক্যাশে ইনভ্যালিডেট করা হয়।
invalidate_all_list_caches(cache_alias='order_cache', prefix='order_list')
return Response(status=status.HTTP_204_NO_CONTENT)
ধাপ ৮: URL প্যাটার্ন কনফিগার করুন (my_project/urls.py)
my_project/urls.py ফাইলটি ওপেন করুন এবং আপনার অ্যাপের ভিউগুলোর জন্য URL প্যাটার্ন যোগ করুন।
from django.contrib import admin
from django.urls import path, include
from data_demo.views import (
ProductListCreateView,
ProductRetrieveUpdateDestroyView,
OrderListCreateView,
OrderRetrieveUpdateDestroyView
)
urlpatterns = [
path('admin/', admin.site.urls),
# Product APIs
# GET (list all products) and POST (create new product)
path('api/products/', ProductListCreateView.as_view(), name='product_list_create'),
# GET (retrieve single product), PUT/PATCH (update product), DELETE (delete product)
path('api/products/<str:product_id>/', ProductRetrieveUpdateDestroyView.as_view(), name='product_detail_update_delete'),
# Order APIs
# GET (list all orders) and POST (create new order)
path('api/orders/', OrderListCreateView.as_view(), name='order_list_create'),
# GET (retrieve single order), PUT/PATCH (update order), DELETE (delete order)
path('api/orders/<str:order_id>/', OrderRetrieveUpdateDestroyView.as_view(), name='order_detail_update_delete'),
]
ধাপ ৯: অ্যাডমিন প্যানেলে মডেল রেজিস্টার করুন (data_demo/admin.py)
আপনার data_demo/admin.py ফাইলটি নিম্নলিখিত কোড দিয়ে আপডেট করুন।
from django.contrib import admin
from .models import Product, Order
# Product এবং Order মডেলকে Django অ্যাডমিনে রেজিস্টার করা হয়েছে।
admin.site.register(Product)
admin.site.register(Order)
ধাপ ১০: মাইগ্রেশন চালান এবং সুপারইউজার তৈরি করুন
এখন ডেটাবেস মাইগ্রেশন চালান এবং অ্যাডমিন প্যানেলে লগইন করার জন্য একটি সুপারইউজার তৈরি করুন।
# 1. মাইগ্রেশন ফাইল তৈরি করুন
python manage.py makemigrations data_demo
# 2. মাইগ্রেশন চালান
python manage.py migrate
# 3. সুপারইউজার তৈরি করুন (যদি আপনার প্রয়োজন হয়, যদিও এই প্রজেক্টে CustomUser মডেল ব্যবহার করা হচ্ছে না)
# Django REST Framework এর ব্রাউজেবল API এর জন্য এটি সহায়ক হতে পারে।
python manage.py createsuperuser
ধাপ ১১: দুটি Redis ইনস্ট্যান্স চালু করুন (VPS সিমুলেশন)
এই ধাপটি অত্যন্ত গুরুত্বপূর্ণ। আপনাকে আপনার লোকালহোস্টে দুটি ভিন্ন পোর্টে দুটি Redis সার্ভার ইনস্ট্যান্স চালু করতে হবে। এটি দুটি ভিন্ন VPS-এ Redis ইনস্ট্যান্স চালানোর সিমুলেশন।
পদ্ধতি:
- Redis ইনস্টল করুন: আপনার সিস্টেমে Redis ইনস্টল করা না থাকলে, আপনার অপারেটিং সিস্টেম অনুযায়ী ইনস্টল করুন।
- Linux (Ubuntu/Debian):
sudo apt update && sudo apt install redis-server - macOS (Homebrew):
brew install redis - Windows: Redis এর অফিসিয়াল ওয়েবসাইট থেকে MSI installer ডাউনলোড করুন অথবা WSL2 (Windows Subsystem for Linux) ব্যবহার করে Linux এর মতো ইনস্টল করুন।
- প্রথম Redis ইনস্ট্যান্স (পোর্ট 6379):
সাধারণত, Redis ডিফল্টভাবে 6379 পোর্টে চলে। আপনি যদি এটি ব্যাকগ্রাউন্ডে চালাতে চান:
redis-server --daemonize yes
অথবা, যদি আপনি ফোরগ্রাউন্ডে দেখতে চান (টার্মিনাল ব্লক করবে):
redis-server
- দ্বিতীয় Redis ইনস্ট্যান্স (পোর্ট 6380):
দ্বিতীয় ইনস্ট্যান্স চালানোর জন্য, আপনাকে একটি কাস্টম কনফিগারেশন ফাইল ব্যবহার করতে হবে যা একটি ভিন্ন পোর্ট নির্দিষ্ট করে। - একটি নতুন ফাইল তৈরি করুন, যেমন
redis_6380.conf। আপনি এটি আপনার প্রজেক্টের রুট ডিরেক্টরিতে বা অন্য কোনো সুবিধাজনক স্থানে তৈরি করতে পারেন।
# redis_6380.conf
port 6380
daemonize yes
pidfile /var/run/redis_6380.pid
logfile /var/log/redis/redis_6380.log
dbfilename dump_6380.rdb
dir /var/lib/redis/6380 # এই ডিরেক্টরি আপনার সিস্টেমে বিদ্যমান থাকতে হবে এবং Redis এর রাইট পারমিশন থাকতে হবে।
bind 127.0.0.1
timeout 0
databases 16
save 900 1
save 300 10
save 60 10000
# requirepass your_redis_password_6380 # প্রোডাকশনে এটি ব্যবহার করা অত্যন্ত গুরুত্বপূর্ণ।
- এখন এই কনফিগারেশন ফাইল ব্যবহার করে দ্বিতীয় Redis ইনস্ট্যান্স চালু করুন:
redis-server /path/to/your/redis_6380.conf
(এখানে /path/to/your/ আপনার redis_6380.conf ফাইলের আসল পাথ হবে)
- আপনি
redis-cli -p 6379 pingএবংredis-cli -p 6380 pingকমান্ড দিয়ে পরীক্ষা করতে পারেন। উভয়ইPONGরেসপন্স দিলে, দুটি ইনস্ট্যান্স ঠিকভাবে চলছে।
ধাপ ১২: প্রজেক্ট চালান এবং ক্যাশিং পরীক্ষা করুন
এখন Django ডেভেলপমেন্ট সার্ভার চালু করুন।
python manage.py runserver
আপনার ব্রাউজারে http://127.0.0.1:8000/admin/ এ যান এবং সুপারইউজার ক্রেডেনশিয়াল দিয়ে লগইন করুন।
ডেটা যোগ করুন (অ্যাডমিন প্যানেল থেকে):
- Products: কিছু প্রোডাক্ট যোগ করুন (যেমন:
Product ID: P001,Name: T-Shirt,Price: 15.00)। - Orders: কিছু অর্ডার যোগ করুন (যেমন:
Order ID: ORD001,Product ID: P001,Product Name: T-Shirt,Quantity: 10,Total Amount: 150.00)।
ক্যাশিং পরীক্ষা করুন (API এন্ডপয়েন্ট ব্যবহার করে):
আপনি curl কমান্ড বা Postman/Insomnia এর মতো টুল ব্যবহার করে API এন্ডপয়েন্টগুলো পরীক্ষা করতে পারেন। আপনার টার্মিনাল বা সার্ভার কনসোলে Cache Hit বা Cache Miss মেসেজগুলো লক্ষ্য করুন।
১. Product CRUD এবং ক্যাশিং পরীক্ষা (product_cache – 6379):
- নতুন Product তৈরি করুন (POST):
curl -X POST -H "Content-Type: application/json" -d '{"product_id": "P002", "name": "Jeans", "description": "Blue denim jeans", "price": "45.00", "is_active": true}' http://127.0.0.1:8000/api/products/
কনসোলে Invalidated 1 list caches (product_cache) with prefix: product_list দেখতে পাবেন।
- সমস্ত Product এর তালিকা দেখুন (GET – List):
- প্রথমবার (Cache Miss):
curl http://127.0.0.1:8000/api/products/
কনসোলে Cache Miss (product_cache) দেখতে পাবেন।
- দ্বিতীয়বার (Cache Hit):
curl http://127.0.0.1:8000/api/products/
কনসোলে Cache Hit (product_cache) দেখতে পাবেন।
- একটি নির্দিষ্ট Product এর বিস্তারিত তথ্য দেখুন (GET – Retrieve):
- প্রথমবার (Cache Miss):
curl http://127.0.0.1:8000/api/products/P001/
কনসোলে Cache Miss (product_cache) দেখতে পাবেন।
- দ্বিতীয়বার (Cache Hit):
curl http://127.0.0.1:8000/api/products/P001/
কনসোলে Cache Hit (product_cache) দেখতে পাবেন।
- একটি Product আপডেট করুন (PUT/PATCH):
curl -X PUT -H "Content-Type: application/json" -d '{"product_id": "P001", "name": "T-Shirt (Updated)", "description": "Updated description", "price": "16.50", "is_active": true}' http://127.0.0.1:8000/api/products/P001/
কনসোলে Cache invalidated (product_cache) for key: product_detail:P001 এবং Invalidated X list caches (product_cache) with prefix: product_list দেখতে পাবেন।
- একটি Product মুছে ফেলুন (DELETE):
curl -X DELETE http://127.0.0.1:8000/api/products/P002/
কনসোলে Cache invalidated (product_cache) for key: product_detail:P002 এবং Invalidated X list caches (product_cache) with prefix: product_list দেখতে পাবেন।
২. Order CRUD এবং ক্যাশিং পরীক্ষা (order_cache – 6380):
- নতুন Order তৈরি করুন (POST):
curl -X POST -H "Content-Type: application/json" -d '{"order_id": "ORD002", "product_id": "P001", "product_name": "T-Shirt", "quantity": 5, "total_amount": "75.00", "status": "confirmed", "customer_name": "Jane Doe"}' http://127.0.0.1:8000/api/orders/
কনসোলে Invalidated X list caches (order_cache) with prefix: order_list দেখতে পাবেন।
- সমস্ত Order এর তালিকা দেখুন (GET – List):
- প্রথমবার (Cache Miss):
curl http://127.0.0.1:8000/api/orders/
কনসোলে Cache Miss (order_cache) দেখতে পাবেন।
- দ্বিতীয়বার (Cache Hit):
curl http://127.0.0.1:8000/api/orders/
কনসোলে Cache Hit (order_cache) দেখতে পাবেন।
- একটি নির্দিষ্ট Order এর বিস্তারিত তথ্য দেখুন (GET – Retrieve):
- প্রথমবার (Cache Miss):
curl http://127.0.0.1:8000/api/orders/ORD001/
কনসোলে Cache Miss (order_cache) দেখতে পাবেন।
- দ্বিতীয়বার (Cache Hit):
curl http://127.0.0.1:8000/api/orders/ORD001/
কনসোলে Cache Hit (order_cache) দেখতে পাবেন।
- একটি Order আপডেট করুন (PUT/PATCH):
curl -X PATCH -H "Content-Type: application/json" -d '{"status": "shipped"}' http://127.0.0.1:8000/api/orders/ORD001/
কনসোলে Cache invalidated (order_cache) for key: order_detail:ORD001 এবং Invalidated X list caches (order_cache) with prefix: order_list দেখতে পাবেন।
- একটি Order মুছে ফেলুন (DELETE):
curl -X DELETE http://127.0.0.1:8000/api/orders/ORD002/
কনসোলে Cache invalidated (order_cache) for key: order_detail:ORD002 এবং Invalidated X list caches (order_cache) with prefix: order_list দেখতে পাবেন।
এই সেটআপটি আপনাকে Django REST Framework এর ক্লাস-ভিত্তিক ভিউ ব্যবহার করে দুটি স্বাধীন মডেলের জন্য সম্পূর্ণ CRUD অপারেশন এবং দুটি ভিন্ন Redis ইনস্ট্যান্সে ক্যাশিং ও ইনভ্যালিডেশন কৌশল কীভাবে কাজ করে তা বিস্তারিতভাবে বুঝতে সাহায্য করবে।
SuggestionsClose suggestions[data-radix-scroll-area-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-scroll-area-viewport]::-webkit-scrollbar{display:none}Add Integrationক্যাশিং পলিসি অপ্টিমাইজ করুনCelery দিয়ে অ্যাসিঙ্ক্রোনাস ক্যাশিংক্যাশ মনিটরিং সেটআপ করুনScroll leftScroll right