How to enroll students into a course

Hello, i hope i can get some helo with this question.

I have models: Course, Modules, Lessons.
Also i have one main Custom User which has the roles of Customer, Teacher, Company owner, Admin etc…i have the views for registering and assining the roles and groups. Everything is working, thanks to the help i received here, that pointed me in a good direction.

My question is in two parts.
Considering i have to connect the course while creating it, with a logged in user, this is my create course view.


def add_course(request):
    form = AddCourseForm()
    if request.method == 'POST':
        form = AddCourseForm(request.POST, files=request.FILES)
        if form.is_valid():
            course = form.save(commit=False)
            # assign course owner
            course.user = request.user
            course.save()
            messages.success(request, f'Course {course} is created!')
            return redirect('dashboard:dashboard')
        
    context = {
        'form': form
    }
    return render(request, 'courses/add-course.html', context)

I will add user passes test and permissions so that only a certain group of users can add the course.

But what is confusing me is next: only one custom user is used for authentication and authorisation. I use the logged in user to create a course, but that same model (different role) can attend the course, is that correct?

So the only thing that will sepparate that Customer from not being able to create the course, is the uses test and permission on that create new course function? Is that correct?

And now the second question.
Can somebody please help me and explain, how do i actually enable course enrollment?

So when a Student registers, he can then start the course, but how does that work?

Thank you very much for any help

I think you need to use (USER_TYPES) in your (CustomUser Model), I don’t know what is your CustomUser Model Fields.
Also you can rely on one Model(CustomUser) for every users and use (is_active) to prevent any user from login to your application and use (USER_TYPES) as :

choices.py file

OWNER = "owner"
CLIENT = "client"
ADMIN = 'admin'
VENDOR = 'vendor'
EDITOR = 'editor'
MANAGER = 'manager'
EMPLOYEE = 'employee'
REPRESENTATIVE = 'rep'
DRIVER = 'driver'


USER_TYPES = (
    (OWNER, "Owner"),
    (MANAGER, "Manager"),
    (ADMIN, "Admin"),
    (EMPLOYEE, "Employee"),
    (EDITOR, "Editor"),
    (VENDOR, "Vendor"),
    (CLIENT, "Client"),
    (REPRESENTATIVE, "Representative"),
    (DRIVER, "Driver"),

)

models.py for CustomUser

from django.contrib.auth.models import AbstractUser

class CustomUser(AbstractUser):
    # add additional fields in here
    mobile1 = models.CharField(max_length=30, unique=True)
    mobile2 = models.CharField(max_length=30, blank=True, null=True)
    phone = models.CharField(max_length=30, blank=True, null=True)

    role = models.CharField(
        max_length=25, choices=choices.USER_TYPES, default=choices.CLIENT
    )

The above code is a sample, you can modify it to meet your needs.

By using (USER_TYPES) you can make any user with a specific role to create or update (course model) also you can prevent any user by doing a simple thing in your template

{% if request.user.role == 'admin' %}
     ... Do something for admin  users
{% elif request.user.role == 'employee' %}
     ... Do something for employees  users
{% elif request.user.role == 'editor' %}
     ... Do something for editors  users
{% endif %}

Or you can make a decorator for a specific view function (to prevent some users with a specific role) to access your (creation course or update course)
Or you can use Django Groups to make such mission

Hi, thank you for answering!
I will post all models in the next message :slight_smile:

Models.py

from django.db import models
from django.contrib.auth.models import AbstractUser

def file_upload_path(instance, filename):
    return f"user-{instance.user.id}/files/{filename}"


class User(AbstractUser):
    # DEFINE ROLES
    class Role(models.TextChoices):
        ADMIN = "ADMIN", "Admin"
        OWNER = "OWNER", "Owner"
        EMPLOYEE = "EMPLOYEE", "Employee"
        CUSTOMER = "CUSTOMER", "Customer"
        # TEACHER = 
        # STUDENT = 

    email = models.EmailField(max_length=50, unique=True)
    username = models.CharField(max_length=20, unique=True)

    role = models.CharField(max_length=10, choices=Role.choices, blank=True, null=True)

    image = models.ImageField(upload_to=file_upload_path, default='default/profile-demo.png')

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["username"]

    agree_to_terms = models.BooleanField('I agree to TOS', default=False, blank=False, null=False)
    
    def __str__(self):
        return self.email
    
    def show_full_name(self):
        return f'{self.first_name} {self.last_name}'
    
    def get_role(self):
        if self.role == User.Role.ADMIN:
            user_role = "Admin"
        elif self.role == User.Role.CUSTOMER:
            user_role = "Customer"
        elif self.role == User.Role.OWNER:
            user_role = "Owner"
        #elif self.role == None:
            #user_role = None
        return user_role
    



    
# to define:
# Owner Profile
# Customer Profile
# Employee Profile    


class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    note = models.CharField(max_length=50)

    #def __str__(self):
        #return self.note

Views.py


# CUSTOMER REGISTRATION
# REGISTER WITH EMAIL VERIFICATION
def register_view_customer(request):
    if request.user.is_authenticated:
        messages.warning(request, 'You are already logged in!')
        return redirect('dashboard:dashboard')
    
    if request.method == 'POST':
        register_form = UserRegisterForm(request.POST)
        if register_form.is_valid():
            user = register_form.save(commit=False)
            user.is_active = False
            user.role = User.Role.CUSTOMER
            user.save()
            group = Group.objects.get(name='Customers')
            group.user_set.add(user)

            # send verification email
            mail_subject = "Please activate your customer account"
            email_template = 'accounts/verification/account_verification_email.html'
            send_verification_email(request, user, mail_subject, email_template)
            return redirect('accounts:email_verification_sent')       
    else:
        register_form = UserRegisterForm()
    
    context = {
        'form': register_form
    }
    return render(request, 'accounts/register-customer.html', context)

So now we have a user registered, and he has a role of Customer.
Customer as in “student”, because the clients wants to sell products (ecom) and also have courses on the same platform. So, people can buy products and courses too, under the same account.

Now, i have Courses app with its own models, maybe i don’t need to overwhelm you with those models and views atm, if you can give me an idea, how do i actually enroll customers into a course?

I mean, we have the course details page, and the “Enroll” button, but i don’t understand the logic.

Thank you :slight_smile:

OK, you must make a relation between the students and the courses model (it should ManyToMany relation because many students can enroll in many courses)

So in your Courses model you must have the next fields from my prespective

  • user:(CustomUser: ForeignKey ) represents the user responsible for entering the Courses model data
  • student_user: (CustomUser: ManyToMany) represents the user plays a student role.
  • teacher_user: (CustomUser: ForeignKey) represents the user plays a teacher or (instructor) role
  • updated_user: (CustomUser: ForeignKey) represents the user responsible for update the Courses model
    and Other fields that concern the Courses model (what is your course name, period of the course, date of start this course and date of ending, time of the session and any important data concern the course itself).
    By the way don’t forget to add related_name to every Customuser relation in Courses model to avoid conflicts ./manage.py makemigrations will ask you to do that .

Ok, we are here in the course detail page (if I succeeded to enter this page with the “Enroll Button” this means that I’m a student ““I’m an authenticated user with role student””)
in your view the represent the course detail page you must do like the following

def course_details(request):
    form =  YourForm(request.POST or None)
    if form.is_valid():
      save_form = form.save(commit=Flase)
      save_form.studen_user = request.user #you get the authenticated user (the student user)
        save_form()

Of course the above is a scenario for clarifying the situation, it’s only to give you an idea
Hope this make sense.

Thank you very much for helping me!

Okay i will update the code and send it back, with some info for you, if you can please take a look and tell me if i am on the right track.

I am actually trying to understand how to connect the course model with custom user model, when i only have one custom user model. I mean i do have different roles, but i don’t realy understand how to connect them via m2m field.

For example, the user / client who will create the course is that logged in user, he logs in and creates the course, therefore we have

user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

But i am having difficulty understanding how to connect other users, can you please help me understand? How do you mean i need to connect student_user?

I can add that role, and i can create student_register_view and assign that role upon that registration. I just don’t realy understand the model relation part.

Thank you!

Pls share your files code

  • viwes.py
  • models.py
  • forms.py
  • templates

Which represents the whole logic

Okay my friend, thank you for trying so hard to help me.

I am sending everything.

First is models.py from Accounts app.

from django.db import models
from django.contrib.auth.models import AbstractUser

def file_upload_path(instance, filename):
    return f"user-{instance.user.id}/files/{filename}"


class User(AbstractUser):
    # DEFINE ROLES
    class Role(models.TextChoices):
        ADMIN = "ADMIN", "Admin"
        # this Owner role is the client, she will upload her lessons, so we can say she is also the teacher.
        OWNER = "OWNER", "Owner" 

        # these will be the employees. For another time i have to learn, how can the owner add her employees so they can do something
        EMPLOYEE = "EMPLOYEE", "Employee"

        # this is the student. We called it "Customer" in this eample
        CUSTOMER = "CUSTOMER", "Customer"
    
    email = models.EmailField(max_length=50, unique=True)
    username = models.CharField(max_length=20, unique=True)

    role = models.CharField(max_length=10, choices=Role.choices, blank=True, null=True)

    image = models.ImageField(upload_to=file_upload_path, default='default/profile-demo.png')

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["username"]

    agree_to_terms = models.BooleanField('I agree to TOS', default=False, blank=False, null=False)
    
    def __str__(self):
        return self.email
    
    def show_full_name(self):
        return f'{self.first_name} {self.last_name}'
    
    def get_role(self):
        if self.role == User.Role.ADMIN:
            user_role = "Admin"
        elif self.role == User.Role.CUSTOMER:
            user_role = "Customer"
        elif self.role == User.Role.OWNER:
            user_role = "Owner"
        return user_role
   
class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    note = models.CharField(max_length=50)

    #def __str__(self):
        #return self.note

Views.py from Accounts app, for registering that Cusomer role

# CUSTOMER REGISTRATION
# REGISTER WITH EMAIL VERIFICATION
def register_view_customer(request):
    if request.user.is_authenticated:
        messages.warning(request, 'You are already logged in!')
        return redirect('dashboard:dashboard')
    
    if request.method == 'POST':
        register_form = UserRegisterForm(request.POST)
        if register_form.is_valid():
            user = register_form.save(commit=False)
            user.is_active = False
            user.role = User.Role.CUSTOMER
            user.save()
            group = Group.objects.get(name='Customers')
            group.user_set.add(user)

            # send verification email
            mail_subject = "Please activate your customer account"
            email_template = 'accounts/verification/account_verification_email.html'
            send_verification_email(request, user, mail_subject, email_template)
            return redirect('accounts:email_verification_sent')       
    else:
        register_form = UserRegisterForm()
    
    context = {
        'form': register_form
    }
    return render(request, 'accounts/register-customer.html', context)


# REGISTER OWNER WITH EMAIL VERIFICATION, this is our client registration view
def register_view(request):
    if request.user.is_authenticated:
        messages.warning(request, 'You are already logged in!')
        return redirect('dashboard:dashboard')
    if request.method == 'POST':
        register_form = UserRegisterForm(request.POST)
        if register_form.is_valid():
            user = register_form.save(commit=False)
            user.is_active = False
            user.role = User.Role.OWNER
            user.save()
            group = Group.objects.get(name='Customers')
            group.user_set.add(user)

            # send verification email
            #mail_subject = "Please activate your account"
            #email_template = 'accounts/verification/account_verification_email.html'
            #send_verification_email(request, user, mail_subject, email_template)
            return redirect('accounts:email_verification_sent')       
    else:
        register_form = UserRegisterForm()
    
    context = {
        'form': register_form
    }
    return render(request, 'accounts/register.html', context)

That is all working, i also have a function to check the role and redirect to Owner or Customer dashboard, now we are going to Courses app.

Site owner / client, can log in. Site owner IS the teacher/client. I have a new question about adding employees, but that is for another time.

She can create courses, add modules to courses and lessons to modules…

Courses can have modules, like module 1, module 2, module 3…

Each module will have lessons. Many Lessons belong only to ONE course.

So you can have a course for example:

“How to create a Django website”

Module 1

  • lesson 1
  • lesson 2

Module 2

  • lesson 3
  • lesson 4

Module 3

  • lesson 5
  • lesson 6
  • lesson 7

So for example like above, we have a course, with 3 modules and 7 lessons in total (example).

Here is the models.py

from django.db import models
from django.urls import reverse
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver

from django.contrib.auth import get_user_model

User = get_user_model()

class Pricing(models.Model):
    name = models.CharField(max_length=100) # basic/pro/premium

    def __str__(self):
        return self.name

class Subscription(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    pricing = models.ForeignKey(Pricing, on_delete=models.CASCADE)
    created = models.DateTimeField(auto_now_add=True)
    stripe_subscription_id = models.CharField(max_length=100)
    status = models.CharField(max_length=100)

    def __str__(self):
        return self.user.email

# Course model
class Course(models.Model):
    name = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(max_length=100, unique=True, blank=True, null=True)
    image = models.ImageField(upload_to='course/thumbnails')

    pricing_tiers = models.ManyToManyField(Pricing, blank=True)

    # relation to user
    # teacher/company owner/ client who creates the course
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)


    # Customer user, how to connect?

    def __str__(self):
        return self.name
    
    def get_absolute_url(self):
        return reverse('courses:course', args=[str(self.slug)])
    
    def clean(self):
        self.name = self.name.capitalize()
        self.slug = self.slug.lower()

# Module
class Module(models.Model):
    name = models.CharField(max_length=100, default='Module name', unique=True)

    # one Course can have many Modules
    course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='modules')

    def __str__(self):
        return self.name
    
    def clean(self):
        self.name = self.name.capitalize()

# Lessons
class Lesson(models.Model):
    title = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(max_length=110, unique=True)
    content = models.TextField() # i will change this to ckeditor later

    is_completed = models.BooleanField(default=False)

    # Relation to Course
    # Many lessons can belong to only one Course
    course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name="course_lessons")

    # Relation to Module    
    module = models.ForeignKey(Module, on_delete=models.CASCADE, related_name='module_lessons')

    # User? Should i connect the Lesson with Owner User who is creating it?

    # for previous and next lesson in the template
    order = models.IntegerField(default=1)
    
    # Video
    # each lesson will have one video, i don't know should i add a video here, on create a model for it, with a Foreign key?

    # PDF materials
    # same question like above

    def __str__(self):
        return self.title    
    
    def get_absolute_url(self):
        return reverse('courses:view_lesson', args=[str(self.course.slug), str(self.slug)])
    
    def clean(self):
        self.title = self.title.capitalize()
        self.slug = self.slug.lower()

    # Do i need this?
    # class Meta:
        ordering = ['order']


class Video(models.Model):
    title = models.CharField(max_length=100, unique=True)

    # Relation to lesson
    # considering the above mentioned, don't know yet how to approach
    lesson = models.ForeignKey(Lesson, on_delete=models.CASCADE)

    # should i connect this model with the Owner user?

    vimeo_id = models.CharField(max_length=50)

    def __str__(self):
        return self.title
    


class PdfMaterial(models.Model):
    title = models.CharField(max_length=100)
    file = models.FileField(upload_to="uploads/pdf/%Y/%m/%d/")

    # Relation to Lesson
    # one lesson can have more pdf files for people to download
    lesson = models.ForeignKey(Lesson, on_delete=models.CASCADE)

    # should i connect this model with the Owner user?

    def __str__(self):
        return self.name

# This is a Signal to create a Subscription when the User is created, but i am not sure about this, because i already have UserProfile Signal in accounts...
'''
def post_save_user(sender, instance, created, *args, **kwargs):
    if created:
        free_trial_pricing = Pricing.objects.get(name="Free Trial")
        Subscription.objects.create(user=instance, pricing=free_trial_pricing)

post_save.connect(post_save_user, sender=User)
'''

This is the Course Admin.py

from django.contrib import admin
from .models import Course, Module, Lesson, Video, PdfMaterial, Subscription, Pricing

@admin.register(Course)
class CourseAdmin(admin.ModelAdmin):
    list_display = [
        'name', 'slug',
    ]

@admin.register(Module)
class ModuleAdmin(admin.ModelAdmin):
    list_display = [
        'name', 'course'
    ]

@admin.register(Lesson)
class LessonAdmin(admin.ModelAdmin):
    list_display = [
        'name', 'course', 'module',
    ]

@admin.register(Video)
class VideoAdmin(admin.ModelAdmin):
    list_display = [
        'title', 'lesson', 
    ]

@admin.register(PdfMaterial)
class PdfMaterialAdminAdmin(admin.ModelAdmin):
    list_display = [
        'name', 'lesson', 
    ]

@admin.register(Pricing)
class PricingAdmin(admin.ModelAdmin):
    list_display = [
        'name'
    ]

@admin.register(Subscription)
class SubscriptionAdmin(admin.ModelAdmin):
    list_display = [
        'user', 'pricing'
    ]

This is the sidebar template which i am including in Course page and in Lesson page

{% for lesson in module.module_lessons.all %}



{{lesson.title}}


15:00 min


{% if lesson.completed %}

{% else %}
{% endif %}




{% endfor %}

This is the Lesson.html page where i extend the course base


   <div class="completed-check">
              {% if lesson.completed %}
              <div class="completed-yes">
                <div class="text-block-52"><span></span></div>
                <div class="text-block-53">Completed</div>
              </div>
              {% else %}
              <div class="completed-no">
                <div class="w-form">
                  <form id="" name="" data-name="" method="get" data-wf-page-id="647661c0f546d8bc8cea2413" data-wf-element-id="6fb5eb60-1b28-c9f3-1a2f-3a7125640495"><input type="submit" value="Mark as complete" data-wait="Please wait..." class="mark-as-complete-btn w-button"></form>
                </div>
              </div>
              {% endif %}
            </div>


          </div>
          <a href="{{next_lesson.get_absolute_url}}" class="next-lesson-link w-inline-block">
            <div class="backend-paragraph lctt__lesson-count">Next lesson:</div>
            <div>{{next_lesson}}</div>
          </a>
        </div>

Now, i don’t understand:

if django is recommending to use only one User, how do i connect a Course with a Customer user, considering i have to connect the Course with the Owner user who is actually creating the course/modules/lessons.

Few images

This is to show you that everything is rendering nicely.

Adding a Course

Addint a Module

This is for adding the lesson

So what i don’t understand and need to learn is:

  1. How to enroll customers into a course?
  2. How to make the process of adding content better, because now everything is on sepparate page, i learned that formset method exist, should i learn that? Can i put 5 different forms into one view, so i can add Course, modules and lessons (videos and pdf) in one view?
  3. On the lesson page i have questions and notes forms, but i also have “mark lesson as complete” so i also have to learn how to send different post requests from forms
  4. To learn how to prevent page reload when “mark lesson as complete” button is clicked, so the icon just appears
  5. How to show lesson video duration
  6. And it would be nice to have this lesson current stay open after you click on that link in the dropdown. Don’t know how to do that. So in the sidebar we have Modules that show lessons, here is the image:

When i click on one lesson i get sent to that lesson, but the dropdown closes, this is just nice to have

Anyhow, i have burried you in text and code now, i am very grateful for your help, i would like to buy you lunch and a beer, just send me your PayPal :slight_smile:

THANK YOU

Okay, in your Course model you must add students users as I said before

you can add them as follows

class Course(models.Model):
    name = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(max_length=100, unique=True, blank=True, null=True)
    image = models.ImageField(upload_to='course/thumbnails')

    pricing_tiers = models.ManyToManyField(Pricing, blank=True)

    # relation to user
    # teacher/company owner/ client who creates the course
    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='owner_user', on_delete=models.CASCADE)
   student_user = models.ManyToMany(settings.AUTH_USER_MODEL, related_name='student_user', through='students')
   teacher_user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='teacher_user', on_delete=models.CASCADE)
   updated_user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='updated_user', on_delete=models.CASCADE)

This is what the Course model will be, But I don’t see in your Pricing model any amount or price field

I don’t know what is this for, But anyway you can add an amount field in the Course model itself .
Also you need to add period of the course, date of start this course and date of ending, time of the session .
As I mentioned before

Thank you very much, i will try do do as you said.

I just don’t understand what do i do with related_name=‘student_user’ after i add it? How do i connect it with the student or customer role?

Also, to be honest, atm i don’t know what you mean by “date of start this course and date of ending, time of the session”

If you are referring to the fields i did not add, i will do it, “created_at”, “updated_at”, i forgot to create them…

Thank you

The Docs - related_name , ForeignKey.related_name explains it .
I mentioned why to use it in my previous post

===================

If you went to take a German or English language course or any course, you will ask about many things about time.

  • How long this course will take? (6 weeks or 8 weeks or 3 months or whatever)
  • Or how many hours per this course should I attend ? (60, 100 hours or whatever)
  • How many hours per week should I attend? (4 hours per 2 days or 2 hours per one day)
  • And How many hours for one session per week ??? (session will be 1.5 hours or 2 hours or …)

Many questions will be exist and you need a good and clear answer for it , I think that’s clear now

Hello to all :slight_smile:

I am trying for days now, almost 30 days struggling with this thing, and i have to say that i really need your help.

I fill like i am close, but there are things i don’t understand i don’t have where to learn them. There are no tutorials for this (there are but in Indian language…) and Django docs are very confusing.

I did make some progress, now sending the updated models.py

Please try to take a look and point me in the right direction.

  1. I updated the models.py
  2. Added the Enrollment model (i can now connect the course with a user from Django Admin)
  3. I still don’t understand how to Enroll that user after he registers, i mean that is the idea.

I want users to see “Add to cart” button if they are not enrolled.
If they are i want to show them “Go back to course” button or a note saying “you are enrolled in this course”

  1. I don’t understand what form to use for frontend enrollment. I managed to show Enrollment model form but it shows a dropdown to choose a user, so i tried to put a queryset (for that form to “see” the student user that is logged in) but i am doing something wrong.

If you can, please help with advice.

MODELS.PY

from django.db import models
from django.conf import settings
from django.urls import reverse

def course_file_upload_path(instance, filename):
    return f'user-{instance.user.id}/courses/files/{filename}'


class Course(models.Model):
    title = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(max_length=110, unique=True)
    image = models.ImageField(upload_to=course_file_upload_path, default='default-images/post.png')
    description = models.TextField(null=True, blank=True)
    short_description = models.CharField(max_length=100)
    price = models.DecimalField(max_digits=5, decimal_places=2, default='100.00')
    created_at = models.DateTimeField(auto_now_add=True) 
    updated_at = models.DateTimeField(auto_now=True)

    # relations
    # to owner of the course = logged in user who is creating the course
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='courses')

    # student user
    # to a user that will attend the course
    student_user = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='student_user', through='enrollment')

    def __str__(self):
        return self.title
    
    def get_absolute_url(self):
        return reverse('courses:view_course', args=[str(self.slug)])
    
    def clean(self):
        self.title = self.title.capitalize()
        self.slug = self.slug.lower()

class Enrollment(models.Model):
    course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='courses')
    student = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='students')

    def __str__(self):
        return self.student.email

class Module(models.Model):
    title = models.CharField(max_length=50)
    tagline = models.CharField(max_length=50, default='Module tagline here up to 50 characters')

    # relation to Course
    course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='modules')

    def __str__(self):
        return self.title

class Lesson(models.Model):
    STATUS_CHOICES = (
        ('draft','Draft'),
        ('published','Published'),
        ('waiting_approval','Waiting approval'),
    )
    title = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(max_length=110, unique=True)
    content = models.TextField(null=True, blank=True)
    video = models.FileField(upload_to=course_file_upload_path, null=True, blank=True)
    created_at = models.DateField(auto_now_add=True)
    updated_at = models.DateField(auto_now=True)

    #order = models.SmallIntegerField()
    order = models.PositiveIntegerField()

    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='published')

    # mark lesson as complete
    is_completed = models.BooleanField(default=False)

    is_free_preview = models.BooleanField(default=False)

    # relations
    course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='lessons')
    module = models.ForeignKey(Module, on_delete=models.CASCADE, related_name='lessons')
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='lessons')

    def __str__(self):
        return self.title
    
    def get_absolute_url(self):
        return reverse('courses:view_lesson', args=[str(self.course.slug), str(self.slug)])
    
    def clean(self):
        self.title = self.title.capitalize()
        self.slug = self.slug.lower()


class Pdf(models.Model):
    title = models.CharField(max_length=100)
    file = models.FileField(upload_to=course_file_upload_path)

    # relation to Lesson
    lesson = models.ForeignKey(Lesson, on_delete=models.CASCADE)

    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

    def __str__(self):
        return self.title

VIEWS.PY


# one course details - single course landing page
def view_course(request, course_slug):
    # Enrollment Form
    enroll_form = EnrollmentForm()
    
    course = Course.objects.get(slug=course_slug)
    lesson = Lesson.objects.get(order=1)

    if request.method == 'POST':
        enroll_form = EnrollmentForm()
        if enroll_form.is_valid():
            student = enroll_form.save(commit=False)
            student.student = request.user
            student.save()
            messages.success(request, 'You are enrolled!')
            return redirect('dashboard:dashboard')
            
    context = {
        'course': course,
        'lesson': lesson,
        'enroll_form': enroll_form,
    }
    return render(request, 'courses/frontend/course.html', context)

