diff --git a/apps/course/admin.py b/apps/course/admin.py index 9b5cd1ae..dab42062 100644 --- a/apps/course/admin.py +++ b/apps/course/admin.py @@ -1,6 +1,7 @@ from django.contrib import admin +from mptt.admin import MPTTModelAdmin, DraggableMPTTAdmin -from .models import Course, Category +from .models import Course, Category, Lesson, Material, CourseComment, LessonComment @admin.register(Course) @@ -20,3 +21,28 @@ class CourseAdmin(admin.ModelAdmin): @admin.register(Category) class CategoryAdmin(admin.ModelAdmin): pass + + +@admin.register(Lesson) +class LessonAdmin(admin.ModelAdmin): + list_display = ( + 'title', + 'course', + ) + + +@admin.register(Material) +class MaterialAdmin(admin.ModelAdmin): + list_display = ( + 'title', + ) + + +@admin.register(CourseComment) +class CourseCommentAdmin(DraggableMPTTAdmin): + pass + + +@admin.register(LessonComment) +class LessonCommentAdmin(DraggableMPTTAdmin): + pass diff --git a/apps/course/migrations/0006_lesson.py b/apps/course/migrations/0006_lesson.py new file mode 100644 index 00000000..0d095d28 --- /dev/null +++ b/apps/course/migrations/0006_lesson.py @@ -0,0 +1,29 @@ +# Generated by Django 2.0.1 on 2018-01-29 14:07 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0005_course_from_author'), + ] + + operations = [ + migrations.CreateModel( + name='Lesson', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=100, verbose_name='Название урока')), + ('short_description', models.TextField(verbose_name='Краткое описание курса')), + ('cover', models.ImageField(upload_to='lessons', verbose_name='Фон урока')), + ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='lessons', to='course.Course')), + ], + options={ + 'verbose_name': 'Урок', + 'verbose_name_plural': 'Уроки', + 'ordering': ('-title',), + }, + ), + ] diff --git a/apps/course/migrations/0007_auto_20180129_1433.py b/apps/course/migrations/0007_auto_20180129_1433.py new file mode 100644 index 00000000..6f2bc7b7 --- /dev/null +++ b/apps/course/migrations/0007_auto_20180129_1433.py @@ -0,0 +1,17 @@ +# Generated by Django 2.0.1 on 2018-01-29 14:33 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0006_lesson'), + ] + + operations = [ + migrations.AlterModelOptions( + name='lesson', + options={'ordering': ('title',), 'verbose_name': 'Урок', 'verbose_name_plural': 'Уроки'}, + ), + ] diff --git a/apps/course/migrations/0008_auto_20180129_1436.py b/apps/course/migrations/0008_auto_20180129_1436.py new file mode 100644 index 00000000..189d08b1 --- /dev/null +++ b/apps/course/migrations/0008_auto_20180129_1436.py @@ -0,0 +1,25 @@ +# Generated by Django 2.0.1 on 2018-01-29 14:36 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0007_auto_20180129_1433'), + ] + + operations = [ + migrations.AddField( + model_name='lesson', + name='created_at', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='lesson', + name='update_at', + field=models.DateTimeField(auto_now=True), + ), + ] diff --git a/apps/course/migrations/0009_auto_20180129_1458.py b/apps/course/migrations/0009_auto_20180129_1458.py new file mode 100644 index 00000000..d3cdaf3e --- /dev/null +++ b/apps/course/migrations/0009_auto_20180129_1458.py @@ -0,0 +1,39 @@ +# Generated by Django 2.0.1 on 2018-01-29 14:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0008_auto_20180129_1436'), + ] + + operations = [ + migrations.CreateModel( + name='Material', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=100, verbose_name='Название материала')), + ('cover', models.ImageField(upload_to='lessons', verbose_name='Фон материала')), + ('short_description', models.TextField(verbose_name='Краткое описание материала')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('update_at', models.DateTimeField(auto_now=True)), + ], + options={ + 'verbose_name': 'Материал', + 'verbose_name_plural': 'Материалы', + 'ordering': ('title',), + }, + ), + migrations.AlterField( + model_name='lesson', + name='short_description', + field=models.TextField(verbose_name='Краткое описание урока'), + ), + migrations.AddField( + model_name='course', + name='materials', + field=models.ManyToManyField(to='course.Material'), + ), + ] diff --git a/apps/course/migrations/0010_auto_20180129_1501.py b/apps/course/migrations/0010_auto_20180129_1501.py new file mode 100644 index 00000000..305afaaa --- /dev/null +++ b/apps/course/migrations/0010_auto_20180129_1501.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.1 on 2018-01-29 15:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0009_auto_20180129_1458'), + ] + + operations = [ + migrations.AlterField( + model_name='material', + name='cover', + field=models.ImageField(upload_to='materials', verbose_name='Фон материала'), + ), + ] diff --git a/apps/course/migrations/0011_auto_20180129_1527.py b/apps/course/migrations/0011_auto_20180129_1527.py new file mode 100644 index 00000000..c2214936 --- /dev/null +++ b/apps/course/migrations/0011_auto_20180129_1527.py @@ -0,0 +1,28 @@ +# Generated by Django 2.0.1 on 2018-01-29 15:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0010_auto_20180129_1501'), + ] + + operations = [ + migrations.AlterField( + model_name='course', + name='from_author', + field=models.TextField(blank=True, default='', null=True, verbose_name='От автора'), + ), + migrations.AlterField( + model_name='course', + name='likes', + field=models.ManyToManyField(blank=True, to='course.Like'), + ), + migrations.AlterField( + model_name='course', + name='materials', + field=models.ManyToManyField(blank=True, to='course.Material'), + ), + ] diff --git a/apps/course/migrations/0012_comment.py b/apps/course/migrations/0012_comment.py new file mode 100644 index 00000000..fe3f6e14 --- /dev/null +++ b/apps/course/migrations/0012_comment.py @@ -0,0 +1,35 @@ +# Generated by Django 2.0.1 on 2018-01-29 17:09 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import mptt.fields + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('course', '0011_auto_20180129_1527'), + ] + + operations = [ + migrations.CreateModel( + name='Comment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('content', models.TextField(default='', verbose_name='Текст комментария')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('update_at', models.DateTimeField(auto_now=True)), + ('lft', models.PositiveIntegerField(db_index=True, editable=False)), + ('rght', models.PositiveIntegerField(db_index=True, editable=False)), + ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)), + ('level', models.PositiveIntegerField(db_index=True, editable=False)), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), + ('parent', mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='children', to='course.Comment')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/course/migrations/0013_auto_20180129_1715.py b/apps/course/migrations/0013_auto_20180129_1715.py new file mode 100644 index 00000000..47be2f6f --- /dev/null +++ b/apps/course/migrations/0013_auto_20180129_1715.py @@ -0,0 +1,17 @@ +# Generated by Django 2.0.1 on 2018-01-29 17:15 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0012_comment'), + ] + + operations = [ + migrations.AlterModelOptions( + name='comment', + options={'verbose_name': 'Комментарий', 'verbose_name_plural': 'Комментарии'}, + ), + ] diff --git a/apps/course/migrations/0014_auto_20180129_1726.py b/apps/course/migrations/0014_auto_20180129_1726.py new file mode 100644 index 00000000..8330ba86 --- /dev/null +++ b/apps/course/migrations/0014_auto_20180129_1726.py @@ -0,0 +1,70 @@ +# Generated by Django 2.0.1 on 2018-01-29 17:26 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import mptt.fields + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('course', '0013_auto_20180129_1715'), + ] + + operations = [ + migrations.CreateModel( + name='CourseComment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('content', models.TextField(default='', verbose_name='Текст комментария')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('update_at', models.DateTimeField(auto_now=True)), + ('lft', models.PositiveIntegerField(db_index=True, editable=False)), + ('rght', models.PositiveIntegerField(db_index=True, editable=False)), + ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)), + ('level', models.PositiveIntegerField(db_index=True, editable=False)), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), + ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='course.Course')), + ('parent', mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='children', to='course.CourseComment')), + ], + options={ + 'verbose_name': 'Комментарий', + 'verbose_name_plural': 'Комментарии', + 'abstract': False, + }, + ), + migrations.CreateModel( + name='LessonComment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('content', models.TextField(default='', verbose_name='Текст комментария')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('update_at', models.DateTimeField(auto_now=True)), + ('lft', models.PositiveIntegerField(db_index=True, editable=False)), + ('rght', models.PositiveIntegerField(db_index=True, editable=False)), + ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)), + ('level', models.PositiveIntegerField(db_index=True, editable=False)), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), + ('lesson', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='course.Lesson')), + ('parent', mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='children', to='course.LessonComment')), + ], + options={ + 'verbose_name': 'Комментарий', + 'verbose_name_plural': 'Комментарии', + 'abstract': False, + }, + ), + migrations.RemoveField( + model_name='comment', + name='author', + ), + migrations.RemoveField( + model_name='comment', + name='parent', + ), + migrations.DeleteModel( + name='Comment', + ), + ] diff --git a/apps/course/migrations/0015_auto_20180129_1733.py b/apps/course/migrations/0015_auto_20180129_1733.py new file mode 100644 index 00000000..986fb1ed --- /dev/null +++ b/apps/course/migrations/0015_auto_20180129_1733.py @@ -0,0 +1,135 @@ +# Generated by Django 2.0.1 on 2018-01-29 17:33 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import mptt.fields + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('course', '0014_auto_20180129_1726'), + ] + + operations = [ + migrations.CreateModel( + name='Comment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('content', models.TextField(default='', verbose_name='Текст комментария')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('update_at', models.DateTimeField(auto_now=True)), + ('lft', models.PositiveIntegerField(db_index=True, editable=False)), + ('rght', models.PositiveIntegerField(db_index=True, editable=False)), + ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)), + ('level', models.PositiveIntegerField(db_index=True, editable=False)), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), + ('parent', mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='children', to='course.Comment')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AlterModelOptions( + name='coursecomment', + options={'verbose_name': 'Комментарий курса', 'verbose_name_plural': 'Комментарии курсов'}, + ), + migrations.AlterModelOptions( + name='lessoncomment', + options={'verbose_name': 'Комментарий урока', 'verbose_name_plural': 'Комментарии уроков'}, + ), + migrations.RemoveField( + model_name='coursecomment', + name='author', + ), + migrations.RemoveField( + model_name='coursecomment', + name='content', + ), + migrations.RemoveField( + model_name='coursecomment', + name='created_at', + ), + migrations.RemoveField( + model_name='coursecomment', + name='id', + ), + migrations.RemoveField( + model_name='coursecomment', + name='level', + ), + migrations.RemoveField( + model_name='coursecomment', + name='lft', + ), + migrations.RemoveField( + model_name='coursecomment', + name='parent', + ), + migrations.RemoveField( + model_name='coursecomment', + name='rght', + ), + migrations.RemoveField( + model_name='coursecomment', + name='tree_id', + ), + migrations.RemoveField( + model_name='coursecomment', + name='update_at', + ), + migrations.RemoveField( + model_name='lessoncomment', + name='author', + ), + migrations.RemoveField( + model_name='lessoncomment', + name='content', + ), + migrations.RemoveField( + model_name='lessoncomment', + name='created_at', + ), + migrations.RemoveField( + model_name='lessoncomment', + name='id', + ), + migrations.RemoveField( + model_name='lessoncomment', + name='level', + ), + migrations.RemoveField( + model_name='lessoncomment', + name='lft', + ), + migrations.RemoveField( + model_name='lessoncomment', + name='parent', + ), + migrations.RemoveField( + model_name='lessoncomment', + name='rght', + ), + migrations.RemoveField( + model_name='lessoncomment', + name='tree_id', + ), + migrations.RemoveField( + model_name='lessoncomment', + name='update_at', + ), + migrations.AddField( + model_name='coursecomment', + name='comment_ptr', + field=models.OneToOneField(auto_created=True, default=0, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='course.Comment'), + preserve_default=False, + ), + migrations.AddField( + model_name='lessoncomment', + name='comment_ptr', + field=models.OneToOneField(auto_created=True, default=0, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='course.Comment'), + preserve_default=False, + ), + ] diff --git a/apps/course/migrations/0016_auto_20180129_1756.py b/apps/course/migrations/0016_auto_20180129_1756.py new file mode 100644 index 00000000..56672ad6 --- /dev/null +++ b/apps/course/migrations/0016_auto_20180129_1756.py @@ -0,0 +1,24 @@ +# Generated by Django 2.0.1 on 2018-01-29 17:56 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0015_auto_20180129_1733'), + ] + + operations = [ + migrations.AlterField( + model_name='coursecomment', + name='course', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='course.Course'), + ), + migrations.AlterField( + model_name='lessoncomment', + name='lesson', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='course.Lesson'), + ), + ] diff --git a/apps/course/models.py b/apps/course/models.py index 7c7ba259..0b851950 100644 --- a/apps/course/models.py +++ b/apps/course/models.py @@ -3,6 +3,8 @@ from django.db import models from django.utils import timezone from django.contrib.auth import get_user_model +from mptt.models import MPTTModel, TreeForeignKey + from .manager import CategoryQuerySet User = get_user_model() @@ -22,19 +24,20 @@ class Course(models.Model): (2, 'Archived'), ) author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) - title = models.CharField("Название курса", max_length=100) - short_description = models.TextField("Краткое описание курса") - from_author = models.TextField("От автора", default='') - cover = models.ImageField("Фон курса", upload_to='courses') - price = models.DecimalField("Цена курса", help_text="Если цены нету, то курс бесплатный", max_digits=10, decimal_places=2, null=True, blank=True) + title = models.CharField('Название курса', max_length=100) + short_description = models.TextField('Краткое описание курса') + from_author = models.TextField('От автора', default='', null=True, blank=True) + cover = models.ImageField('Фон курса', upload_to='courses') + price = models.DecimalField('Цена курса', help_text='Если цены нету, то курс бесплатный', max_digits=10, decimal_places=2, null=True, blank=True) is_infinite = models.BooleanField(default=False) - deferred_start_at = models.DateTimeField("Отложенный запуск курса", help_text="Заполнить если курс отложенный", null=True, blank=True) - category = models.ForeignKey("Category", on_delete=models.PROTECT) - duration = models.IntegerField("Продолжительность курса", default=0) + deferred_start_at = models.DateTimeField('Отложенный запуск курса', help_text='Заполнить если курс отложенный', null=True, blank=True) + category = models.ForeignKey('Category', on_delete=models.PROTECT) + duration = models.IntegerField('Продолжительность курса', default=0) is_featured = models.BooleanField(default=False) url = models.URLField('Ссылка', default='') status = models.PositiveSmallIntegerField('Статус', default=0, choices=STATUS_CHOICES) - likes = models.ManyToManyField(Like) + likes = models.ManyToManyField(Like, blank=True) + materials = models.ManyToManyField('Material', blank=True) created_at = models.DateTimeField(auto_now_add=True) update_at = models.DateTimeField(auto_now=True) @@ -62,18 +65,92 @@ class Course(models.Model): return True return False + def __str__(self): + return str(self.id) + ' ' + self.title + class Meta: - verbose_name = "Курс" - verbose_name_plural = "Курсы" + verbose_name = 'Курс' + verbose_name_plural = 'Курсы' ordering = ['-created_at'] class Category(models.Model): - title = models.CharField("Название категории", max_length=100) + title = models.CharField('Название категории', max_length=100) + + def __str__(self): + return self.title + + class Meta: + verbose_name = 'Категория' + verbose_name_plural = 'Категории' + + +class Lesson(models.Model): + title = models.CharField('Название урока', max_length=100) + short_description = models.TextField('Краткое описание урока') + course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='lessons') + cover = models.ImageField('Фон урока', upload_to='lessons') + + created_at = models.DateTimeField(auto_now_add=True) + update_at = models.DateTimeField(auto_now=True) def __str__(self): return self.title class Meta: - verbose_name = "Категория" - verbose_name_plural = "Категории" + verbose_name = 'Урок' + verbose_name_plural = 'Уроки' + ordering = ('title',) + + +class Material(models.Model): + title = models.CharField('Название материала', max_length=100) + cover = models.ImageField('Фон материала', upload_to='materials') + short_description = models.TextField('Краткое описание материала') + + created_at = models.DateTimeField(auto_now_add=True) + update_at = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.title + + class Meta: + verbose_name = 'Материал' + verbose_name_plural = 'Материалы' + ordering = ('title',) + + +class Comment(MPTTModel): + parent = TreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True, on_delete=models.PROTECT) + author = models.ForeignKey(User, on_delete=models.PROTECT) + content = models.TextField('Текст комментария', default='') + + created_at = models.DateTimeField(auto_now_add=True) + update_at = models.DateTimeField(auto_now=True) + + @property + def created_at_humanize(self): + return arrow.get(self.created_at).humanize(locale='ru') + + def __str__(self): + return self.content + + class MPTTMeta: + order_insertion_by = ['created_at'] + abstract = True + + +class CourseComment(Comment): + course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='comments') + + class Meta: + verbose_name = 'Комментарий курса' + verbose_name_plural = 'Комментарии курсов' + + +class LessonComment(Comment): + lesson = models.ForeignKey(Lesson, on_delete=models.CASCADE, related_name='comments') + + class Meta: + verbose_name = 'Комментарий урока' + verbose_name_plural = 'Комментарии уроков' diff --git a/apps/course/templates/course/course.html b/apps/course/templates/course/course.html index c6202d1d..fc847874 100644 --- a/apps/course/templates/course/course.html +++ b/apps/course/templates/course/course.html @@ -1,5 +1,6 @@ {% extends "templates/lilcity/index.html" %} {% load static %} +{% load mptt_tags %} {% block content %}
@@ -36,7 +37,7 @@
-
12 часов
+
{{ course.duration }}
@@ -50,7 +51,7 @@
-
5 уроков
+
{{ course.lessons.count }}
@@ -91,52 +92,19 @@
Содержание курса
+ {% for lesson in course.lessons.all %}
-
1 УРОК
+
{{ lesson.title }}
+ {% if lesson.cover %} +
+ {% else %}
-
Выбираем сюжет, с которым мы будем работать на курсе и главного героя вашей истории. С этим героем мы будем работать на протяжении всего курса.
+ {% endif %} +
{{ lesson.short_description | safe }}
-
-
-
2 УРОК
-
-
-
-

Выделяем основные черты внешности и характера вашего персонажа. Вам нужно будет ответить на несколько ключевых вопросов:


-

– Кто он и как он выглядит?

-

– Где и в какое время он живет?

-

– Какой его характерный признак?

-
-
-
-
-
1 УРОК
-
-
-
-

Собираем материал и экспериментируем с образом героя, используя полученный список характерных особенностей персонажа. По каждому признаку нужно будет собрать «референсы». Рисуем элементы образа в своем стиле.

-

Собираем из них нашего персонажа. Выбираем самые удачные элементы, объединяем их в одном наброске.

-
-
-
-
-
1 УРОК
-
-
-
-

Попробуем оживить персонажа. Выбираем 5 характерных действий для вашего героя и того сюжета, в котором он принимает участие, рисуем эскизы вашего персонажа в разных характерных для него позах и движении.

-

Выделяем 5 характерных эмоций для героя, над которым работаем. По эмоциям подбираем референсы и стилизуем их в своем стиле.

-
-
-
-
-
1 УРОК
-
-
-
Адаптируем вашего героя в соответствии с целевой аудиторией. Рассматриваем, как люди разных возрастов по разному воспринимают образы, героев и их эмоции. Выделяем 3 основные группы — дети, подростки и взрослые люди.
-
-
+
+ {% endfor %}
@@ -146,20 +114,19 @@
Материалы, которые понадобятся
+ {% for material in course.materials.all %}
+ {% if material.cover %} +
+ {% else %}
+ {% endif %}
-
Бумага акварельная
-
Этот курс поможет детям узнать о том как из простых форм создавать веселый и харизматичных персонажей.
-
-
-
-
-
-
Бумага акварельная
-
Этот курс поможет детям узнать о том как из простых форм создавать веселый и харизматичных персонажей.
+
{{ material.title }}
+
{{ material.short_description }}
+ {% endfor %}
@@ -210,7 +177,7 @@
-
12 часов
+
{{ course.duration }}
@@ -225,6 +192,7 @@
+
Задавайте вопросы:
@@ -233,17 +201,29 @@
-
-
-
-
-
Богиня Недалекая16 Сен, 16:52
-
А можно ли заниматься если у меня нет интернета и компьютера. Можно ли запустить видео на моей микроволновке?
+ {% recursetree course.comments.all %} + +
+ {% if node.author.photo %} +
+ {% else %} +
+ {% endif %} +
+
+
{{ node.author.get_full_name }}{{ node.created_at_humanize }}
+
{{ node.content }}
+
+
-
-
-
+ + {% if not node.is_leaf_node %} + {{ children }} + {% endif %} + {% endrecursetree %} + +
diff --git a/project/settings.py b/project/settings.py index 8e3c8a81..41d842cc 100644 --- a/project/settings.py +++ b/project/settings.py @@ -40,6 +40,7 @@ INSTALLED_APPS = [ 'anymail', 'active_link', 'django_filters', + 'mptt', ] + [ 'apps.auth.apps', 'apps.user', diff --git a/requirements.txt b/requirements.txt index 17ee0f7c..f059ea19 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,3 +9,4 @@ Pillow==5.0.0 django-active-link==0.1.2 arrow==0.12.1 django-filter==2.0.0.dev1 +django-mptt==0.9.0