From 214e35e5f0f47e48a10c47d63126577e4d10016a Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Mon, 5 Feb 2018 19:12:13 +0300 Subject: [PATCH 01/45] Fix comments --- .../migrations/0021_auto_20180205_1559.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 apps/course/migrations/0021_auto_20180205_1559.py diff --git a/apps/course/migrations/0021_auto_20180205_1559.py b/apps/course/migrations/0021_auto_20180205_1559.py new file mode 100644 index 00000000..c4a6a6de --- /dev/null +++ b/apps/course/migrations/0021_auto_20180205_1559.py @@ -0,0 +1,35 @@ +# Generated by Django 2.0.2 on 2018-02-05 15:59 + +from django.db import migrations +from django.contrib.contenttypes.models import ContentType + + +def fwrd_func(apps, schema_editor): + CourseComment = apps.get_model('course', 'CourseComment') + LessonComment = apps.get_model('course', 'LessonComment') + db_alias = schema_editor.connection.alias + if CourseComment.objects.exists(): + coursecomment_content_type = ContentType.objects.get( + app_label='course', model='coursecomment', + ) + CourseComment.objects.using(db_alias).all().update( + polymorphic_ctype=coursecomment_content_type, + ) + if LessonComment.objects.exists(): + lessoncomment_content_type = ContentType.objects.get( + app_label='course', model='lessoncomment', + ) + LessonComment.objects.using(db_alias).all().update( + polymorphic_ctype=lessoncomment_content_type, + ) + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0020_auto_20180202_1716'), + ] + + operations = [ + migrations.RunPython(fwrd_func) + ] From 45676787a7648da9b6b8dc83d3f4138702de900c Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Mon, 5 Feb 2018 19:17:24 +0300 Subject: [PATCH 02/45] Fix present comments --- .../migrations/0022_auto_20180205_1615.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 apps/course/migrations/0022_auto_20180205_1615.py diff --git a/apps/course/migrations/0022_auto_20180205_1615.py b/apps/course/migrations/0022_auto_20180205_1615.py new file mode 100644 index 00000000..87bc49b7 --- /dev/null +++ b/apps/course/migrations/0022_auto_20180205_1615.py @@ -0,0 +1,35 @@ +# Generated by Django 2.0.2 on 2018-02-05 16:15 + +from django.db import migrations +from django.contrib.contenttypes.models import ContentType + + +def fwrd_func(apps, schema_editor): + CourseComment = apps.get_model('course', 'CourseComment') + LessonComment = apps.get_model('course', 'LessonComment') + db_alias = schema_editor.connection.alias + if CourseComment.objects.using(db_alias).all().exists(): + coursecomment_content_type = ContentType.objects.get( + app_label='course', model='coursecomment', + ) + CourseComment.objects.using(db_alias).all().update( + polymorphic_ctype=coursecomment_content_type, + ) + if LessonComment.objects.using(db_alias).all().exists(): + lessoncomment_content_type = ContentType.objects.get( + app_label='course', model='lessoncomment', + ) + LessonComment.objects.using(db_alias).all().update( + polymorphic_ctype=lessoncomment_content_type, + ) + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0021_auto_20180205_1559'), + ] + + operations = [ + migrations.RunPython(fwrd_func) + ] From aaec799aa141e9e3ac50c6818199ad635761de8e Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Tue, 6 Feb 2018 15:31:45 +0300 Subject: [PATCH 03/45] Fix base template --- project/templates/lilcity/index.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/project/templates/lilcity/index.html b/project/templates/lilcity/index.html index 7b068127..e60e7dab 100644 --- a/project/templates/lilcity/index.html +++ b/project/templates/lilcity/index.html @@ -126,10 +126,10 @@ - {% if user.is_authenticated %} + {% if request.user.is_authenticated %} - {% if user.is_authenticated and not user.fb_id or user.is_authenticated and not user.is_email_proved %} + {% if request.user.is_authenticated and not request.user.fb_id or request.user.is_authenticated and not request.user.is_email_proved %}
Необходимо подтвердить электронную почту
{% endif %} From a4528d9722cf63d69dd68016ed5811cdb7a1e6ec Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Tue, 6 Feb 2018 15:50:43 +0300 Subject: [PATCH 04/45] Fix celery workers start cmd --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 9465b2a4..f42e5b6b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,7 +22,7 @@ services: restart: always volumes: - .:/lilcity - command: bash -c "python manage.py migrate && python manage.py loaddata /lilcity/apps/*/fixtures/*.json && python manage.py runserver 0.0.0.0:8000 && celery worker -A project -Q web" + command: bash -c "python manage.py migrate && python manage.py loaddata /lilcity/apps/*/fixtures/*.json && python manage.py runserver 0.0.0.0:8000 && celery worker -A project" environment: - DJANGO_SETTINGS_MODULE=project.settings - DATABASE_SERVICE_HOST=db From 71bec97bd6681bc31c65d71f3adcc36b597bb1b1 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Tue, 6 Feb 2018 17:12:48 +0300 Subject: [PATCH 05/45] Add birthday to User model --- apps/user/migrations/0005_user_birthday.py | 18 ++++++++++++++++++ .../user/migrations/0006_auto_20180206_1352.py | 18 ++++++++++++++++++ apps/user/models.py | 3 ++- 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 apps/user/migrations/0005_user_birthday.py create mode 100644 apps/user/migrations/0006_auto_20180206_1352.py diff --git a/apps/user/migrations/0005_user_birthday.py b/apps/user/migrations/0005_user_birthday.py new file mode 100644 index 00000000..479a9294 --- /dev/null +++ b/apps/user/migrations/0005_user_birthday.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.2 on 2018-02-06 13:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('user', '0004_auto_20180129_1259'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='birthday', + field=models.DateField(blank=True, null=True, verbose_name='День рождения'), + ), + ] diff --git a/apps/user/migrations/0006_auto_20180206_1352.py b/apps/user/migrations/0006_auto_20180206_1352.py new file mode 100644 index 00000000..b65c014a --- /dev/null +++ b/apps/user/migrations/0006_auto_20180206_1352.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.2 on 2018-02-06 13:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('user', '0005_user_birthday'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='about', + field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='О себе'), + ), + ] diff --git a/apps/user/models.py b/apps/user/models.py index d5633f8c..5d88315e 100644 --- a/apps/user/models.py +++ b/apps/user/models.py @@ -18,9 +18,10 @@ class User(AbstractUser): email = models.EmailField(_('email address'), unique=True) role = models.PositiveSmallIntegerField('Роль', default=0, choices=ROLE_CHOICES) gender = models.CharField('Пол', max_length=1, default='n', choices=GENDER_CHOICES) + birthday = models.DateField('День рождения', null=True, blank=True) country = models.CharField('Страна', max_length=50, default='') city = models.CharField('Город', max_length=85, default='') - about = models.CharField('О себе', max_length=1000, default='', blank=True) + about = models.CharField('О себе', max_length=1000, null=True, blank=True) instagram = models.URLField(default='', null=True, blank=True) facebook = models.URLField(default='', null=True, blank=True) twitter = models.URLField(default='', null=True, blank=True) From baafd6921019690b8aeacd3b60693013e474f10e Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Tue, 6 Feb 2018 17:47:42 +0300 Subject: [PATCH 06/45] LIL-181. Fix link to edit profile on profile page --- apps/user/templates/user/profile.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/user/templates/user/profile.html b/apps/user/templates/user/profile.html index 2142dca5..c4085f29 100644 --- a/apps/user/templates/user/profile.html +++ b/apps/user/templates/user/profile.html @@ -2,7 +2,7 @@
- Редактировать + Редактировать {% if user.photo %}
From 8a0c785b7af87659e5c9b9f73c6a8f45bfd7e948 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Tue, 6 Feb 2018 17:48:20 +0300 Subject: [PATCH 07/45] LIL-181. Add edit profile template --- .../user/templates/user/profile-settings.html | 185 ++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 apps/user/templates/user/profile-settings.html diff --git a/apps/user/templates/user/profile-settings.html b/apps/user/templates/user/profile-settings.html new file mode 100644 index 00000000..55672fae --- /dev/null +++ b/apps/user/templates/user/profile-settings.html @@ -0,0 +1,185 @@ +{% extends "templates/lilcity/index.html" %} {% load static %} {% block content %} + +{% comment %} + +{% endcomment %} +
+
+
+
+
+
Личные данные
+
+ {% if user.photo %} + + {% else %} + + {% endif %} + +
+ + + +
+
+
+
+
ИМЯ
+
+ +
+
+
+
ФАМИЛИЯ
+
+ +
+
+
+
+
Почта
+
+ +
+
+
+
+
ГОРОД
+
+ +
+
+
+
СТРАНА
+
+ +
+
+
+
+
+
ДАТА РОЖДЕНИЯ
+
+ +
+
+
+
ПОЛ
+
+
+
+ {% if user.gender == 'n' %}М / Ж{% elif user.gender == 'm' %}M{% else %}Ж{% endif %} +
+
+
+
М
+
+
+
Ж
+
+
+ +
+
+
+
+
+
О себе
+
+ +
+
+
+
+
Пароль
+
+
ТЕКУЩИЙ ПАРОЛЬ
+
+ +
+
+
+
НОВЫЙ ПАРОЛЬ
+
+ +
+
+
+
ПОДТВЕРДИТЬ НОВЫЙ ПАРОЛЬ
+
+ +
+
+
+
+
Соцсети
+
+
INSTAGRAM
+
+ +
+
+
+
FACEBOOK
+
+ +
+
+
+
TWITTER
+
+ +
+
+
+
PINTEREST
+
+ +
+
+
+
YOUTUBE
+
+ +
+
+
+
VKONTAKTE
+
+ +
+
+
+
+ +
+
+
+
+
+{% endblock content %} \ No newline at end of file From cb53f51a724d274f44453beed390c01d97a920e2 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Tue, 6 Feb 2018 17:49:27 +0300 Subject: [PATCH 08/45] LIL-183. Add simple user edit view. --- apps/user/views.py | 10 +++++++++- project/urls.py | 3 ++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/user/views.py b/apps/user/views.py index 18d7c9ce..428ca2ce 100644 --- a/apps/user/views.py +++ b/apps/user/views.py @@ -1,5 +1,5 @@ from django.shortcuts import render -from django.views.generic import DetailView +from django.views.generic import DetailView, UpdateView from django.contrib.auth import get_user_model from apps.course.models import Course @@ -16,3 +16,11 @@ class UserView(DetailView): context['published'] = Course.objects.filter(author=self.object, status=Course.PUBLISHED) context['paid'] = Course.objects.none() return context + + +class UserEditView(UpdateView): + model = User + template_name = 'user/profile-settings.html' + fields = ( + 'first_name', + ) diff --git a/project/urls.py b/project/urls.py index 29213e45..586fe045 100644 --- a/project/urls.py +++ b/project/urls.py @@ -24,7 +24,7 @@ from apps.course.views import ( lessoncomment, ) from apps.course.models import Course -from apps.user.views import UserView +from apps.user.views import UserView, UserEditView urlpatterns = [ path('admin/', admin.site.urls), @@ -37,6 +37,7 @@ urlpatterns = [ path('lesson//comment', lessoncomment, name='lessoncomment'), path('search/', SearchView.as_view(), name='search'), path('user//', UserView.as_view(), name='user'), + path('user//edit/', UserEditView.as_view(), name='user-edit'), path('privacy', TemplateView.as_view(template_name="templates/lilcity/privacy_policy.html"), name='privacy'), path('terms', TemplateView.as_view(template_name="templates/lilcity/terms.html"), name='terms'), path('refund-policy', TemplateView.as_view(template_name="templates/lilcity/refund_policy.html"), name='refund_policy'), From 0e6de0b5513689896d3406627e0e2635438f8e47 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Tue, 6 Feb 2018 18:42:00 +0300 Subject: [PATCH 09/45] LIL-175. Add serialized data of nested objects of Course model on list & retrieve actions --- api/v1/serializers.py | 12 +++++++++--- api/v1/views.py | 3 +++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/api/v1/serializers.py b/api/v1/serializers.py index 8ff6f44a..8ec39b54 100644 --- a/api/v1/serializers.py +++ b/api/v1/serializers.py @@ -27,6 +27,7 @@ class MaterialSerializer(serializers.ModelSerializer): read_only_fields = ( 'id', + 'cover', 'created_at', 'update_at', ) @@ -59,9 +60,9 @@ class CategorySerializer(serializers.ModelSerializer): 'title', ) - read_only_fields = ( - 'id', - ) + read_only_fields = ( + 'id', + ) class CourseSerializer(serializers.ModelSerializer): @@ -97,6 +98,11 @@ class CourseSerializer(serializers.ModelSerializer): ) +class CourseRetrieveSerializer(CourseSerializer): + category = CategorySerializer() + materials = MaterialSerializer(many=True) + + class LessonSerializer(serializers.ModelSerializer): class Meta: diff --git a/api/v1/views.py b/api/v1/views.py index 744e7b13..97ff71de 100644 --- a/api/v1/views.py +++ b/api/v1/views.py @@ -15,6 +15,7 @@ from .serializers import ( UserSerializer, UserPhotoSerializer, LessonSerializer, ContentImageSerializer, GalleryImageSerializer, CoverImageSerializer, + CourseRetrieveSerializer, ) from .permissions import IsAdmin, IsAdminOrIsSelf, IsAuthorOrAdmin, IsAuthorObjectOrAdmin @@ -59,6 +60,8 @@ class CourseViewSet(ExtendedModelViewSet): ).all() serializer_class = CourseSerializer serializer_class_map = { + 'list': CourseRetrieveSerializer, + 'retrieve': CourseRetrieveSerializer, 'upload_photo': CoverImageSerializer, } filter_fields = ('category', 'status', 'is_infinite', 'is_featured',) From 03e0729e761c39fd6a8185e58a550065aac16a4c Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Wed, 7 Feb 2018 08:12:24 +0300 Subject: [PATCH 10/45] Add "no cover" img --- project/templates/lilcity/index.html | 2 +- web/build/{ => img}/favicon.ico | Bin web/build/img/no_cover.png | Bin 0 -> 793 bytes web/gulp/tasks/copy.js | 2 +- web/src/img/favicon.ico | Bin 0 -> 1150 bytes web/src/img/no_cover.png | Bin 0 -> 793 bytes 6 files changed, 2 insertions(+), 2 deletions(-) rename web/build/{ => img}/favicon.ico (100%) create mode 100644 web/build/img/no_cover.png create mode 100644 web/src/img/favicon.ico create mode 100644 web/src/img/no_cover.png diff --git a/project/templates/lilcity/index.html b/project/templates/lilcity/index.html index e60e7dab..43ae104b 100644 --- a/project/templates/lilcity/index.html +++ b/project/templates/lilcity/index.html @@ -33,7 +33,7 @@ - + {% endblock content %} \ No newline at end of file From 877d65f5d8e5c60bcc3fdf1fc98c826462b76b21 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Wed, 7 Feb 2018 16:10:14 +0300 Subject: [PATCH 21/45] LIL-183. Add photo field to user edit form --- apps/user/forms.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/user/forms.py b/apps/user/forms.py index b95c951f..697d3910 100644 --- a/apps/user/forms.py +++ b/apps/user/forms.py @@ -23,6 +23,7 @@ class UserEditForm(forms.ModelForm): pinterest = forms.URLField(required=False) youtube = forms.URLField(required=False) vkontakte = forms.URLField(required=False) + photo = forms.ImageField(required=False) class Meta: model = User @@ -44,4 +45,5 @@ class UserEditForm(forms.ModelForm): 'pinterest', 'youtube', 'vkontakte', + 'photo', ) From 839806b0d296f7cac349b2c955167cf6ebcf18e8 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Wed, 7 Feb 2018 16:12:56 +0300 Subject: [PATCH 22/45] LIL-183. Only you can edit your profile --- apps/user/views.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/user/views.py b/apps/user/views.py index 8dc66d18..9ad82013 100644 --- a/apps/user/views.py +++ b/apps/user/views.py @@ -3,7 +3,10 @@ from django.shortcuts import render, reverse from django.views.generic import DetailView, UpdateView from django.contrib import messages from django.contrib.auth import get_user_model +from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.hashers import check_password, make_password +from django.http import Http404 +from django.utils.decorators import method_decorator from apps.course.models import Course @@ -30,9 +33,15 @@ class UserEditView(UpdateView): template_name = 'user/profile-settings.html' form_class = UserEditForm + @method_decorator(login_required) + def dispatch(self, request, *args, **kwargs): + self.object = self.get_object() + if request.user != self.object: + raise Http404() + return super().dispatch(request, *args, **kwargs) + def post(self, request, *args, **kwargs): # it's magic *-*-*-*-* - self.object = self.get_object() if not request.POST._mutable: request.POST._mutable = True old_password = request.POST.pop('old_password')[0] From ddab06639b72308d3d0951bdaf796b0c6565c8a6 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Wed, 7 Feb 2018 17:10:29 +0300 Subject: [PATCH 23/45] Crop image at center on backend --- apps/user/views.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/apps/user/views.py b/apps/user/views.py index 9ad82013..3b3f1daa 100644 --- a/apps/user/views.py +++ b/apps/user/views.py @@ -1,3 +1,6 @@ +from io import BytesIO +from PIL import Image +from os.path import splitext from django.contrib.auth import login from django.shortcuts import render, reverse from django.views.generic import DetailView, UpdateView @@ -42,6 +45,30 @@ class UserEditView(UpdateView): def post(self, request, *args, **kwargs): # it's magic *-*-*-*-* + if 'photo' in request.FILES: + photo_fp = request.FILES.pop('photo')[0] + fname = photo_fp.name + photo = Image.open(photo_fp) + lowest_side = min(photo.size) + horizontal_padding = (lowest_side - photo.size[0]) / 2 + vertical_padding = (lowest_side - photo.size[1]) / 2 + photo = photo.crop( + ( + -horizontal_padding, + -vertical_padding, + photo.size[0] + horizontal_padding, + photo.size[1] + vertical_padding + ) + ) + if photo.size[0] > 512: + photo = photo.resize((512, 512,)) + buffer = BytesIO() + ext = splitext(fname)[1][1:].upper() + if ext == 'JPG': + ext = 'JPEG' + photo.save(buffer, ext) + self.object.photo.save(fname, buffer) + buffer.close() if not request.POST._mutable: request.POST._mutable = True old_password = request.POST.pop('old_password')[0] From 60097d36ca4db5bd1eced208ab861766af0fd1f7 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Wed, 7 Feb 2018 22:14:53 +0300 Subject: [PATCH 24/45] Remove not needed migrations --- .../migrations/0021_auto_20180205_1559.py | 35 ------------------- .../migrations/0022_auto_20180205_1615.py | 35 ------------------- 2 files changed, 70 deletions(-) delete mode 100644 apps/course/migrations/0021_auto_20180205_1559.py delete mode 100644 apps/course/migrations/0022_auto_20180205_1615.py diff --git a/apps/course/migrations/0021_auto_20180205_1559.py b/apps/course/migrations/0021_auto_20180205_1559.py deleted file mode 100644 index c4a6a6de..00000000 --- a/apps/course/migrations/0021_auto_20180205_1559.py +++ /dev/null @@ -1,35 +0,0 @@ -# Generated by Django 2.0.2 on 2018-02-05 15:59 - -from django.db import migrations -from django.contrib.contenttypes.models import ContentType - - -def fwrd_func(apps, schema_editor): - CourseComment = apps.get_model('course', 'CourseComment') - LessonComment = apps.get_model('course', 'LessonComment') - db_alias = schema_editor.connection.alias - if CourseComment.objects.exists(): - coursecomment_content_type = ContentType.objects.get( - app_label='course', model='coursecomment', - ) - CourseComment.objects.using(db_alias).all().update( - polymorphic_ctype=coursecomment_content_type, - ) - if LessonComment.objects.exists(): - lessoncomment_content_type = ContentType.objects.get( - app_label='course', model='lessoncomment', - ) - LessonComment.objects.using(db_alias).all().update( - polymorphic_ctype=lessoncomment_content_type, - ) - - -class Migration(migrations.Migration): - - dependencies = [ - ('course', '0020_auto_20180202_1716'), - ] - - operations = [ - migrations.RunPython(fwrd_func) - ] diff --git a/apps/course/migrations/0022_auto_20180205_1615.py b/apps/course/migrations/0022_auto_20180205_1615.py deleted file mode 100644 index 87bc49b7..00000000 --- a/apps/course/migrations/0022_auto_20180205_1615.py +++ /dev/null @@ -1,35 +0,0 @@ -# Generated by Django 2.0.2 on 2018-02-05 16:15 - -from django.db import migrations -from django.contrib.contenttypes.models import ContentType - - -def fwrd_func(apps, schema_editor): - CourseComment = apps.get_model('course', 'CourseComment') - LessonComment = apps.get_model('course', 'LessonComment') - db_alias = schema_editor.connection.alias - if CourseComment.objects.using(db_alias).all().exists(): - coursecomment_content_type = ContentType.objects.get( - app_label='course', model='coursecomment', - ) - CourseComment.objects.using(db_alias).all().update( - polymorphic_ctype=coursecomment_content_type, - ) - if LessonComment.objects.using(db_alias).all().exists(): - lessoncomment_content_type = ContentType.objects.get( - app_label='course', model='lessoncomment', - ) - LessonComment.objects.using(db_alias).all().update( - polymorphic_ctype=lessoncomment_content_type, - ) - - -class Migration(migrations.Migration): - - dependencies = [ - ('course', '0021_auto_20180205_1559'), - ] - - operations = [ - migrations.RunPython(fwrd_func) - ] From a17c49ce4bfe2cf41c96b2785afb95a1957d4b8b Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Wed, 7 Feb 2018 22:17:58 +0300 Subject: [PATCH 25/45] LIL-223. Add content to Course serializer --- api/v1/serializers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/v1/serializers.py b/api/v1/serializers.py index 8ec39b54..cc16d8e8 100644 --- a/api/v1/serializers.py +++ b/api/v1/serializers.py @@ -88,11 +88,13 @@ class CourseSerializer(serializers.ModelSerializer): 'materials', 'created_at', 'update_at', + 'content', ) read_only_fields = ( 'id', 'cover', + 'content', 'created_at', 'update_at', ) From 9d699ed59e804f28d41bf33da9d706b0ac01209a Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Wed, 7 Feb 2018 22:18:47 +0300 Subject: [PATCH 26/45] LIL-223. Add related name for course & lesson fields of Course model --- apps/content/models.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/content/models.py b/apps/content/models.py index aa5e87a8..e5eefe7e 100644 --- a/apps/content/models.py +++ b/apps/content/models.py @@ -9,12 +9,14 @@ class Content(PolymorphicModel): course = models.ForeignKey( Course, on_delete=models.CASCADE, null=True, blank=True, - verbose_name='Курс' + verbose_name='Курс', + related_name='content', ) lesson = models.ForeignKey( Lesson, on_delete=models.CASCADE, null=True, blank=True, - verbose_name='Урок' + verbose_name='Урок', + related_name='content', ) title = models.CharField('Заголовок', max_length=100, default='') position = models.PositiveSmallIntegerField( From fd2fb657b6d6db6daa401671fe2fb37bda923757 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Wed, 7 Feb 2018 22:19:38 +0300 Subject: [PATCH 27/45] LIL-223. Add content to prefetch related in Course viewset --- api/v1/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/v1/views.py b/api/v1/views.py index 97ff71de..3cfeaa5e 100644 --- a/api/v1/views.py +++ b/api/v1/views.py @@ -56,7 +56,7 @@ class CourseViewSet(ExtendedModelViewSet): queryset = Course.objects.select_related( 'author', 'category' ).prefetch_related( - 'likes', 'materials' + 'likes', 'materials', 'content', ).all() serializer_class = CourseSerializer serializer_class_map = { From b02aa5c273d8cbe44f953e02e5e906388b666270 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Wed, 7 Feb 2018 22:21:06 +0300 Subject: [PATCH 28/45] More fastest query Course list --- apps/course/views.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/course/views.py b/apps/course/views.py index c3f6f05b..cd5fd64a 100644 --- a/apps/course/views.py +++ b/apps/course/views.py @@ -177,7 +177,11 @@ class CoursesView(ListView): return super().get(request, args, kwargs) def get_queryset(self): - queryset = super().get_queryset() + queryset = super().get_queryset().select_related( + 'author', 'category' + ).prefetch_related( + 'likes', 'materials', 'content', + ) filtered = CourseFilter(self.request.GET, queryset=queryset) return filtered.qs From 325abf5a9abc521c65951efc12aeb9ff31a32a81 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Wed, 7 Feb 2018 22:21:28 +0300 Subject: [PATCH 29/45] Add static files url patterns --- project/urls.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/project/urls.py b/project/urls.py index fcb9b717..5e5d2e8e 100644 --- a/project/urls.py +++ b/project/urls.py @@ -47,6 +47,9 @@ urlpatterns = [ if settings.DEBUG: - urlpatterns += [path('silk/', include('silk.urls', namespace='silk'))] from django.conf.urls.static import static + from django.contrib.staticfiles.urls import staticfiles_urlpatterns + + urlpatterns += [path('silk/', include('silk.urls', namespace='silk'))] urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + urlpatterns += staticfiles_urlpatterns() From bed9b3eb9884365518000a25547d81a40e84f16c Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Thu, 8 Feb 2018 08:23:58 +0300 Subject: [PATCH 30/45] LIL-224. Add ImageObject model --- .../migrations/0005_auto_20180208_0520.py | 31 +++++++++++++++++++ apps/content/models.py | 4 +++ 2 files changed, 35 insertions(+) create mode 100644 apps/content/migrations/0005_auto_20180208_0520.py diff --git a/apps/content/migrations/0005_auto_20180208_0520.py b/apps/content/migrations/0005_auto_20180208_0520.py new file mode 100644 index 00000000..a739f51f --- /dev/null +++ b/apps/content/migrations/0005_auto_20180208_0520.py @@ -0,0 +1,31 @@ +# Generated by Django 2.0.2 on 2018-02-08 05:20 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('content', '0004_gallery_galleryimage'), + ] + + operations = [ + migrations.CreateModel( + name='ImageObject', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('image', models.ImageField(upload_to='content/imageobject', verbose_name='Изображение')), + ], + ), + migrations.AlterField( + model_name='content', + name='course', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='content', to='course.Course', verbose_name='Курс'), + ), + migrations.AlterField( + model_name='content', + name='lesson', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='content', to='course.Lesson', verbose_name='Урок'), + ), + ] diff --git a/apps/content/models.py b/apps/content/models.py index e5eefe7e..240a618e 100644 --- a/apps/content/models.py +++ b/apps/content/models.py @@ -5,6 +5,10 @@ from polymorphic.models import PolymorphicModel from apps.course.models import Course, Lesson +class ImageObject(models.Model): + image = models.ImageField('Изображение', upload_to='content/imageobject') + + class Content(PolymorphicModel): course = models.ForeignKey( Course, on_delete=models.CASCADE, From 75d59cf622e64eda0da3d842f8574c4cc5062171 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Thu, 8 Feb 2018 08:37:46 +0300 Subject: [PATCH 31/45] LIL-224. Add serializer, viewset, route, admin for ImageObject model --- api/v1/serializers.py | 19 ++++++++++++++++++- api/v1/urls.py | 3 ++- api/v1/views.py | 10 ++++++++-- apps/content/admin.py | 7 ++++++- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/api/v1/serializers.py b/api/v1/serializers.py index cc16d8e8..1ec91a21 100644 --- a/api/v1/serializers.py +++ b/api/v1/serializers.py @@ -6,12 +6,29 @@ from . import Base64ImageField from apps.course.models import Category, Course, Material, Lesson, Like from apps.content.models import ( Image, Text, ImageText, Video, - Gallery, GalleryImage, + Gallery, GalleryImage, ImageObject, ) User = get_user_model() +class ImageObjectSerializer(serializers.ModelSerializer): + image = Base64ImageField( + required=True, allow_empty_file=False, allow_null=False, + ) + + class Meta: + model = ImageObject + fields = ( + 'id', + 'image', + ) + + read_only_fields = ( + 'id', + ) + + class MaterialSerializer(serializers.ModelSerializer): class Meta: diff --git a/api/v1/urls.py b/api/v1/urls.py index 6483f23d..846af2ae 100644 --- a/api/v1/urls.py +++ b/api/v1/urls.py @@ -12,7 +12,7 @@ from .views import ( ImageViewSet, TextViewSet, ImageTextViewSet, VideoViewSet, GalleryViewSet, GalleryImageViewSet, - UserViewSet, LessonViewSet, + UserViewSet, LessonViewSet, ImageObjectViewSet, ) router = DefaultRouter() @@ -22,6 +22,7 @@ router.register(r'materials', MaterialViewSet, base_name='materials') router.register(r'lessons', LessonViewSet, base_name='lessons') router.register(r'likes', LikeViewSet, base_name='likes') +router.register(r'image-objects', ImageObjectViewSet, base_name='image-objects') router.register(r'images', ImageViewSet, base_name='images') router.register(r'texts', TextViewSet, base_name='texts') router.register(r'image-texts', ImageTextViewSet, base_name='image-texts') diff --git a/api/v1/views.py b/api/v1/views.py index 3cfeaa5e..7ecb780d 100644 --- a/api/v1/views.py +++ b/api/v1/views.py @@ -15,19 +15,25 @@ from .serializers import ( UserSerializer, UserPhotoSerializer, LessonSerializer, ContentImageSerializer, GalleryImageSerializer, CoverImageSerializer, - CourseRetrieveSerializer, + CourseRetrieveSerializer, ImageObjectSerializer, ) from .permissions import IsAdmin, IsAdminOrIsSelf, IsAuthorOrAdmin, IsAuthorObjectOrAdmin from apps.course.models import Category, Course, Material, Lesson, Like from apps.content.models import ( Image, Text, ImageText, Video, - Gallery, GalleryImage, + Gallery, GalleryImage, ImageObject, ) User = get_user_model() +class ImageObjectViewSet(ExtendedModelViewSet): + queryset = ImageObject.objects.all() + serializer_class = ImageObjectSerializer + # permission_classes = (IsAuthorOrAdmin,) + + class MaterialViewSet(ExtendedModelViewSet): queryset = Material.objects.all() serializer_class = MaterialSerializer diff --git a/apps/content/admin.py b/apps/content/admin.py index 53619829..88c5a8bc 100644 --- a/apps/content/admin.py +++ b/apps/content/admin.py @@ -7,10 +7,15 @@ from polymorphic.admin import ( from apps.content.models import ( Content, Image, Text, ImageText, Video, - Gallery, GalleryImage, + Gallery, GalleryImage, ImageObject, ) +@admin.register(ImageObject) +class ImageObjectAdmin(admin.ModelAdmin): + pass + + class ContentChildAdmin(PolymorphicChildModelAdmin): base_model = Content From dabff59eb5662bb46cf15037a5acabfc82b88686 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Thu, 8 Feb 2018 08:53:06 +0300 Subject: [PATCH 32/45] LIL-224. Add datable fields to ImageObject model --- api/v1/serializers.py | 6 +++- .../migrations/0006_auto_20180208_0551.py | 29 +++++++++++++++++++ apps/content/models.py | 8 +++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 apps/content/migrations/0006_auto_20180208_0551.py diff --git a/api/v1/serializers.py b/api/v1/serializers.py index 1ec91a21..ca2b77f6 100644 --- a/api/v1/serializers.py +++ b/api/v1/serializers.py @@ -14,7 +14,7 @@ User = get_user_model() class ImageObjectSerializer(serializers.ModelSerializer): image = Base64ImageField( - required=True, allow_empty_file=False, allow_null=False, + required=True, allow_empty_file=False, allow_null=False, read_only=False, ) class Meta: @@ -22,10 +22,14 @@ class ImageObjectSerializer(serializers.ModelSerializer): fields = ( 'id', 'image', + 'created_at', + 'update_at', ) read_only_fields = ( 'id', + 'created_at', + 'update_at', ) diff --git a/apps/content/migrations/0006_auto_20180208_0551.py b/apps/content/migrations/0006_auto_20180208_0551.py new file mode 100644 index 00000000..361a2d49 --- /dev/null +++ b/apps/content/migrations/0006_auto_20180208_0551.py @@ -0,0 +1,29 @@ +# Generated by Django 2.0.2 on 2018-02-08 05:51 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('content', '0005_auto_20180208_0520'), + ] + + operations = [ + migrations.AlterModelOptions( + name='imageobject', + options={'ordering': ('-created_at',), 'verbose_name': 'Объект изображения', 'verbose_name_plural': 'Объекты изображения'}, + ), + migrations.AddField( + model_name='imageobject', + name='created_at', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='imageobject', + name='update_at', + field=models.DateTimeField(auto_now=True), + ), + ] diff --git a/apps/content/models.py b/apps/content/models.py index 240a618e..64b1c200 100644 --- a/apps/content/models.py +++ b/apps/content/models.py @@ -8,6 +8,14 @@ from apps.course.models import Course, Lesson class ImageObject(models.Model): image = models.ImageField('Изображение', upload_to='content/imageobject') + created_at = models.DateTimeField(auto_now_add=True) + update_at = models.DateTimeField(auto_now=True) + + class Meta: + verbose_name = 'Объект изображения' + verbose_name_plural = 'Объекты изображения' + ordering = ('-created_at',) + class Content(PolymorphicModel): course = models.ForeignKey( From 74364234740bc2c6bc56c490af0876630f3b5254 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Thu, 8 Feb 2018 08:58:18 +0300 Subject: [PATCH 33/45] LIL-224. Update ImageObject admin --- apps/content/admin.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/content/admin.py b/apps/content/admin.py index 88c5a8bc..bb935e08 100644 --- a/apps/content/admin.py +++ b/apps/content/admin.py @@ -13,7 +13,12 @@ from apps.content.models import ( @admin.register(ImageObject) class ImageObjectAdmin(admin.ModelAdmin): - pass + list_display = ( + 'id', + 'image', + 'created_at', + 'update_at', + ) class ContentChildAdmin(PolymorphicChildModelAdmin): From 3a1260d7d88163071d395ac57b389a931a4db05e Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Thu, 8 Feb 2018 09:30:25 +0300 Subject: [PATCH 34/45] Update Image(Content) model --- api/v1/serializers.py | 5 +++-- .../migrations/0007_auto_20180208_0626.py | 19 +++++++++++++++++++ apps/content/models.py | 5 ++++- 3 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 apps/content/migrations/0007_auto_20180208_0626.py diff --git a/api/v1/serializers.py b/api/v1/serializers.py index ca2b77f6..2a8b8eca 100644 --- a/api/v1/serializers.py +++ b/api/v1/serializers.py @@ -149,6 +149,7 @@ class LessonSerializer(serializers.ModelSerializer): class ImageSerializer(serializers.ModelSerializer): + img = ImageObjectSerializer() class Meta: model = Image @@ -158,13 +159,13 @@ class ImageSerializer(serializers.ModelSerializer): 'lesson', 'title', 'position', + 'img', 'created_at', 'update_at', - ) + ('img',) + ) read_only_fields = ( 'id', - 'img', 'created_at', 'update_at', ) diff --git a/apps/content/migrations/0007_auto_20180208_0626.py b/apps/content/migrations/0007_auto_20180208_0626.py new file mode 100644 index 00000000..acba3e98 --- /dev/null +++ b/apps/content/migrations/0007_auto_20180208_0626.py @@ -0,0 +1,19 @@ +# Generated by Django 2.0.2 on 2018-02-08 06:26 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('content', '0006_auto_20180208_0551'), + ] + + operations = [ + migrations.AlterField( + model_name='image', + name='img', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='content_images', to='content.ImageObject', verbose_name='Объект изображения'), + ), + ] diff --git a/apps/content/models.py b/apps/content/models.py index 64b1c200..75b6147e 100644 --- a/apps/content/models.py +++ b/apps/content/models.py @@ -46,7 +46,10 @@ class Content(PolymorphicModel): class Image(Content): - img = models.ImageField('Изображение', upload_to='content/images') + img = models.ForeignKey( + ImageObject, related_name='content_images', + verbose_name='Объект изображения', on_delete=models.CASCADE, + ) class Text(Content): From 28ebc6b1eedd100e40ddb92cb2ae92dd14e92e1f Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Thu, 8 Feb 2018 09:32:58 +0300 Subject: [PATCH 35/45] Update ImageText(Content) model --- api/v1/serializers.py | 6 ++++-- .../migrations/0008_auto_20180208_0631.py | 19 +++++++++++++++++++ apps/content/models.py | 5 ++++- 3 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 apps/content/migrations/0008_auto_20180208_0631.py diff --git a/api/v1/serializers.py b/api/v1/serializers.py index 2a8b8eca..1468313f 100644 --- a/api/v1/serializers.py +++ b/api/v1/serializers.py @@ -193,6 +193,7 @@ class TextSerializer(serializers.ModelSerializer): class ImageTextSerializer(serializers.ModelSerializer): + img = ImageObjectSerializer() class Meta: model = ImageText @@ -202,13 +203,14 @@ class ImageTextSerializer(serializers.ModelSerializer): 'lesson', 'title', 'position', + 'img', + 'txt', 'created_at', 'update_at', - ) + ('img', 'txt',) + ) read_only_fields = ( 'id', - 'img', 'created_at', 'update_at', ) diff --git a/apps/content/migrations/0008_auto_20180208_0631.py b/apps/content/migrations/0008_auto_20180208_0631.py new file mode 100644 index 00000000..3f8b023e --- /dev/null +++ b/apps/content/migrations/0008_auto_20180208_0631.py @@ -0,0 +1,19 @@ +# Generated by Django 2.0.2 on 2018-02-08 06:31 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('content', '0007_auto_20180208_0626'), + ] + + operations = [ + migrations.AlterField( + model_name='imagetext', + name='img', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='content_imagetexts', to='content.ImageObject', verbose_name='Объект изображения'), + ), + ] diff --git a/apps/content/models.py b/apps/content/models.py index 75b6147e..cc4bdd06 100644 --- a/apps/content/models.py +++ b/apps/content/models.py @@ -57,7 +57,10 @@ class Text(Content): class ImageText(Content): - img = models.ImageField('Изображение', upload_to='content/images') + img = models.ForeignKey( + ImageObject, related_name='content_imagetexts', + verbose_name='Объект изображения', on_delete=models.CASCADE, + ) txt = models.TextField('Текст', default='') From 2d5b221349310561dd61d1fe9677f83cca386d33 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Thu, 8 Feb 2018 09:37:52 +0300 Subject: [PATCH 36/45] Update GalleryImage model --- api/v1/serializers.py | 4 ++-- .../migrations/0009_auto_20180208_0637.py | 23 +++++++++++++++++++ apps/content/models.py | 6 +++-- 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 apps/content/migrations/0009_auto_20180208_0637.py diff --git a/api/v1/serializers.py b/api/v1/serializers.py index 1468313f..5bd197f0 100644 --- a/api/v1/serializers.py +++ b/api/v1/serializers.py @@ -257,20 +257,20 @@ class GallerySerializer(serializers.ModelSerializer): class GalleryImageSerializer(serializers.ModelSerializer): + img = ImageObjectSerializer() class Meta: model = GalleryImage fields = ( 'id', 'gallery', - 'image', + 'img', 'created_at', 'update_at', ) read_only_fields = ( 'id', - 'image', 'created_at', 'update_at', ) diff --git a/apps/content/migrations/0009_auto_20180208_0637.py b/apps/content/migrations/0009_auto_20180208_0637.py new file mode 100644 index 00000000..f96f55f6 --- /dev/null +++ b/apps/content/migrations/0009_auto_20180208_0637.py @@ -0,0 +1,23 @@ +# Generated by Django 2.0.2 on 2018-02-08 06:37 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('content', '0008_auto_20180208_0631'), + ] + + operations = [ + migrations.RemoveField( + model_name='galleryimage', + name='image', + ), + migrations.AddField( + model_name='galleryimage', + name='img', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='gallery_images', to='content.ImageObject', verbose_name='Объект изображения'), + ), + ] diff --git a/apps/content/models.py b/apps/content/models.py index cc4bdd06..21b63151 100644 --- a/apps/content/models.py +++ b/apps/content/models.py @@ -90,8 +90,10 @@ class GalleryImage(models.Model): Gallery, on_delete=models.CASCADE, verbose_name='Галерея' ) - image = models.ImageField( - 'Изображение', upload_to='content/gallery_images' + img = models.ForeignKey( + ImageObject, related_name='gallery_images', + verbose_name='Объект изображения', on_delete=models.CASCADE, + null=True, blank=True, ) created_at = models.DateTimeField(auto_now_add=True) From 0a0f03b95c1a67c84753e9b97a050a22f43c6ae6 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Thu, 8 Feb 2018 11:22:44 +0300 Subject: [PATCH 37/45] Update Course model --- .../migrations/0022_auto_20180208_0647.py | 19 +++++++++++++++ apps/course/models.py | 23 +++++++++++++++---- 2 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 apps/course/migrations/0022_auto_20180208_0647.py diff --git a/apps/course/migrations/0022_auto_20180208_0647.py b/apps/course/migrations/0022_auto_20180208_0647.py new file mode 100644 index 00000000..c95cd3fc --- /dev/null +++ b/apps/course/migrations/0022_auto_20180208_0647.py @@ -0,0 +1,19 @@ +# Generated by Django 2.0.2 on 2018-02-08 06:47 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0021_auto_20180206_0632'), + ] + + operations = [ + migrations.AlterField( + model_name='course', + name='cover', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='course_covers', to='content.ImageObject', verbose_name='Обложка курса'), + ), + ] diff --git a/apps/course/models.py b/apps/course/models.py index 76c46756..238fc1b0 100644 --- a/apps/course/models.py +++ b/apps/course/models.py @@ -7,6 +7,8 @@ from polymorphic_tree.models import PolymorphicMPTTModel, PolymorphicTreeForeign from .manager import CategoryQuerySet +from apps.content.models import ImageObject + User = get_user_model() @@ -30,10 +32,20 @@ class Course(models.Model): title = models.CharField('Название курса', max_length=100, db_index=True) short_description = models.TextField('Краткое описание курса', db_index=True) 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) + cover = models.ForeignKey( + ImageObject, related_name='course_covers', + verbose_name='Обложка курса', on_delete=models.CASCADE, + null=True, blank=True, + ) + 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) + 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) @@ -127,7 +139,10 @@ class Material(models.Model): class Comment(PolymorphicMPTTModel): content = models.TextField('Текст комментария', default='') author = models.ForeignKey(User, on_delete=models.CASCADE) - parent = PolymorphicTreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True, on_delete=models.PROTECT) + parent = PolymorphicTreeForeignKey( + 'self', null=True, blank=True, related_name='children', + db_index=True, on_delete=models.PROTECT + ) created_at = models.DateTimeField(auto_now_add=True) update_at = models.DateTimeField(auto_now=True) From 41c377f6dfe49e58e735e91b0057f6dfd0a05960 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Thu, 8 Feb 2018 11:23:20 +0300 Subject: [PATCH 38/45] Add Course migration and fix fixtures --- apps/course/fixtures/course.json | 17 ----------------- .../migrations/0023_auto_20180208_0714.py | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 17 deletions(-) create mode 100644 apps/course/migrations/0023_auto_20180208_0714.py diff --git a/apps/course/fixtures/course.json b/apps/course/fixtures/course.json index 52f5bba3..cc3d2a0f 100644 --- a/apps/course/fixtures/course.json +++ b/apps/course/fixtures/course.json @@ -7,7 +7,6 @@ "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", "from_author": "", - "cover": "courses/pic-1_sTaZawQ.jpg", "price": "1500.00", "is_infinite": false, "deferred_start_at": null, @@ -30,7 +29,6 @@ "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", "from_author": "", - "cover": "courses/pic-1_sTaZawQ.jpg", "price": "1900.00", "is_infinite": false, "deferred_start_at": null, @@ -53,7 +51,6 @@ "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", "from_author": "", - "cover": "courses/pic-1_sTaZawQ.jpg", "price": "100.00", "is_infinite": false, "deferred_start_at": null, @@ -76,7 +73,6 @@ "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", "from_author": "", - "cover": "courses/pic-1_sTaZawQ.jpg", "price": "400.00", "is_infinite": false, "deferred_start_at": null, @@ -99,7 +95,6 @@ "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", "from_author": "", - "cover": "courses/pic-1_sTaZawQ.jpg", "price": "1800.00", "is_infinite": false, "deferred_start_at": null, @@ -122,7 +117,6 @@ "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", "from_author": "", - "cover": "courses/pic-1_sTaZawQ.jpg", "price": "100.00", "is_infinite": false, "deferred_start_at": null, @@ -145,7 +139,6 @@ "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", "from_author": "", - "cover": "courses/pic-1_sTaZawQ.jpg", "price": "1600.00", "is_infinite": false, "deferred_start_at": null, @@ -168,7 +161,6 @@ "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", "from_author": "", - "cover": "courses/pic-1_sTaZawQ.jpg", "price": "1900.00", "is_infinite": false, "deferred_start_at": null, @@ -191,7 +183,6 @@ "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", "from_author": "", - "cover": "courses/pic-1_sTaZawQ.jpg", "price": "200.00", "is_infinite": false, "deferred_start_at": null, @@ -214,7 +205,6 @@ "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", "from_author": "", - "cover": "courses/pic-1_sTaZawQ.jpg", "price": "800.00", "is_infinite": false, "deferred_start_at": null, @@ -241,7 +231,6 @@ "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", "from_author": "", - "cover": "courses/pic-1_sTaZawQ.jpg", "price": "100.00", "is_infinite": false, "deferred_start_at": "2018-02-28T12:00:00Z", @@ -330,7 +319,6 @@ "title": "1 \u0423\u0420\u041e\u041a", "short_description": "\u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0441\u044e\u0436\u0435\u0442, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430 \u043a\u0443\u0440\u0441\u0435 \u0438 \u0433\u043b\u0430\u0432\u043d\u043e\u0433\u043e \u0433\u0435\u0440\u043e\u044f \u0432\u0430\u0448\u0435\u0439 \u0438\u0441\u0442\u043e\u0440\u0438\u0438. \u0421 \u044d\u0442\u0438\u043c \u0433\u0435\u0440\u043e\u0435\u043c \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430 \u043f\u0440\u043e\u0442\u044f\u0436\u0435\u043d\u0438\u0438 \u0432\u0441\u0435\u0433\u043e \u043a\u0443\u0440\u0441\u0430.", "course": 11, - "cover": "lessons/kat-watercolor.jpg", "created_at": "2018-01-31T15:06:14.830Z", "update_at": "2018-01-31T15:06:14.830Z" } @@ -342,7 +330,6 @@ "title": "2 \u0423\u0420\u041e\u041a", "short_description": "\u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0438 \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u0441 \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0433\u0435\u0440\u043e\u044f, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u043d\u044b\u0445 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0435\u0439 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0430. \u041f\u043e \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0443 \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u00ab\u0440\u0435\u0444\u0435\u0440\u0435\u043d\u0441\u044b\u00bb. \u0420\u0438\u0441\u0443\u0435\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u043e\u0431\u0440\u0430\u0437\u0430 \u0432 \u0441\u0432\u043e\u0435\u043c \u0441\u0442\u0438\u043b\u0435.\r\n\r\n\u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u0438\u0437 \u043d\u0438\u0445 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0430. \u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0441\u0430\u043c\u044b\u0435 \u0443\u0434\u0430\u0447\u043d\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b, \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u043c \u0438\u0445 \u0432 \u043e\u0434\u043d\u043e\u043c \u043d\u0430\u0431\u0440\u043e\u0441\u043a\u0435.", "course": 11, - "cover": "lessons/kat-watercolor_SA9juHa.jpg", "created_at": "2018-01-31T15:06:46.772Z", "update_at": "2018-01-31T15:06:46.772Z" } @@ -354,7 +341,6 @@ "title": "3 \u0423\u0420\u041e\u041a", "short_description": "\u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u043e\u0436\u0438\u0432\u0438\u0442\u044c \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0430. \u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c 5 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u043d\u044b\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0434\u043b\u044f \u0432\u0430\u0448\u0435\u0433\u043e \u0433\u0435\u0440\u043e\u044f \u0438 \u0442\u043e\u0433\u043e \u0441\u044e\u0436\u0435\u0442\u0430, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043e\u043d \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0443\u0447\u0430\u0441\u0442\u0438\u0435, \u0440\u0438\u0441\u0443\u0435\u043c \u044d\u0441\u043a\u0438\u0437\u044b \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0430 \u0432 \u0440\u0430\u0437\u043d\u044b\u0445 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u043d\u044b\u0445 \u0434\u043b\u044f \u043d\u0435\u0433\u043e \u043f\u043e\u0437\u0430\u0445 \u0438 \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u0438.\r\n\r\n\u0412\u044b\u0434\u0435\u043b\u044f\u0435\u043c 5 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u043d\u044b\u0445 \u044d\u043c\u043e\u0446\u0438\u0439 \u0434\u043b\u044f \u0433\u0435\u0440\u043e\u044f, \u043d\u0430\u0434 \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c. \u041f\u043e \u044d\u043c\u043e\u0446\u0438\u044f\u043c \u043f\u043e\u0434\u0431\u0438\u0440\u0430\u0435\u043c \u0440\u0435\u0444\u0435\u0440\u0435\u043d\u0441\u044b \u0438 \u0441\u0442\u0438\u043b\u0438\u0437\u0443\u0435\u043c \u0438\u0445 \u0432 \u0441\u0432\u043e\u0435\u043c \u0441\u0442\u0438\u043b\u0435.", "course": 11, - "cover": "lessons/kat-watercolor_QYFi9sq.jpg", "created_at": "2018-01-31T15:07:08.979Z", "update_at": "2018-01-31T15:07:08.979Z" } @@ -364,7 +350,6 @@ "pk": 1, "fields": { "title": "\u0411\u0443\u043c\u0430\u0433\u0430 \u0430\u043a\u0432\u0430\u0440\u0435\u043b\u044c\u043d\u0430\u044f", - "cover": "materials/kat-watercolor.jpg", "short_description": "\u0411\u0443\u043c\u0430\u0433\u0430 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0430\u043a\u0432\u0430\u0440\u0435\u043b\u044c\u044e \u0438\u043c\u0435\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435. \u042d\u0442\u043e \u043e\u0431\u044a\u044f\u0441\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u0435\u043c, \u0447\u0442\u043e \u0430\u043a\u0432\u0430\u0440\u0435\u043b\u044c \u2014 \u043a\u0440\u0430\u0441\u043a\u0430 \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u0430\u044f, \u0430 \u0437\u043d\u0430\u0447\u0438\u0442 \u0444\u0430\u043a\u0442\u0443\u0440\u0430 \u0431\u0443\u043c\u0430\u0433\u0438 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0441\u0438\u043b\u044c\u043d\u043e \u0432\u043b\u0438\u044f\u0442\u044c \u043d\u0430 \u0432\u043d\u0435\u0448\u043d\u0438\u0439 \u0432\u0438\u0434 \u043a\u0440\u0430\u0441\u043e\u0447\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u044f.", "created_at": "2018-01-31T14:55:48.394Z", "update_at": "2018-01-31T14:55:48.394Z" @@ -375,7 +360,6 @@ "pk": 2, "fields": { "title": "\u041a\u0438\u0441\u0442\u043e\u0447\u043a\u0438 \u0434\u043b\u044f \u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0438\u044f", - "cover": "materials/shutterstock_125323070-700x861.jpg", "short_description": "\u041a\u0438\u0441\u0442\u044c \u2014 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u043f\u043e\u043a\u0440\u0430\u0441\u043a\u0438 \u0438 \u0436\u0438\u0432\u043e\u043f\u0438\u0441\u0438. \u041a\u0438\u0441\u0442\u0438 \u0434\u0435\u043b\u0430\u044e\u0442\u0441\u044f \u0438\u0437 \u0449\u0435\u0442\u0438\u043d\u044b \u0438 \u0445\u0432\u043e\u0441\u0442\u043e\u0432\u044b\u0445 \u0432\u043e\u043b\u043e\u0441\u043a\u043e\u0432 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0436\u0438\u0432\u043e\u0442\u043d\u044b\u0445.", "created_at": "2018-01-31T14:57:37.751Z", "update_at": "2018-01-31T14:57:37.751Z" @@ -386,7 +370,6 @@ "pk": 3, "fields": { "title": "\u041a\u0440\u0430\u0441\u043a\u0438 \u0430\u043a\u0432\u0430\u0440\u0435\u043b\u044c\u043d\u044b\u0435", - "cover": "materials/\u043a\u0440\u0430\u0441\u043a\u0438.jpeg", "short_description": "\u0417\u0430\u0432\u043e\u0434 \u0445\u0443\u0434\u043e\u0436\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u043a\u0440\u0430\u0441\u043e\u043a \u00ab\u041d\u0435\u0432\u0441\u043a\u0430\u044f \u043f\u0430\u043b\u0438\u0442\u0440\u0430\u00bb \u0432\u044b\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0430\u043a\u0432\u0430\u0440\u0435\u043b\u044c 80 \u043b\u0435\u0442, \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044f \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0441\u0442\u0432\u0430 \u043f\u0440\u043e\u0434\u0443\u043a\u0446\u0438\u0438 \u0432\u044b\u0441\u043e\u0447\u0430\u0439\u0448\u0435\u0433\u043e \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430. \u041f\u0440\u043e\u0432\u0435\u0440\u0435\u043d\u043d\u044b\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c \u0440\u0435\u0446\u0435\u043f\u0442\u0443\u0440\u044b, \u043e\u0442\u043b\u0430\u0436\u0435\u043d\u043d\u0430\u044f \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044f \u0438\u0437\u0433\u043e\u0442\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0430\u043a\u0432\u0430\u0440\u0435\u043b\u044c\u043d\u044b\u0435 \u043a\u0440\u0430\u0441\u043a\u0438 \u0432\u0438\u0437\u0438\u0442\u043d\u043e\u0439 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u043e\u0439 \u043f\u0440\u0435\u0434\u043f\u0440\u0438\u044f\u0442\u0438\u044f \u0432 \u0420\u043e\u0441\u0441\u0438\u0438", "created_at": "2018-01-31T14:58:46.209Z", "update_at": "2018-01-31T14:58:46.209Z" diff --git a/apps/course/migrations/0023_auto_20180208_0714.py b/apps/course/migrations/0023_auto_20180208_0714.py new file mode 100644 index 00000000..797d9539 --- /dev/null +++ b/apps/course/migrations/0023_auto_20180208_0714.py @@ -0,0 +1,19 @@ +# Generated by Django 2.0.2 on 2018-02-08 07:14 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0022_auto_20180208_0647'), + ] + + operations = [ + migrations.AlterField( + model_name='course', + name='cover', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='course_covers', to='content.ImageObject', verbose_name='Обложка курса'), + ), + ] From 1f71df4c72f02e7cd7ae28676c259d2efc07004e Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Thu, 8 Feb 2018 11:23:34 +0300 Subject: [PATCH 39/45] Fix Content model --- apps/content/models.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/apps/content/models.py b/apps/content/models.py index 21b63151..1bd06dff 100644 --- a/apps/content/models.py +++ b/apps/content/models.py @@ -2,8 +2,6 @@ from django.db import models from polymorphic.models import PolymorphicModel -from apps.course.models import Course, Lesson - class ImageObject(models.Model): image = models.ImageField('Изображение', upload_to='content/imageobject') @@ -19,13 +17,13 @@ class ImageObject(models.Model): class Content(PolymorphicModel): course = models.ForeignKey( - Course, on_delete=models.CASCADE, + 'course.Course', on_delete=models.CASCADE, null=True, blank=True, verbose_name='Курс', related_name='content', ) lesson = models.ForeignKey( - Lesson, on_delete=models.CASCADE, + 'course.Lesson', on_delete=models.CASCADE, null=True, blank=True, verbose_name='Урок', related_name='content', @@ -69,11 +67,6 @@ class Video(Content): class Gallery(models.Model): - course = models.ForeignKey( - Course, on_delete=models.CASCADE, - null=True, blank=True, - verbose_name='Курс' - ) title = models.CharField('Заголовок', max_length=100, default='') created_at = models.DateTimeField(auto_now_add=True) From 7e332af5c5f5820995e1a5335ff7590b46b8564c Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Thu, 8 Feb 2018 11:30:12 +0300 Subject: [PATCH 40/45] Update Lesson model --- .../migrations/0024_auto_20180208_0824.py | 19 +++++++++++++++++++ apps/course/models.py | 7 +++++-- 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 apps/course/migrations/0024_auto_20180208_0824.py diff --git a/apps/course/migrations/0024_auto_20180208_0824.py b/apps/course/migrations/0024_auto_20180208_0824.py new file mode 100644 index 00000000..4205d492 --- /dev/null +++ b/apps/course/migrations/0024_auto_20180208_0824.py @@ -0,0 +1,19 @@ +# Generated by Django 2.0.2 on 2018-02-08 08:24 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0023_auto_20180208_0714'), + ] + + operations = [ + migrations.AlterField( + model_name='lesson', + name='cover', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='lesson_covers', to='content.ImageObject', verbose_name='Обложка урока'), + ), + ] diff --git a/apps/course/models.py b/apps/course/models.py index 238fc1b0..4e8503a3 100644 --- a/apps/course/models.py +++ b/apps/course/models.py @@ -105,8 +105,11 @@ 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') - + cover = models.ForeignKey( + ImageObject, related_name='lesson_covers', + verbose_name='Обложка урока', on_delete=models.CASCADE, + null=True, blank=True, + ) created_at = models.DateTimeField(auto_now_add=True) update_at = models.DateTimeField(auto_now=True) From 9e0f2ea9e612e35319cbd3aefcdc4cdbfc6a180c Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Thu, 8 Feb 2018 11:32:43 +0300 Subject: [PATCH 41/45] Update course app serializers --- api/v1/serializers.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/api/v1/serializers.py b/api/v1/serializers.py index 5bd197f0..0be807e7 100644 --- a/api/v1/serializers.py +++ b/api/v1/serializers.py @@ -87,6 +87,7 @@ class CategorySerializer(serializers.ModelSerializer): class CourseSerializer(serializers.ModelSerializer): + cover = ImageObjectSerializer() class Meta: model = Course @@ -114,7 +115,6 @@ class CourseSerializer(serializers.ModelSerializer): read_only_fields = ( 'id', - 'cover', 'content', 'created_at', 'update_at', @@ -124,9 +124,11 @@ class CourseSerializer(serializers.ModelSerializer): class CourseRetrieveSerializer(CourseSerializer): category = CategorySerializer() materials = MaterialSerializer(many=True) + cover = ImageObjectSerializer() class LessonSerializer(serializers.ModelSerializer): + cover = ImageObjectSerializer() class Meta: model = Lesson @@ -136,6 +138,7 @@ class LessonSerializer(serializers.ModelSerializer): 'short_description', 'course', 'cover', + 'content', 'created_at', 'update_at', ) @@ -143,6 +146,7 @@ class LessonSerializer(serializers.ModelSerializer): read_only_fields = ( 'id', 'cover', + 'content', 'created_at', 'update_at', ) From 5ac3bc901bdc674fa65e6877d04f364d89c2bc40 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Thu, 8 Feb 2018 11:41:23 +0300 Subject: [PATCH 42/45] Update api views --- api/v1/views.py | 87 +++++-------------------------------------------- 1 file changed, 9 insertions(+), 78 deletions(-) diff --git a/api/v1/views.py b/api/v1/views.py index 7ecb780d..24e69893 100644 --- a/api/v1/views.py +++ b/api/v1/views.py @@ -60,7 +60,7 @@ class CategoryViewSet(ExtendedModelViewSet): class CourseViewSet(ExtendedModelViewSet): queryset = Course.objects.select_related( - 'author', 'category' + 'author', 'category', 'cover', ).prefetch_related( 'likes', 'materials', 'content', ).all() @@ -68,7 +68,6 @@ class CourseViewSet(ExtendedModelViewSet): serializer_class_map = { 'list': CourseRetrieveSerializer, 'retrieve': CourseRetrieveSerializer, - 'upload_photo': CoverImageSerializer, } filter_fields = ('category', 'status', 'is_infinite', 'is_featured',) search_fields = ('author__email', 'title', 'category__title',) @@ -79,25 +78,12 @@ class CourseViewSet(ExtendedModelViewSet): # 'delete': IsAdmin, # } - @detail_route(methods=['post'], url_path='upload-photo') - def upload_photo(self, request, pk=None): - course = self.get_object() - serializer = self.get_serializer() - serialized_data = serializer(data=request.data) - if serialized_data.is_valid(): - course.cover = serialized_data['cover'] - course.save() - return Response({'success': True}) - else: - return Response({'success': False}, status=status.HTTP_400_BAD_REQUEST) - class LessonViewSet(ExtendedModelViewSet): - queryset = Lesson.objects.select_related('course').all() + queryset = Lesson.objects.select_related( + 'course', 'cover' + ).prefetch_related('content').all() serializer_class = LessonSerializer - serializer_class_map = { - 'upload_photo': CoverImageSerializer, - } filter_fields = ('course',) search_fields = ('title', 'short_description',) ordering_fields = ('title', 'created_at', 'update_at',) @@ -107,27 +93,12 @@ class LessonViewSet(ExtendedModelViewSet): # 'delete': IsAdmin, # } - @detail_route(methods=['post'], url_path='upload-photo') - def upload_photo(self, request, pk=None): - lesson = self.get_object() - serializer = self.get_serializer() - serialized_data = serializer(data=request.data) - if serialized_data.is_valid(): - lesson.cover = serialized_data['cover'] - lesson.save() - return Response({'success': True}) - else: - return Response({'success': False}, status=status.HTTP_400_BAD_REQUEST) - class ImageViewSet(ExtendedModelViewSet): queryset = Image.objects.select_related( - 'course', 'lesson' + 'course', 'lesson', 'img', ).all() serializer_class = ImageSerializer - serializer_class_map = { - 'upload_photo': ContentImageSerializer, - } search_fields = ('title',) ordering_fields = ('title', 'created_at', 'update_at', 'position',) # permission_classes = (IsAuthorOrAdmin,) @@ -135,18 +106,6 @@ class ImageViewSet(ExtendedModelViewSet): # 'delete': IsAdmin, # } - @detail_route(methods=['post'], url_path='upload-photo') - def upload_photo(self, request, pk=None): - image = self.get_object() - serializer = self.get_serializer() - serialized_data = serializer(data=request.data) - if serialized_data.is_valid(): - image.img = serialized_data['img'] - image.save() - return Response({'success': True}) - else: - return Response({'success': False}, status=status.HTTP_400_BAD_REQUEST) - class TextViewSet(ExtendedModelViewSet): queryset = Text.objects.select_related( @@ -163,12 +122,9 @@ class TextViewSet(ExtendedModelViewSet): class ImageTextViewSet(ExtendedModelViewSet): queryset = ImageText.objects.select_related( - 'course', 'lesson' + 'course', 'lesson', 'img' ).all() serializer_class = ImageTextSerializer - serializer_class_map = { - 'upload_photo': ContentImageSerializer, - } search_fields = ('title',) ordering_fields = ('title', 'created_at', 'update_at', 'position',) # permission_classes = (IsAuthorOrAdmin,) @@ -176,18 +132,6 @@ class ImageTextViewSet(ExtendedModelViewSet): # 'delete': IsAdmin, # } - @detail_route(methods=['post'], url_path='upload-photo') - def upload_photo(self, request, pk=None): - image_text = self.get_object() - serializer = self.get_serializer() - serialized_data = serializer(data=request.data) - if serialized_data.is_valid(): - image_text.img = serialized_data['img'] - image_text.save() - return Response({'success': True}) - else: - return Response({'success': False}, status=status.HTTP_400_BAD_REQUEST) - class VideoViewSet(ExtendedModelViewSet): queryset = Video.objects.select_related( @@ -214,29 +158,16 @@ class GalleryViewSet(ExtendedModelViewSet): class GalleryImageViewSet(ExtendedModelViewSet): - queryset = GalleryImage.objects.select_related('gallery').all() + queryset = GalleryImage.objects.select_related( + 'gallery', 'img', + ).all() serializer_class = GalleryImageSerializer - serializer_class_map = { - 'upload_photo': GalleryImageSerializer, - } search_fields = ('gallery__title',) # permission_classes = (IsAuthorOrAdmin,) # permission_map = { # 'delete': IsAdmin, # } - @detail_route(methods=['post'], url_path='upload-photo') - def upload_photo(self, request, pk=None): - gallery_image = self.get_object() - serializer = self.get_serializer() - serialized_data = serializer(data=request.data) - if serialized_data.is_valid(): - gallery_image.image = serialized_data['image'] - gallery_image.save() - return Response({'success': True}) - else: - return Response({'success': False}, status=status.HTTP_400_BAD_REQUEST) - class UserViewSet(ExtendedModelViewSet): queryset = User.objects.all() From 451659cac82765c07c31cfd6786de25583a8988a Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Thu, 8 Feb 2018 11:44:05 +0300 Subject: [PATCH 43/45] Fix Gallery serializer & viewset --- api/v1/serializers.py | 1 - api/v1/views.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/api/v1/serializers.py b/api/v1/serializers.py index 0be807e7..dde027c6 100644 --- a/api/v1/serializers.py +++ b/api/v1/serializers.py @@ -247,7 +247,6 @@ class GallerySerializer(serializers.ModelSerializer): model = Gallery fields = ( 'id', - 'course', 'title', 'created_at', 'update_at', diff --git a/api/v1/views.py b/api/v1/views.py index 24e69893..651f1024 100644 --- a/api/v1/views.py +++ b/api/v1/views.py @@ -147,7 +147,7 @@ class VideoViewSet(ExtendedModelViewSet): class GalleryViewSet(ExtendedModelViewSet): - queryset = Gallery.objects.select_related('course').all() + queryset = Gallery.objects.all() serializer_class = GallerySerializer search_fields = ('title',) ordering_fields = ('title', 'created_at', 'update_at',) From f541c56d7d6c56206f730cc5af81e2dd5b8670a3 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Thu, 8 Feb 2018 11:48:44 +0300 Subject: [PATCH 44/45] Add gallery field to Course model --- .../migrations/0010_remove_gallery_course.py | 17 ++++++++++++++++ apps/course/migrations/0025_course_gallery.py | 20 +++++++++++++++++++ apps/course/models.py | 6 +++++- 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 apps/content/migrations/0010_remove_gallery_course.py create mode 100644 apps/course/migrations/0025_course_gallery.py diff --git a/apps/content/migrations/0010_remove_gallery_course.py b/apps/content/migrations/0010_remove_gallery_course.py new file mode 100644 index 00000000..06537707 --- /dev/null +++ b/apps/content/migrations/0010_remove_gallery_course.py @@ -0,0 +1,17 @@ +# Generated by Django 2.0.2 on 2018-02-08 08:47 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('content', '0009_auto_20180208_0637'), + ] + + operations = [ + migrations.RemoveField( + model_name='gallery', + name='course', + ), + ] diff --git a/apps/course/migrations/0025_course_gallery.py b/apps/course/migrations/0025_course_gallery.py new file mode 100644 index 00000000..b20d83cb --- /dev/null +++ b/apps/course/migrations/0025_course_gallery.py @@ -0,0 +1,20 @@ +# Generated by Django 2.0.2 on 2018-02-08 08:47 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('content', '0010_remove_gallery_course'), + ('course', '0024_auto_20180208_0824'), + ] + + operations = [ + migrations.AddField( + model_name='course', + name='gallery', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='content.Gallery', verbose_name='Галерея работ'), + ), + ] diff --git a/apps/course/models.py b/apps/course/models.py index 4e8503a3..6ad7866f 100644 --- a/apps/course/models.py +++ b/apps/course/models.py @@ -7,7 +7,7 @@ from polymorphic_tree.models import PolymorphicMPTTModel, PolymorphicTreeForeign from .manager import CategoryQuerySet -from apps.content.models import ImageObject +from apps.content.models import ImageObject, Gallery User = get_user_model() @@ -53,6 +53,10 @@ class Course(models.Model): status = models.PositiveSmallIntegerField('Статус', default=0, choices=STATUS_CHOICES) likes = models.ManyToManyField(Like, blank=True) materials = models.ManyToManyField('Material', blank=True) + gallery = models.ForeignKey( + Gallery, verbose_name='Галерея работ', + on_delete=models.CASCADE, null=True, blank=True, + ) created_at = models.DateTimeField(auto_now_add=True) update_at = models.DateTimeField(auto_now=True) From f66b9c080dbfdff9d834fee0c3984008bc4e0ac8 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Thu, 8 Feb 2018 11:51:08 +0300 Subject: [PATCH 45/45] Update Course serializer --- api/v1/serializers.py | 79 ++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/api/v1/serializers.py b/api/v1/serializers.py index dde027c6..f0955f50 100644 --- a/api/v1/serializers.py +++ b/api/v1/serializers.py @@ -33,6 +33,44 @@ class ImageObjectSerializer(serializers.ModelSerializer): ) +class GallerySerializer(serializers.ModelSerializer): + + class Meta: + model = Gallery + fields = ( + 'id', + 'title', + 'created_at', + 'update_at', + ) + + read_only_fields = ( + 'id', + 'created_at', + 'update_at', + ) + + +class GalleryImageSerializer(serializers.ModelSerializer): + img = ImageObjectSerializer() + + class Meta: + model = GalleryImage + fields = ( + 'id', + 'gallery', + 'img', + 'created_at', + 'update_at', + ) + + read_only_fields = ( + 'id', + 'created_at', + 'update_at', + ) + + class MaterialSerializer(serializers.ModelSerializer): class Meta: @@ -88,6 +126,7 @@ class CategorySerializer(serializers.ModelSerializer): class CourseSerializer(serializers.ModelSerializer): cover = ImageObjectSerializer() + gallery = GallerySerializer() class Meta: model = Course @@ -111,6 +150,7 @@ class CourseSerializer(serializers.ModelSerializer): 'created_at', 'update_at', 'content', + 'gallery', ) read_only_fields = ( @@ -125,6 +165,7 @@ class CourseRetrieveSerializer(CourseSerializer): category = CategorySerializer() materials = MaterialSerializer(many=True) cover = ImageObjectSerializer() + gallery = GallerySerializer() class LessonSerializer(serializers.ModelSerializer): @@ -241,44 +282,6 @@ class VideoSerializer(serializers.ModelSerializer): ) -class GallerySerializer(serializers.ModelSerializer): - - class Meta: - model = Gallery - fields = ( - 'id', - 'title', - 'created_at', - 'update_at', - ) - - read_only_fields = ( - 'id', - 'created_at', - 'update_at', - ) - - -class GalleryImageSerializer(serializers.ModelSerializer): - img = ImageObjectSerializer() - - class Meta: - model = GalleryImage - fields = ( - 'id', - 'gallery', - 'img', - 'created_at', - 'update_at', - ) - - read_only_fields = ( - 'id', - 'created_at', - 'update_at', - ) - - class UserSerializer(serializers.ModelSerializer): class Meta: