diff --git a/apps/course/models.py b/apps/course/models.py index 0c33fd81..553e6c7c 100644 --- a/apps/course/models.py +++ b/apps/course/models.py @@ -139,7 +139,7 @@ class Course(BaseModel, DeactivatedMixin): return self.get_absolute_url() def get_absolute_url(self): - return reverse_lazy('course', args=[self.slug or self.id]) + return reverse_lazy('course', args=[self.slug.lower() if self.slug else self.id]) @property def is_free(self): diff --git a/apps/course/views.py b/apps/course/views.py index 07ca6bad..5e625c42 100644 --- a/apps/course/views.py +++ b/apps/course/views.py @@ -200,6 +200,32 @@ class CourseView(DetailView): # ((self.object.status != Course.PUBLISHED and request.user.role != User.ADMIN_ROLE) or # (self.object.status != Course.PUBLISHED and request.user.role != User.AUTHOR_ROLE and self.object.author != request.user)): + def get_object(self, queryset=None): + if queryset is None: + queryset = self.get_queryset() + + pk = self.kwargs.get(self.pk_url_kwarg) + slug = self.kwargs.get(self.slug_url_kwarg) + if pk is not None: + queryset = queryset.filter(pk=pk) + + if slug is not None and (pk is None or self.query_pk_and_slug): + slug_field = self.get_slug_field() + queryset = queryset.filter(**{'%s__iexact' % slug_field: slug}) + + if pk is None and slug is None: + raise AttributeError("Generic detail view %s must be called with " + "either an object pk or a slug." + % self.__class__.__name__) + + try: + # Get the single item from the filtered queryset + obj = queryset.get() + except queryset.model.DoesNotExist: + raise Http404(_("No %(verbose_name)s found matching the query") % + {'verbose_name': queryset.model._meta.verbose_name}) + return obj + def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) if self.request.user.is_authenticated: diff --git a/web/src/components/CourseRedactor.vue b/web/src/components/CourseRedactor.vue index d2c7fddc..073e9f49 100644 --- a/web/src/components/CourseRedactor.vue +++ b/web/src/components/CourseRedactor.vue @@ -476,7 +476,7 @@ onCourseNameInput() { this.$v.course.title.$touch(); if (!this.slugChanged && !this.$v.course.status) { - this.course.slug = slugify(this.course.title); + this.course.slug = (slugify(this.course.title) || '').toLowerCase(); } }, removeLesson(lessonIndex) { diff --git a/web/src/js/modules/api.js b/web/src/js/modules/api.js index c9627aac..93b3281c 100644 --- a/web/src/js/modules/api.js +++ b/web/src/js/modules/api.js @@ -113,7 +113,7 @@ export const api = { deferred_start_at: deferredStart, duration: courseObject.duration || 0, is_featured: courseObject.is_featured, - slug: courseObject.slug, + slug: (courseObject.slug || '').toLowerCase(), date: (courseObject.date) ? courseObject.date.value:null, stream: courseObject.stream, cover: courseObject.coverImageId ? courseObject.coverImageId : null,