Thank you <3

The Enrollment is simple if you have two fields you have to make a function to get the two fields data and then save it to your database

def enrollment(request, course_id):
    student = request.user
    course = Course.objects.get(id=course_id)  #(an_object_to_the_course)  
    saving_to_enroll_model = Enrollment.objects.create(course=course, student=student)

If this snippet is working you have to add

if enroll_form.is_valid():
            student = enroll_form.save(commit=False)
            student.student = request.user  # This is the authenticated user
            student.course = Course.objects.get(slug_field=course_slug) 
            student.save()
            messages.success(request, 'You are enrolled!')
            return redirect('dashboard:dashboard')

I think that’s all, forgive me if I forgot anything else, I’ll try to reply for every point you referred as possible as I can.
hope it helps

Hi, thank you very much for all your help and advice, please don’t apologise for helping me for free and dedicating time, i am really grateful.

I see that i am explaning incorrectly, maybe because i have information overload, i went through so much and don’t know where i am hitting anymore :slight_smile:

Okay so when the business owner or her employess want to add a user (customer, student) to a course, i have created a page for them do to so from the dashboard.
Basically it just renders the enrollment form, they select the user, select the course and click on the submit button, that is the pandan of admin form, same thing. (although it doesn’t show an error link the django admin does, when you try to enroll a user that is already enrolled)


class Enrollment(models.Model):
    # user to be enrolled
    student = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    # course to be enrolled in
    course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='students')

    enrolled_at = models.DateTimeField(auto_now_add=True, null=True, blank=True)

    class Meta:
        unique_together = ('student', 'course')

    def __str__(self):
        return self.student.email

Is this okay? Do i need to user this unique_together field / am i using it right?
How can i show the error if the user is already enrolled and you submit to enroll him again? (this is not a priority atm but would be nice to solve)


# ENROLL STUDENT IN A COURSE BY ADMIN OR OWNER
def enroll_student(request):
    form = EnrollmentForm()
    context = {
        'form': form
    }
    if request.method == 'POST':
        form = EnrollmentForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, 'Student is enrolled!')
            return redirect('dashboard:all_courses')
    
    return render(request, 'courses/courses/enroll-student.html', context)

So this form works basically.

Now the front end. This is a problem for me and a priority before i hit other things.

I render the course landing page:

 # one course landing page
    path('<slug:course_slug>/', views.view_course, name='view_course'),

We need users to see “Add to cart” button if they are not of a role CUSTOMER and if they are not registered. I know how to do this, i mean the only thing that is confusing is how to do this enroll automatically for that registered, student role user.

I already tried to create that function actually but i don’t know what to do with it, or how and where to call it :slight_smile:

Like i mentioned before, i tried to create a queryset but it shows a dropdown above the form so i deleted it, and that is the answer for does the enroll_form works in this view, and the answer is no :confused:

So when i now recreate this function as you have wrote:

def enrollment(request, course_id):
    student = request.user
    course = Course.objects.get(id=course_id)  #(an_object_to_the_course)  
    saving_to_enroll_model = Enrollment.objects.create(course=course, student=student)

What is the next step? Could you please explain?

WOW, i tried something, looks like it worked, sending feedback now

Okay so i have followed your advice.

Look:


# Enrollment 
def enrollment(request, course_slug):
    student = request.user
    course = Course.objects.get(slug=course_slug)
    saving_to_enroll_model = Enrollment.objects.create(course=course, student=student)

# one course details - single course landing page
def view_course(request, course_slug):    
    course = Course.objects.get(slug=course_slug)
    lesson = Lesson.objects.get(order=1)

    if request.method == 'POST':
        enrollment(request, course_slug)
        messages.success(request, 'You are enrolled!')
        return redirect('dashboard:dashboard')
            
    context = {
        'course': course,
        'lesson': lesson,
    }
    return render(request, 'courses/frontend/course.html', context)

I created that enrollment function and tried to call it on POST request in the view_course function.

Course page, i just did this (for now, have to do those checks):

<form action="" method='post'>
{% csrf_token %}
<button type='submit' class="shop-continue-button-copy w-inline-block">Enroll!</button>
</form>

When i click on that submit button: i do get redirected to dashboard (will edit this)

In the admin i get the data

So basically i can “call” a function on post request from that form on the course landing page? No url path needed or form action url? Just like this?

Thank you!