Accessing the course contents

We need a view for displaying the courses the students are enrolled in, and a view for accessing the actual course contents. Edit the views.py file of the students application and add the following code to it:

from django.views.generic.list import ListView
from courses.models import Course

class StudentCourseListView(LoginRequiredMixin, ListView):
    model = Course
    template_name = 'students/course/list.html'

    def get_queryset(self):
        qs = super(StudentCourseListView, self).get_queryset()
        return qs.filter(students__in=[self.request.user])

This is the view for students to list the courses they are enrolled in. It inherits from LoginRequiredMixin to make sure that only logged in users can access the view. It also inherits from the generic ListView for displaying a list of Course objects. We override the get_queryset() method for retrieving only the courses the user is enrolled in: We filter the querysetk by the students ManyToManyField field for doing so.

Then add the following code to the views.py file:

from django.views.generic.detail import DetailView

class StudentCourseDetailView(DetailView):
    model = Course
    template_name = 'students/course/detail.html'

    def get_queryset(self):
        qs = super(StudentCourseDetailView, self).get_queryset()
        return qs.filter(students__in=[self.request.user])

    def get_context_data(self, **kwargs):
        context = super(StudentCourseDetailView,
                        self).get_context_data(**kwargs)
        # get course object
        course = self.get_object()
        if 'module_id' in self.kwargs:
            # get current module
            context['module'] = course.modules.get(
                                    id=self.kwargs['module_id'])
        else:
            # get first module
            context['module'] = course.modules.all()[0]
        return context

This is the StudentCourseDetailView. We override the get_queryset() method to limit the base queryset to courses in which the user is enrolled. We also override the get_context_data() method to set a course module in the context if the module_id URL parameter is given. Otherwise, we set the first module of the course. This way, students will be able to navigate through modules inside a course.

Edit the the urls.py file of the students application and add the following URL patterns to it:

url(r'^courses/$',
    views.StudentCourseListView.as_view(),
    name='student_course_list'),

url(r'^course/(?P<pk>d+)/$',
    views.StudentCourseDetailView.as_view(),
    name='student_course_detail'),

url(r'^course/(?P<pk>d+)/(?P<module_id>d+)/$',
    views.StudentCourseDetailView.as_view(),
    name='student_course_detail_module'),

Create the following file structure inside the templates/students/ directory of the students application:

course/
   detail.html
   list.html

Edit the students/course/list.html template and add the following code to it:

{% extends "base.html" %}

{% block title %}My courses{% endblock %}

{% block content %}
    <h1>My courses</h1>

    <div class="module">
        {% for course in object_list %}
            <div class="course-info">
                <h3>{{ course.title }}</h3>
                <p><a href="{% url "student_course_detail" course.id %}">Access contents</a></p>
            </div>
        {% empty %}
            <p>
                You are not enrolled in any courses yet.
                <a href="{% url "course_list" %}">Browse courses</a> to enroll in a course.
            </p>
        {% endfor %}
    </div>
{% endblock %}

This template displays the courses the user is enrolled in. Edit the students/course/detail.html template and add the following code to it:

{% extends "base.html" %}

{% block title %}
    {{ object.title }}
{% endblock %}

{% block content %}
    <h1>
        {{ module.title }}
    </h1>
    <div class="contents">
        <h3>Modules</h3>
        <ul id="modules">
        {% for m in object.modules.all %}
            <li data-id="{{ m.id }}" {% if m == module %}class="selected"{% endif %}>
                <a href="{% url "student_course_detail_module" object.id m.id %}">
                    <span>
                        Module <span class="order">{{ m.order|add:1 }}</span>
                    </span>
                    <br>
                    {{ m.title }}
                </a>
            </li>
        {% empty %}
            <li>No modules yet.</li>
        {% endfor %}
        </ul>
    </div>
    <div class="module">
        {% for content in module.contents.all %}
            {% with item=content.item %}
                <h2>{{ item.title }}</h2>
                {{ item.render }}
            {% endwith %}
        {% endfor %}
    </div>
{% endblock %}

This is the template for enrolled students to access a course contents. First we build an HTML list including all course modules and highlighting the current module. Then we iterate over the current module contents, and access each content item to display it using {{ item.render }}. We are going to add the render() method to the content models next. This method will take care of rendering the content properly.

Rendering different types of content

We need to provide a way to render each type of content. Edit the models.py file of the courses application directory and add the following render() method to the ItemBase model as follows:

from django.template.loader import render_to_string
from django.utils.safestring import mark_safe

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

    def render(self):
        return render_to_string('courses/content/{}.html'.format(
                   self._meta.model_name), {'item': self})

This method uses the render_to_string() function for rendering a template and returning the rendered content as a string. Each kind of content is rendered using a template named after the content model. We use self._meta.model_name to build the appropriate template name for la. The render() methods provides a common interface for rendering diverse content.

Create the following file structure inside the templates/courses/ directory of the courses application:

content/
    text.html
    file.html
    image.html
    video.html

Edit the courses/content/text.html template and write this code:

{{ item.content|linebreaks|safe }}

Edit the courses/content/file.html template and add the following:

<p><a href="{{ item.file.url }}" class="button">Download file</a></p>

Edit the courses/content/image.html template and write:

<p><img src="{{ item.file.url }}"></p>

For files uploaded with ImageField and FileField to work, we need to set up our project to serve media files with the development server. Edit the settings.py file of your project and add the following code to it:

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')

Remember that MEDIA_URL is the base URL to serve uploaded media files and MEDIA_ROOT is the local path where the files are located.

Edit the main urls.py file of your project and add the following imports:

from django.conf import settings
from django.conf.urls.static import static

Then, write the following lines at the end of the file:

urlpatterns += static(settings.MEDIA_URL,
                      document_root=settings.MEDIA_ROOT)

Your project is now ready for uploading and serving media files using the development server. Remember that the development server is not suitable for production use. We will cover configuring a production environment in the next chapter.

We also have to create a template for rendering Video objects. We will use django-embed-video for embedding video content. Django-embed-video is a third-party Django application that allows you to embed videos in your templates, from sources like YouTube or Vimeo, by simply providing the video public URL.

Install the package with the following command:

pip install django-embed-video==1.0.0

Then, edit the settings.py file of your project and add 'embed_video' to the INSTALLED_APPS setting. You can find django-embed-video's documentation at http://django-embed-video.readthedocs.org/en/v1.0.0/.

Edit the courses/content/video.html template and write the following code:

{% load embed_video_tags %}
{% video item.url 'small' %}

Now run the development server and access http://127.0.0.1:8000/course/mine/ in your browser. Access the site with a user that belongs the Instructors group or a superuser, and add multiple contents to a course. For including video content, you can just copy any YouTube URL, for example https://www.youtube.com/watch?v=bgV39DlmZ2U, and include it it in the url field of the form. After adding contents to the course open http://127.0.0.1:8000/, click the course and click the ENROLL NOW button. You should get enrolled in the course and be redirected to the student_course_detail URL. The following image shows a sample course contents:

Rendering different types of content

Great! You have created a common interface for rendering course contents, each of them being rendered in a particular way.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset