u
This commit is contained in:
parent
300a1efc4e
commit
19907b1e83
10 changed files with 168 additions and 82 deletions
35
lms/accounts/middleware.py
Normal file
35
lms/accounts/middleware.py
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
from django.contrib.auth import get_user_model
|
||||
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
class TransitionManager:
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
# هذه الخطوة تتم قبل وصول الطلب إلى الفيو
|
||||
print("قبل معالجة الطلب")
|
||||
|
||||
# تمرير الطلب إلى الفيو والحصول على الاستجابة
|
||||
response = self.get_response(request)
|
||||
|
||||
# التحقق من وجود المستخدم
|
||||
try:
|
||||
user = User.objects.get(email=request.user.email)
|
||||
match user.role:
|
||||
case "student":
|
||||
print("طالب")
|
||||
case "instructor":
|
||||
print("instructor")
|
||||
case "admin":
|
||||
print("مشرف")
|
||||
case _:
|
||||
print("القيمة غير معروفة")
|
||||
except User.DoesNotExist:
|
||||
print("المستخدم غير موجود")
|
||||
|
||||
print("بعد معالجة الطلب")
|
||||
|
||||
return response
|
||||
|
||||
18
lms/accounts/migrations/0008_customuser_image.py
Normal file
18
lms/accounts/migrations/0008_customuser_image.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.0.10 on 2025-01-12 14:39
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0007_remove_customuser_username_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='customuser',
|
||||
name='image',
|
||||
field=models.ImageField(blank=True, null=True, upload_to='account/profile_image/'),
|
||||
),
|
||||
]
|
||||
|
|
@ -19,27 +19,25 @@ class CustomUserManager(BaseUserManager):
|
|||
|
||||
|
||||
class CustomUser(AbstractUser):
|
||||
# إزالة الحقل username من AbstractUser
|
||||
ROLE_CHOICES = {
|
||||
'student': 'Student',
|
||||
'instructor': 'Instructor',
|
||||
'admin': 'Admin',
|
||||
}
|
||||
username = None
|
||||
first_name = None
|
||||
last_name = None
|
||||
|
||||
# الحقول الخاصة بالمستخدم المخصص
|
||||
email = models.EmailField(unique=True)
|
||||
full_name = models.CharField(max_length=255, null=True, blank=True)
|
||||
ROLE_CHOICES = [
|
||||
('student', 'Student'),
|
||||
('instructor', 'Instructor'),
|
||||
('admin', 'Admin'),
|
||||
]
|
||||
image = models.ImageField(upload_to="account/profile_image/", null=True, blank=True)
|
||||
|
||||
role = models.CharField(max_length=20, choices=ROLE_CHOICES, null=True, blank=True)
|
||||
|
||||
# تخصيص مدير المستخدم
|
||||
objects = CustomUserManager()
|
||||
|
||||
# تحديد الحقول الأساسية
|
||||
USERNAME_FIELD = 'email'
|
||||
REQUIRED_FIELDS = [] # قائمة الحقول المطلوبة لإنشاء مستخدم عبر createsuperuser
|
||||
REQUIRED_FIELDS = []
|
||||
|
||||
def __str__(self):
|
||||
return self.email
|
||||
|
|
|
|||
18
lms/app/migrations/0002_course_image.py
Normal file
18
lms/app/migrations/0002_course_image.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.0.10 on 2025-01-12 10:03
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('app', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='course',
|
||||
name='image',
|
||||
field=models.ImageField(null=True, upload_to='courses/image'),
|
||||
),
|
||||
]
|
||||
23
lms/app/migrations/0003_course_is_paid_course_price.py
Normal file
23
lms/app/migrations/0003_course_is_paid_course_price.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 5.0.10 on 2025-01-12 10:16
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('app', '0002_course_image'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='course',
|
||||
name='is_paid',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='course',
|
||||
name='price',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True),
|
||||
),
|
||||
]
|
||||
18
lms/app/migrations/0004_course_rating.py
Normal file
18
lms/app/migrations/0004_course_rating.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.0.10 on 2025-01-12 14:39
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('app', '0003_course_is_paid_course_price'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='course',
|
||||
name='rating',
|
||||
field=models.PositiveSmallIntegerField(blank=True, null=True),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
from django.db import models
|
||||
from uuid import uuid4
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
|
@ -10,13 +10,25 @@ class Course(models.Model):
|
|||
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
|
||||
title = models.CharField(max_length=255, verbose_name="Course Title")
|
||||
description = models.TextField(verbose_name="Course Description")
|
||||
image = models.ImageField(upload_to="courses/image", null=True)
|
||||
is_paid = models.BooleanField(default=False)
|
||||
price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
|
||||
instructor = models.ForeignKey(User, on_delete=models.CASCADE, related_name='courses_taught', verbose_name="Instructor")
|
||||
rating = models.PositiveSmallIntegerField(null=True, blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Created At")
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name="Updated At")
|
||||
|
||||
def str(self):
|
||||
return self.title
|
||||
|
||||
def clean(self):
|
||||
if self.is_paid and (self.price is None or self.price <= 0):
|
||||
raise ValidationError({'price': 'Price must be set and greater than 0 for paid products.'})
|
||||
|
||||
if not self.is_paid and self.price:
|
||||
raise ValidationError({'price': 'Price must be empty for free products.'})
|
||||
|
||||
|
||||
# Table for modules (Module)
|
||||
class Module(models.Model):
|
||||
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
|
||||
|
|
|
|||
|
|
@ -7,79 +7,15 @@ from allauth.account.models import EmailAddress
|
|||
from dj_rest_auth.registration.serializers import RegisterSerializer
|
||||
|
||||
|
||||
class CustomLoginSerializer(LoginSerializer):
|
||||
email = serializers.EmailField(required=True)
|
||||
password = serializers.CharField(style={'input_type': 'password'})
|
||||
|
||||
def validate(self, attrs):
|
||||
email = attrs.get('email')
|
||||
password = attrs.get('password')
|
||||
|
||||
if not email or not password:
|
||||
raise serializers.ValidationError("Please enter both email and password.")
|
||||
|
||||
User = get_user_model()
|
||||
users = User.objects.filter(email=email)
|
||||
|
||||
if not users.exists():
|
||||
raise serializers.ValidationError("Incorrect email.")
|
||||
|
||||
if users.count() > 1:
|
||||
raise serializers.ValidationError("Multiple users found with this email. Please contact support.")
|
||||
|
||||
user = users.first()
|
||||
|
||||
if not user.check_password(password):
|
||||
raise serializers.ValidationError("Incorrect password.")
|
||||
|
||||
if not self.is_email_verified(user):
|
||||
raise serializers.ValidationError("Email not verified. Please verify your email first.")
|
||||
|
||||
attrs['user'] = user
|
||||
return attrs
|
||||
|
||||
|
||||
def is_email_verified(self, user):
|
||||
if hasattr(user, 'email_verified'):
|
||||
return user.email_verified
|
||||
else:
|
||||
try:
|
||||
email_address = EmailAddress.objects.get(user=user, email=user.email)
|
||||
return email_address.verified
|
||||
except EmailAddress.DoesNotExist:
|
||||
return False
|
||||
|
||||
|
||||
|
||||
|
||||
class CustomRegisterSerializer(RegisterSerializer):
|
||||
full_name = serializers.CharField(required=True)
|
||||
|
||||
def save(self, request):
|
||||
user = super().save(request)
|
||||
user.full_name = self.data.get('full_name', '')
|
||||
user.save()
|
||||
return user
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class CourseSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class CourseSerializer(serializers.ModelSerializer):
|
||||
instructor_name = serializers.CharField(source='instructor.username', read_only=True)
|
||||
class Meta:
|
||||
model = Course
|
||||
fields = ['url', 'id', 'title', 'description', 'instructor_name', 'created_at', 'updated_at']
|
||||
fields = ['id', 'title', 'description', 'is_paid', 'price', 'image', 'instructor_name', 'created_at', 'updated_at']
|
||||
read_only_fields = ['created_at', 'updated_at']
|
||||
|
||||
class ModuleSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class Meta:
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from .views import *
|
|||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register(r'courses-read', CourseRead, basename='course-read')
|
||||
router.register(r'courses', CourseViewSet, basename='course')
|
||||
router.register(r'modules', ModuleViewSet, basename='modules')
|
||||
router.register(r'lessons', LessonViewSet, basename='lessons')
|
||||
|
|
|
|||
|
|
@ -1,25 +1,44 @@
|
|||
from django.shortcuts import render
|
||||
from .serializers import *
|
||||
from .models import *
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from rest_framework.permissions import IsAuthenticated, BasePermission
|
||||
from .permissions import IsInstructor, IsAdmin
|
||||
|
||||
|
||||
|
||||
|
||||
class CourseRead(ReadOnlyModelViewSet):
|
||||
permission_classes = [IsAuthenticated]
|
||||
serializer_class = CourseSerializer
|
||||
queryset = Course.objects.all()
|
||||
|
||||
|
||||
|
||||
class CourseViewSet(ModelViewSet):
|
||||
"""
|
||||
A ViewSet for viewing and editing Course instances.
|
||||
"""
|
||||
permission_classes = [IsAuthenticated,]
|
||||
queryset = Course.objects.all()
|
||||
serializer_class = CourseSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
Return courses belonging to the authenticated user.
|
||||
"""
|
||||
user = self.request.user
|
||||
return Course.objects.filter(instructor=user)
|
||||
|
||||
def perform_create(self, serializer):
|
||||
"""
|
||||
Save the post data when creating a new course.
|
||||
"""
|
||||
serializer.save(instructor=self.request.user)
|
||||
user = self.request.user
|
||||
|
||||
|
||||
serializer.save(instructor=user)
|
||||
|
||||
def perform_update(self, serializer):
|
||||
"""
|
||||
|
|
@ -44,6 +63,14 @@ class CourseViewSet(ModelViewSet):
|
|||
)
|
||||
instance.delete()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ModuleViewSet(ModelViewSet):
|
||||
"""
|
||||
ViewSet for managing modules.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue