Struggling at ManyToMany

السلام عليكم شباب
كيف الحال ؟ :relaxed:
كنت اشتغل مأخرا على مشروع و قمت ب رفعه على heroku لاخذ ال feedback
وكما تعلمت معكم دائما : مادام موجود نسخة تشتغل خليها ترى النور و الباقي features :sweat_smile:
على كل هذا رابط المشروع على هيروكو
جميل ,
لكن المشكلة اني تفطنت الى خطأ في علاقة M:M بين بعض الmodels
فلأشرح قليلا.
المشروع عبارة عن منصة تعليمية فيها طلبة و مسالك
كل مسلك فيه دروس و كل درس فيه محاور
مشكلتي في الموديلز الثلاثة التالية :

class Student(models.Model):
user 		= models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True)
full_name 	= models.CharField(max_length=200, null=True, blank=True)
age 		= models.PositiveIntegerField(null=True, blank=True)
email 		= models.CharField(max_length=200, null=True, blank=True)
phone 		= models.CharField(max_length=20, null=True, blank=True)
about 		= models.TextField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=False, null=True, blank=True)

class Course(models.Model):
owner = models.ForeignKey(User, related_name='courses_created',help_text=_('owner') ,on_delete=models.CASCADE)
subject = models.ForeignKey(Subject, related_name='courses',help_text=_('subject') ,on_delete=models.CASCADE)
title = models.CharField(max_length=200,help_text=_('title'))
slug = models.SlugField(max_length=200, help_text=_('slug') ,unique=True, allow_unicode=True)
overview = models.TextField(help_text=_('overview'))
created = models.DateTimeField(auto_now_add=True)
thumbnail = models.ImageField(upload_to='images', null=True, blank=True)
students = models.ManyToManyField(Student, related_name='courses_joined',help_text=_('students'), blank=True)
completed = models.BooleanField(default=False, null=True)



class Module(models.Model):
course = models.ForeignKey(Course, related_name='modules',help_text=_('course') ,on_delete=models.CASCADE)
title = models.CharField(max_length=200 )
description = models.TextField(blank=True)
order = OrderField(blank=True, for_fields=['course'])
completed = models.BooleanField(default=False, null=True)

كما تلاحظون فإن كل درس يحتوي على عدة طلبة وكل طالب يمكنه التسجيل في عدة دروس من العلاقة m:m وكل محور لديه عدة دروس الا ان كل محور ينتمي الى درس معين
المشكلة(اسف على هذه المقدمة :sweat_smile: )
ان لاحظتم فالمحور فيه field completed و ذلك لكي يكمل المحور و عندا تنتهي محاور درس معين يتم اكتمال الدرس
image
مشكلتي ان المحور اذا انتهى عند طالب معين فانه ينتنهي عند جميع المستخدمين


يعني مثلا هذه التنيجة تظهر لكل المستخدمين رغم اني حاولت ان يكون كل درس خاص بطالب المسجل فيه
أعينوني بارك الله فيكم :sob: :sweat_smile:

4 Likes

ماشاء الله أخي سيف @saifeddin01 تصميم جبار :smiling_face_with_three_hearts: :heart_eyes:

2 Likes

شكرا جدا يا مصعب :heart_eyes: :heart_eyes:
ما رأيك من ناحية ال responsiveness ؟
حاولت جهدي

2 Likes

لاتقلق رائع كبداية لك :muscle:

2 Likes

عمل أكثر من رأئع صديقي سيف :heart_eyes: أبدعت فعلا…أنا اشتركت في موقعك الآن لا تنسى أن تشاركني كل جديد :grin:

نعود الى مشكلتك…

أولا ال M2M هي علاقة متعددة بين جدولين … لا يهم التعريف أعطيك مثال أفضل لتفهم الأمر (وهو نفس الحالة في موقعك):
تخيل أن لدينا جدولين واحد للدروس وواحد للتلاميذ أي Course و Student و نريد أن يكون نربطهما بحيث كل تلميذ يمكنه الارتباط بعدة دروس أو بمعنى لغوي الدروس المشاهدة من طرف التلميذ و لكن في نفس الوقت لا نريد أن نكرر الدروس في قاعدة البيانات بل درس واحد ولكن مرتبط بعدة تلاميذ أي بالمعنى اللغوي تم مشاهدته من طرف عدة تلاميذ…

هذا يستدعي عمل جدول وسيط يعرف العلاقة بين كل تلميذ و درس معين … نسميه مثلا مشاهدة أو Completion أو أي شيئ لأنه الاسم لا يهم… لماذا لا يهم؟ لأن جانقو يفعل هذا من أجلنا فكل ما علينا أن نعرف العلاقة بين الجدولين المعنيين مباشرة ولكن تحت الغطاء هذا ما يفعله جانقو

و بالتالي في حالتك هذا السطر غير ضروري و لا فائدة منه اطلاقا

completed = models.BooleanField(default=False, null=True)

بدلا من ذلك نستعمل حقل مشابه تمام ل students و نسميه watched_by مثلا

watched_by= models.ManyToManyField(Student, related_name='courses_watched',help_text=_('watched_by'), blank=True)

ثم نتأكد هل التلميذ لديه هذا الدرس في قائمة courses_watched أو لا لكي نعرف هل شاهد الدرس :ok_hand:

أتمنى أن يكون هذا الشرح مفهوم صديقي سيف

4 Likes

الموقع جميل بيحتاج responsive بعض التعديلات ورائع احسنت @saifeddin01 :+1:

2 Likes

شكرا جدا هشام والله فرحتني بال feedback والشرح اكثر من واضح

لكن اذا اردت ان يكون الدرس مكتملا فكيف افعل
فكما قلت لك , هذا الحقل موجود في المحور و الدرس
بالنسبة للمحور فان حقل اكمال المحور يتم عبر النقر على الcheckbox
ونفس الدالة تقووم بأكمال الدرس 100% اذا كانت جميع محاور ذاك الدرس مكتملة
اظن المشكلة في المحاور لان الطالب حين يسجل في كورس يجد المحاور كما تركها الطلاب الاخرون

image

جرت احضار الكورس عن طريق request.user.student.courses_joined.filter(id=course_id)I
اظن هذا ما قصدته عندما قلت :

لكن نفس النتيجة
فكرت في علاقة بين المحور و الطالب لكن لا اظن ذلك صحيحا
قمت بالتعديل على المودل :


و عدلت على الدالة هكذا :
image
لكن لم يغير شيء :pensive:

2 Likes

شكرا جدا جدا :heart_eyes: :heart_eyes:

3 Likes

وجدت انه من الافضل التخلي عن خاصية اكمال الكورس مبدئيا

1 Like

أهنئك يا سيف على الموقع فهو يبدو جميل بالفعل :clap:

صراحة بإمكاني أقول لك الكويري تبع اكمال الكورس ستجدها غريبة ومعقدة نوعا ما

لأنها ستجمع بين كلاً من الدروس + اليوزرز في جدول في الوسط ManyToMany، هذا جزء من أكواد الأكاديمية مسرب لك :grin:

from django.db import models as django_models
from django.contrib.auth import get_user_model

User = get_user_model()


class LessonManager(django_models.Manager):
    def with_is_shown(self, user):

        user_shown_lessons = user.shown_lessons.values('id')

        shown_user_case = django_models.Case(
            django_models.When(id__in=user_shown_lessons, then=django_models.Value(True)),
            default=django_models.Value(False),
            output_field=django_models.BooleanField())

        return self.get_queryset().annotate(is_shown=shown_user_case)


class Lesson(django_models.Model):
     title = models.CharField(max_length=60, verbose_name=_('title'))
     shown_users = models.ManyToManyField(User, related_name='shown_lessons', verbose_name=_('shown users'), blank=True)

     objects = LessonManager()

وبإمكانك استدعاء الدروس بهذا الشكل:

from django.contrib.auth import get_user_model
User = get_user_model()
user1 = User.objects.get(pk=1) # choose the pk you want
Lesson.objects.with_is_shown(user1).all()

باختصار هذا الكود سيضيف حقل is_shown أثناء الكويري annotate (لن يتم إضافته لقاعدة البيانات)

هكذا ستقوم الكويري بجلب الحقل is_shown مع الدرس وقيمته True أو False

باستعمال قيمة is_shown تستطيع عمل count لتعرف كم بالمائة اليوزر أنهى من الكورس

1 Like

شكرا استاذ ياسر :heart_eyes:
رغم اني ضايع شوي فالكود لكن سأحاول فهمه و تطبيقه على الموقع