From 442ae435e75a0bd46e65a7669a1139f861e5ac33 Mon Sep 17 00:00:00 2001 From: amurasm <101619423+amurasm@users.noreply.github.com> Date: Tue, 15 Mar 2022 20:34:18 +0400 Subject: [PATCH 01/11] =?UTF-8?q?=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 1.1-first-project/first_project/app/views.py | 15 +++++++++++---- .../first_project/first_project/urls.py | 7 +++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/1.1-first-project/first_project/app/views.py b/1.1-first-project/first_project/app/views.py index ad1b3dec0..16aff26fb 100644 --- a/1.1-first-project/first_project/app/views.py +++ b/1.1-first-project/first_project/app/views.py @@ -1,3 +1,6 @@ +from datetime import datetime +from os import listdir + from django.http import HttpResponse from django.shortcuts import render, reverse @@ -8,8 +11,8 @@ def home_view(request): # функцию `reverse` pages = { 'Главная страница': reverse('home'), - 'Показать текущее время': '', - 'Показать содержимое рабочей директории': '' + 'Показать текущее время': reverse('time'), + 'Показать содержимое рабочей директории': reverse('workdir') } # context и параметры render менять не нужно @@ -23,7 +26,7 @@ def home_view(request): def time_view(request): # обратите внимание – здесь HTML шаблона нет, # возвращается просто текст - current_time = None + current_time = datetime.now().strftime("%Y.%m.%d %H:%M:%S") msg = f'Текущее время: {current_time}' return HttpResponse(msg) @@ -32,4 +35,8 @@ def workdir_view(request): # по аналогии с `time_view`, напишите код, # который возвращает список файлов в рабочей # директории - raise NotImplemented + list_dir = listdir() + msg = '' + for item in list_dir: + msg = msg + '

' + item + '

' + return HttpResponse(msg) diff --git a/1.1-first-project/first_project/first_project/urls.py b/1.1-first-project/first_project/first_project/urls.py index 9cff514f1..3c6d45ae0 100644 --- a/1.1-first-project/first_project/first_project/urls.py +++ b/1.1-first-project/first_project/first_project/urls.py @@ -16,14 +16,13 @@ from django.contrib import admin from django.urls import path, include -from app.views import home_view - +from app.views import home_view, workdir_view, time_view urlpatterns = [ path('', home_view, name='home'), # Раскомментируйте код, чтобы данные урлы # обрабатывались Django - # path('current_time/', time_view, name='time'), - # path('workdir/', workdir_view, name='workdir'), + path('current_time/', time_view, name='time'), + path('workdir/', workdir_view, name='workdir'), path('admin/', admin.site.urls), ] From 49e9c1458f23880528e69b5f75738c6c9335f9a2 Mon Sep 17 00:00:00 2001 From: amurasm <101619423+amurasm@users.noreply.github.com> Date: Sun, 20 Mar 2022 21:16:24 +0400 Subject: [PATCH 02/11] =?UTF-8?q?=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calculator/templates/calculator/index.html | 5 ++++- .../recipes/calculator/templatetags/__init__.py | 0 .../recipes/calculator/templatetags/extras.py | 12 ++++++++++++ 1.2-requests-templates/recipes/calculator/views.py | 13 +++++++++++++ 1.2-requests-templates/recipes/recipes/settings.py | 1 + 1.2-requests-templates/recipes/recipes/urls.py | 3 +++ 6 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 1.2-requests-templates/recipes/calculator/templatetags/__init__.py create mode 100644 1.2-requests-templates/recipes/calculator/templatetags/extras.py diff --git a/1.2-requests-templates/recipes/calculator/templates/calculator/index.html b/1.2-requests-templates/recipes/calculator/templates/calculator/index.html index bc607f1fd..f67db2580 100644 --- a/1.2-requests-templates/recipes/calculator/templates/calculator/index.html +++ b/1.2-requests-templates/recipes/calculator/templates/calculator/index.html @@ -5,10 +5,13 @@ Рецепты +{% load extras %} {% for ingredient, amount in recipe.items %} -

{{ ingredient }}: {{ amount }}

+

{{ ingredient }}: {% multiply amount servings %}

{% empty %}

Такого рецепта не знаю :(

{% endfor %} + + diff --git a/1.2-requests-templates/recipes/calculator/templatetags/__init__.py b/1.2-requests-templates/recipes/calculator/templatetags/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/1.2-requests-templates/recipes/calculator/templatetags/extras.py b/1.2-requests-templates/recipes/calculator/templatetags/extras.py new file mode 100644 index 000000000..79fc6789f --- /dev/null +++ b/1.2-requests-templates/recipes/calculator/templatetags/extras.py @@ -0,0 +1,12 @@ +from django import template + +register = template.Library() + + +@register.simple_tag() +def multiply(x, y): + z = x * y + if type(x) == int: + return round(z) + else: + return round(z, 2) diff --git a/1.2-requests-templates/recipes/calculator/views.py b/1.2-requests-templates/recipes/calculator/views.py index 0337130fb..c68d656d3 100644 --- a/1.2-requests-templates/recipes/calculator/views.py +++ b/1.2-requests-templates/recipes/calculator/views.py @@ -19,6 +19,7 @@ # можете добавить свои рецепты ;) } + # Напишите ваш обработчик. Используйте DATA как источник данных # Результат - render(request, 'calculator/index.html', context) # В качестве контекста должен быть передан словарь с рецептом: @@ -28,3 +29,15 @@ # 'ингредиент2': количество2, # } # } + +def calculator(request, recipe): + context = { + 'recipe': { + }, + 'servings': 1, + } + servings = float(request.GET.get('servings', 1)) + context['servings'] = servings + context['recipe'] = DATA[recipe] + + return render(request, 'calculator/index.html', context) diff --git a/1.2-requests-templates/recipes/recipes/settings.py b/1.2-requests-templates/recipes/recipes/settings.py index 0ec0f2bd4..b8e4a5248 100644 --- a/1.2-requests-templates/recipes/recipes/settings.py +++ b/1.2-requests-templates/recipes/recipes/settings.py @@ -39,6 +39,7 @@ 'django.contrib.staticfiles', 'calculator', + ] MIDDLEWARE = [ diff --git a/1.2-requests-templates/recipes/recipes/urls.py b/1.2-requests-templates/recipes/recipes/urls.py index 2d6f1024f..2f08ac50e 100644 --- a/1.2-requests-templates/recipes/recipes/urls.py +++ b/1.2-requests-templates/recipes/recipes/urls.py @@ -16,6 +16,9 @@ from django.urls import path +from calculator.views import calculator + urlpatterns = [ # здесь зарегистрируйте вашу view-функцию + path('calculator//', calculator, name='calculator'), ] From 07d453111d2f086a22b8887a7e18f7e59f1a3813 Mon Sep 17 00:00:00 2001 From: amurasm <101619423+amurasm@users.noreply.github.com> Date: Thu, 31 Mar 2022 08:46:52 +0400 Subject: [PATCH 03/11] =?UTF-8?q?=D0=97=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=202.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../work_with_database/main/settings.py | 10 ++++--- 2.1-databases/work_with_database/main/urls.py | 1 + .../management/commands/import_phones.py | 10 +++++-- .../work_with_database/phones/models.py | 15 ++++++++++- .../work_with_database/phones/views.py | 27 +++++++++++++++++-- 5 files changed, 54 insertions(+), 9 deletions(-) diff --git a/2.1-databases/work_with_database/main/settings.py b/2.1-databases/work_with_database/main/settings.py index fdcc86d0f..9bbaf389c 100644 --- a/2.1-databases/work_with_database/main/settings.py +++ b/2.1-databases/work_with_database/main/settings.py @@ -50,10 +50,12 @@ DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': 'netology_import_phones', - 'HOST': '127.0.0.1', - 'PORT': '5432', + #'ENGINE': 'django.db.backends.postgresql', + #'NAME': 'netology_import_phones', + #'HOST': '127.0.0.1', + #'PORT': '5432', + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } diff --git a/2.1-databases/work_with_database/main/urls.py b/2.1-databases/work_with_database/main/urls.py index f50320b4a..b14f367ae 100644 --- a/2.1-databases/work_with_database/main/urls.py +++ b/2.1-databases/work_with_database/main/urls.py @@ -23,4 +23,5 @@ path('', phones.views.index), path('catalog/', phones.views.show_catalog, name='catalog'), path('catalog//', phones.views.show_product, name='phone'), + path('show_db/', phones.views.show_db, name='show_db'), ] diff --git a/2.1-databases/work_with_database/phones/management/commands/import_phones.py b/2.1-databases/work_with_database/phones/management/commands/import_phones.py index be777c517..b7ae43186 100644 --- a/2.1-databases/work_with_database/phones/management/commands/import_phones.py +++ b/2.1-databases/work_with_database/phones/management/commands/import_phones.py @@ -1,6 +1,8 @@ import csv from django.core.management.base import BaseCommand +#from django.utils.text import slugify + from phones.models import Phone @@ -13,5 +15,9 @@ def handle(self, *args, **options): phones = list(csv.DictReader(file, delimiter=';')) for phone in phones: - # TODO: Добавьте сохранение модели - pass + print(phone) + Phone.objects.create(name=phone['name'], image=phone['image'], price=phone['price'], + release_date=phone['release_date'], lte_exists=phone['lte_exists']) + #slug = slugify(phone['name']), + #id = phone['id'], + #, slug = phone['name'] \ No newline at end of file diff --git a/2.1-databases/work_with_database/phones/models.py b/2.1-databases/work_with_database/phones/models.py index e79b3ba32..24e300e38 100644 --- a/2.1-databases/work_with_database/phones/models.py +++ b/2.1-databases/work_with_database/phones/models.py @@ -1,6 +1,19 @@ from django.db import models +from django.utils.text import slugify class Phone(models.Model): # TODO: Добавьте требуемые поля - pass + #id = models.CharField(max_length=10, primary_key=True) + name = models.CharField(max_length=50) + #slug = models.CharField(max_length=100) + slug = models.SlugField(max_length=100, unique=True) + image = models.CharField(max_length=100) + price = models.CharField(max_length=20) + release_date = models.CharField(max_length=50) + lte_exists = models.CharField(max_length=20) + # + + def save(self, *args, **kwargs): + self.slug = slugify(self.name) + super(Phone, self).save(*args, **kwargs) diff --git a/2.1-databases/work_with_database/phones/views.py b/2.1-databases/work_with_database/phones/views.py index afec3b5ec..9105df653 100644 --- a/2.1-databases/work_with_database/phones/views.py +++ b/2.1-databases/work_with_database/phones/views.py @@ -1,17 +1,40 @@ +from django.http import HttpResponse from django.shortcuts import render, redirect +from phones.models import Phone + def index(request): return redirect('catalog') def show_catalog(request): + phones = Phone.objects.all() + sort = request.GET.get('sort') + if sort == 'name': + phones = phones.order_by("name") + elif sort == 'min_price': + phones = phones.order_by("price") + elif sort == 'max_price': + phones = phones.order_by("-price") + + data = [phone.__dict__ for phone in phones] + template = 'catalog.html' - context = {} + context = {'phones': data} return render(request, template, context) def show_product(request, slug): + phone_objects = Phone.objects.filter(slug=slug) + # print(phone_objects[0].__dict__) template = 'product.html' - context = {} + context = {'phone': phone_objects[0].__dict__} return render(request, template, context) + + +def show_db(request): + phone_objects = Phone.objects.all() + phones = [f'{p.id}: {p.name}, {p.slug}, {p.image}, {p.price}, {p.release_date}, {p.lte_exists}' for p in + phone_objects] + return HttpResponse('
'.join(phones)) From 4f76bf023051d080775673ff004df93e3b8afb8a Mon Sep 17 00:00:00 2001 From: amurasm <101619423+amurasm@users.noreply.github.com> Date: Tue, 12 Apr 2022 12:03:13 +0400 Subject: [PATCH 04/11] =?UTF-8?q?=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=202.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../m2m-relations/articles/admin.py | 33 +++++++++++++++++-- .../articles/migrations/0001_initial.py | 24 +++++++++++++- .../m2m-relations/articles/models.py | 25 ++++++++++++-- .../m2m-relations/articles/views.py | 12 ++++--- .../m2m-relations/website/settings.py | 10 +++--- .../orm_migrations/school/models.py | 2 +- .../orm_migrations/school/views.py | 9 ++--- .../templates/school/students_list.html | 5 ++- .../orm_migrations/website/settings.py | 17 ++++++---- .../orm_migrations/website/urls.py | 1 + 10 files changed, 111 insertions(+), 27 deletions(-) diff --git a/2.2-databases-2/m2m-relations/articles/admin.py b/2.2-databases-2/m2m-relations/articles/admin.py index 4799babf4..8a5d70743 100644 --- a/2.2-databases-2/m2m-relations/articles/admin.py +++ b/2.2-databases-2/m2m-relations/articles/admin.py @@ -1,8 +1,37 @@ from django.contrib import admin -from .models import Article +from django.core.exceptions import ValidationError +from django.forms import BaseInlineFormSet +from .models import Tag, Article, Scope + + +admin.site.register(Tag) + + +class ScopeInlineFormset(BaseInlineFormSet): + def clean(self): + form_not_del = [f for f in self.forms if not f.cleaned_data['DELETE']] + tag_ids = set(form.cleaned_data['tag'].id for form in form_not_del) + if len(tag_ids) != len(form_not_del): + raise ValidationError('Найдены дубликаты разделов!') + + is_mains = [form.cleaned_data['is_main'] for form in form_not_del if form.cleaned_data['is_main']] + print(is_mains) + if len(is_mains) == 0: + raise ValidationError('Укажите основной раздел!') + + if len(is_mains) > 1: + raise ValidationError('Основным может быть только один раздел!') + + return super().clean() # вызываем базовый код переопределяемого метода + + +class ScopeInline(admin.TabularInline): + model = Scope + extra = 0 + formset = ScopeInlineFormset @admin.register(Article) class ArticleAdmin(admin.ModelAdmin): - pass + inlines = [ScopeInline] diff --git a/2.2-databases-2/m2m-relations/articles/migrations/0001_initial.py b/2.2-databases-2/m2m-relations/articles/migrations/0001_initial.py index ca47d3de3..86215f765 100644 --- a/2.2-databases-2/m2m-relations/articles/migrations/0001_initial.py +++ b/2.2-databases-2/m2m-relations/articles/migrations/0001_initial.py @@ -1,6 +1,7 @@ -# Generated by Django 2.0.5 on 2018-09-30 20:46 +# Generated by Django 4.0.3 on 2022-04-06 06:48 from django.db import migrations, models +import django.db.models.deletion class Migration(migrations.Migration): @@ -25,4 +26,25 @@ class Migration(migrations.Migration): 'verbose_name_plural': 'Статьи', }, ), + migrations.CreateModel( + name='Scope', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='articles.article')), + ], + ), + migrations.CreateModel( + name='Tag', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100, verbose_name='Тэг')), + ('is_main', models.BooleanField(default=False)), + ('articles', models.ManyToManyField(related_name='scopes', through='articles.Scope', to='articles.article')), + ], + ), + migrations.AddField( + model_name='scope', + name='tag', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='articles.tag'), + ), ] diff --git a/2.2-databases-2/m2m-relations/articles/models.py b/2.2-databases-2/m2m-relations/articles/models.py index 0486c9fe8..06b8f9125 100644 --- a/2.2-databases-2/m2m-relations/articles/models.py +++ b/2.2-databases-2/m2m-relations/articles/models.py @@ -2,11 +2,10 @@ class Article(models.Model): - title = models.CharField(max_length=256, verbose_name='Название') text = models.TextField(verbose_name='Текст') published_at = models.DateTimeField(verbose_name='Дата публикации') - image = models.ImageField(null=True, blank=True, verbose_name='Изображение',) + image = models.ImageField(null=True, blank=True, verbose_name='Изображение', ) class Meta: verbose_name = 'Статья' @@ -14,3 +13,25 @@ class Meta: def __str__(self): return self.title + + +class Tag(models.Model): + name = models.CharField(max_length=100, verbose_name='Раздел') + articles = models.ManyToManyField(Article, through='Scope') + + class Meta: + verbose_name = 'Раздел' + verbose_name_plural = 'Разделы' + + def __str__(self): + return self.name + + +class Scope(models.Model): + article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='scopes', verbose_name='Статья') + tag = models.ForeignKey(Tag, on_delete=models.CASCADE, verbose_name='Раздел') + is_main = models.BooleanField(default=False, verbose_name='Основной') + + class Meta: + + ordering = ['-is_main', '-tag'] diff --git a/2.2-databases-2/m2m-relations/articles/views.py b/2.2-databases-2/m2m-relations/articles/views.py index e1d022b45..01e42f371 100644 --- a/2.2-databases-2/m2m-relations/articles/views.py +++ b/2.2-databases-2/m2m-relations/articles/views.py @@ -4,11 +4,15 @@ def articles_list(request): + articles = Article.objects.all() template = 'articles/news.html' - context = {} - # используйте этот параметр для упорядочивания результатов - # https://docs.djangoproject.com/en/3.1/ref/models/querysets/#django.db.models.query.QuerySet.order_by - ordering = '-published_at' + context = {'object_list': articles} + print(context) + for article in articles: + print(article.title, ":") + for scope in article.scopes.all(): + print(scope.is_main, scope.tag.name) + return render(request, template, context) diff --git a/2.2-databases-2/m2m-relations/website/settings.py b/2.2-databases-2/m2m-relations/website/settings.py index d9bc8000f..85722eb5b 100644 --- a/2.2-databases-2/m2m-relations/website/settings.py +++ b/2.2-databases-2/m2m-relations/website/settings.py @@ -75,10 +75,12 @@ DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': 'netology_m2m_relations', - 'HOST': '127.0.0.1', - 'PORT': '5432', + #'ENGINE': 'django.db.backends.postgresql', + #'NAME': 'netology_m2m_relations', + #'HOST': '127.0.0.1', + #'PORT': '5432', + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } diff --git a/2.2-databases-2/orm_migrations/school/models.py b/2.2-databases-2/orm_migrations/school/models.py index abab28192..46d32c8b7 100644 --- a/2.2-databases-2/orm_migrations/school/models.py +++ b/2.2-databases-2/orm_migrations/school/models.py @@ -15,7 +15,7 @@ def __str__(self): class Student(models.Model): name = models.CharField(max_length=30, verbose_name='Имя') - teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE) + teacher = models.ManyToManyField(Teacher, related_name='students') group = models.CharField(max_length=10, verbose_name='Класс') class Meta: diff --git a/2.2-databases-2/orm_migrations/school/views.py b/2.2-databases-2/orm_migrations/school/views.py index e3cb4d625..24b34c6ba 100644 --- a/2.2-databases-2/orm_migrations/school/views.py +++ b/2.2-databases-2/orm_migrations/school/views.py @@ -1,15 +1,12 @@ -from django.views.generic import ListView + from django.shortcuts import render from .models import Student def students_list(request): + students = Student.objects.order_by('group') template = 'school/students_list.html' - context = {} - - # используйте этот параметр для упорядочивания результатов - # https://docs.djangoproject.com/en/2.2/ref/models/querysets/#django.db.models.query.QuerySet.order_by - ordering = 'group' + context = {'object_list': students} return render(request, template, context) diff --git a/2.2-databases-2/orm_migrations/templates/school/students_list.html b/2.2-databases-2/orm_migrations/templates/school/students_list.html index d7d9b2d21..8bca3a6c3 100644 --- a/2.2-databases-2/orm_migrations/templates/school/students_list.html +++ b/2.2-databases-2/orm_migrations/templates/school/students_list.html @@ -6,7 +6,10 @@
    {% for student in object_list %} -
  • {{ student.name }} {{ student.group }}
    Преподаватель: {{ student.teacher.name }} {{ student.teacher.subject }}
  • +
  • {{ student.name }} {{ student.group }}
    Преподаватель: + {% for teacher in student.teacher.all %} +

    {{ teacher.name }}: {{ teacher.subject }}

    + {% endfor %}
  • {% endfor %}
diff --git a/2.2-databases-2/orm_migrations/website/settings.py b/2.2-databases-2/orm_migrations/website/settings.py index 32bc56c38..59959ecbb 100644 --- a/2.2-databases-2/orm_migrations/website/settings.py +++ b/2.2-databases-2/orm_migrations/website/settings.py @@ -37,6 +37,7 @@ 'django.contrib.messages', 'django.contrib.staticfiles', 'school', + 'debug_toolbar', ] MIDDLEWARE = [ @@ -47,6 +48,7 @@ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'debug_toolbar.middleware.DebugToolbarMiddleware', ] ROOT_URLCONF = 'website.urls' @@ -72,10 +74,12 @@ DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': 'netology_orm_migrations', - 'HOST': '127.0.0.1', - 'PORT': '5432', + #'ENGINE': 'django.db.backends.postgresql', + #'NAME': 'netology_orm_migrations', + #'HOST': '127.0.0.1', + #'PORT': '5432', + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } @@ -102,7 +106,7 @@ # Internationalization # https://docs.djangoproject.com/en/2.1/topics/i18n/ -LANGUAGE_CODE = 'RU-ru' +LANGUAGE_CODE = 'ru-RU' TIME_ZONE = 'Europe/Moscow' @@ -116,7 +120,7 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.1/howto/static-files/ -STATIC_URL = '/static/' +STATIC_URL = 'static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), @@ -131,3 +135,4 @@ except ImportError: pass +DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' diff --git a/2.2-databases-2/orm_migrations/website/urls.py b/2.2-databases-2/orm_migrations/website/urls.py index dee37f896..097e44087 100644 --- a/2.2-databases-2/orm_migrations/website/urls.py +++ b/2.2-databases-2/orm_migrations/website/urls.py @@ -21,4 +21,5 @@ urlpatterns = [ path('', include('school.urls')), path('admin/', admin.site.urls), + path('__debug__/', include('debug_toolbar.urls')), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) From 1cc8dcd730281175b0bb5e0bdc7a799d5a1bbad8 Mon Sep 17 00:00:00 2001 From: amurasm <101619423+amurasm@users.noreply.github.com> Date: Tue, 12 Apr 2022 12:05:09 +0400 Subject: [PATCH 05/11] =?UTF-8?q?=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=202.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../0002_remove_tag_is_main_scope_is_main.py | 22 +++++++++++++ ..._alter_scope_article_alter_tag_articles.py | 24 ++++++++++++++ ...udent_group_alter_student_name_and_more.py | 33 +++++++++++++++++++ ..._remove_student_teacher_student_teacher.py | 22 +++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 2.2-databases-2/m2m-relations/articles/migrations/0002_remove_tag_is_main_scope_is_main.py create mode 100644 2.2-databases-2/m2m-relations/articles/migrations/0003_alter_scope_article_alter_tag_articles.py create mode 100644 2.2-databases-2/orm_migrations/school/migrations/0002_alter_student_group_alter_student_name_and_more.py create mode 100644 2.2-databases-2/orm_migrations/school/migrations/0003_remove_student_teacher_student_teacher.py diff --git a/2.2-databases-2/m2m-relations/articles/migrations/0002_remove_tag_is_main_scope_is_main.py b/2.2-databases-2/m2m-relations/articles/migrations/0002_remove_tag_is_main_scope_is_main.py new file mode 100644 index 000000000..3cbb74123 --- /dev/null +++ b/2.2-databases-2/m2m-relations/articles/migrations/0002_remove_tag_is_main_scope_is_main.py @@ -0,0 +1,22 @@ +# Generated by Django 4.0.3 on 2022-04-06 09:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('articles', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='tag', + name='is_main', + ), + migrations.AddField( + model_name='scope', + name='is_main', + field=models.BooleanField(default=False), + ), + ] diff --git a/2.2-databases-2/m2m-relations/articles/migrations/0003_alter_scope_article_alter_tag_articles.py b/2.2-databases-2/m2m-relations/articles/migrations/0003_alter_scope_article_alter_tag_articles.py new file mode 100644 index 000000000..46a73b3af --- /dev/null +++ b/2.2-databases-2/m2m-relations/articles/migrations/0003_alter_scope_article_alter_tag_articles.py @@ -0,0 +1,24 @@ +# Generated by Django 4.0.3 on 2022-04-06 11:08 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('articles', '0002_remove_tag_is_main_scope_is_main'), + ] + + operations = [ + migrations.AlterField( + model_name='scope', + name='article', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='scopes', to='articles.article'), + ), + migrations.AlterField( + model_name='tag', + name='articles', + field=models.ManyToManyField(through='articles.Scope', to='articles.article'), + ), + ] diff --git a/2.2-databases-2/orm_migrations/school/migrations/0002_alter_student_group_alter_student_name_and_more.py b/2.2-databases-2/orm_migrations/school/migrations/0002_alter_student_group_alter_student_name_and_more.py new file mode 100644 index 000000000..dcf518c83 --- /dev/null +++ b/2.2-databases-2/orm_migrations/school/migrations/0002_alter_student_group_alter_student_name_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 4.0.3 on 2022-04-09 14:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('school', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='student', + name='group', + field=models.CharField(max_length=10, verbose_name='Класс'), + ), + migrations.AlterField( + model_name='student', + name='name', + field=models.CharField(max_length=30, verbose_name='Имя'), + ), + migrations.AlterField( + model_name='teacher', + name='name', + field=models.CharField(max_length=30, verbose_name='Имя'), + ), + migrations.AlterField( + model_name='teacher', + name='subject', + field=models.CharField(max_length=10, verbose_name='Предмет'), + ), + ] diff --git a/2.2-databases-2/orm_migrations/school/migrations/0003_remove_student_teacher_student_teacher.py b/2.2-databases-2/orm_migrations/school/migrations/0003_remove_student_teacher_student_teacher.py new file mode 100644 index 000000000..6ce73eafc --- /dev/null +++ b/2.2-databases-2/orm_migrations/school/migrations/0003_remove_student_teacher_student_teacher.py @@ -0,0 +1,22 @@ +# Generated by Django 4.0.3 on 2022-04-09 16:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('school', '0002_alter_student_group_alter_student_name_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='student', + name='teacher', + ), + migrations.AddField( + model_name='student', + name='teacher', + field=models.ManyToManyField(related_name='students', to='school.teacher'), + ), + ] From 96b2852aa32f0287bbbcdd79f4eb9c057483c411 Mon Sep 17 00:00:00 2001 From: amurasm <101619423+amurasm@users.noreply.github.com> Date: Mon, 18 Apr 2022 10:06:55 +0400 Subject: [PATCH 06/11] =?UTF-8?q?=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=203.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3.1-drf-intro/smart_home/measurement/admin.py | 10 +++++++ .../smart_home/measurement/models.py | 26 +++++++++++++++++++ .../smart_home/measurement/serializers.py | 21 +++++++++++++++ 3.1-drf-intro/smart_home/measurement/urls.py | 5 ++++ 3.1-drf-intro/smart_home/measurement/views.py | 23 ++++++++++++++++ .../smart_home/smart_home/settings.py | 13 ++++++---- 6 files changed, 93 insertions(+), 5 deletions(-) diff --git a/3.1-drf-intro/smart_home/measurement/admin.py b/3.1-drf-intro/smart_home/measurement/admin.py index 8c38f3f3d..9baa0718e 100644 --- a/3.1-drf-intro/smart_home/measurement/admin.py +++ b/3.1-drf-intro/smart_home/measurement/admin.py @@ -1,3 +1,13 @@ from django.contrib import admin # Register your models here. +from measurement.models import Sensor, Measurement + +admin.site.register(Sensor) + + +@admin.register(Measurement) +class MeasurementAdmin(admin.ModelAdmin): + list = [Measurement.temperature, Measurement.created_at, Measurement.sensor] + + diff --git a/3.1-drf-intro/smart_home/measurement/models.py b/3.1-drf-intro/smart_home/measurement/models.py index 54f0ea2e4..57163105c 100644 --- a/3.1-drf-intro/smart_home/measurement/models.py +++ b/3.1-drf-intro/smart_home/measurement/models.py @@ -1,3 +1,29 @@ from django.db import models + # TODO: опишите модели датчика (Sensor) и измерения (Measurement) + +class Sensor(models.Model): + name = models.CharField(max_length=256, verbose_name='Название') + description = models.CharField(max_length=256, verbose_name='Описание') + + class Meta: + verbose_name = 'Датчик' + verbose_name_plural = 'Датчики' + + def __str__(self): + return self.name + + +class Measurement(models.Model): + temperature = models.CharField(max_length=50, verbose_name='Измерение') + created_at = models.DateTimeField(auto_now_add=True, verbose_name='Дата измерения') + image = models.ImageField(max_length=255, blank=True) + sensor = models.ForeignKey(Sensor, on_delete=models.CASCADE, related_name='measurements') + + class Meta: + verbose_name = 'Измерение' + verbose_name_plural = 'Измерения' + + def __str__(self): + return self.temperature diff --git a/3.1-drf-intro/smart_home/measurement/serializers.py b/3.1-drf-intro/smart_home/measurement/serializers.py index 5f2dcc713..34833ff30 100644 --- a/3.1-drf-intro/smart_home/measurement/serializers.py +++ b/3.1-drf-intro/smart_home/measurement/serializers.py @@ -1,3 +1,24 @@ from rest_framework import serializers # TODO: опишите необходимые сериализаторы +from measurement.models import Sensor, Measurement + + +class MeasurementSerializer(serializers.ModelSerializer): + class Meta: + model = Measurement + fields = ['temperature', 'created_at', 'image'] + + +class SensorsSerializer(serializers.ModelSerializer): + class Meta: + model = Sensor + fields = ['id', 'name', 'description'] + + +class SensorSerializer(serializers.ModelSerializer): + measurements = MeasurementSerializer(read_only=True, many=True) + + class Meta: + model = Sensor + fields = ['id', 'name', 'description', 'measurements'] diff --git a/3.1-drf-intro/smart_home/measurement/urls.py b/3.1-drf-intro/smart_home/measurement/urls.py index f3e565996..55dab3b05 100644 --- a/3.1-drf-intro/smart_home/measurement/urls.py +++ b/3.1-drf-intro/smart_home/measurement/urls.py @@ -1,5 +1,10 @@ from django.urls import path +from measurement.views import SensorView, SensorsView, MeasurementCreate + urlpatterns = [ # TODO: зарегистрируйте необходимые маршруты + path('measurements/', MeasurementCreate.as_view()), + path('sensors/', SensorsView.as_view()), + path('sensors//', SensorView.as_view()), ] diff --git a/3.1-drf-intro/smart_home/measurement/views.py b/3.1-drf-intro/smart_home/measurement/views.py index 257bca6a1..f625c8034 100644 --- a/3.1-drf-intro/smart_home/measurement/views.py +++ b/3.1-drf-intro/smart_home/measurement/views.py @@ -1,2 +1,25 @@ # TODO: опишите необходимые обработчики, рекомендуется использовать generics APIView классы: # TODO: ListCreateAPIView, RetrieveUpdateAPIView, CreateAPIView +from rest_framework.generics import RetrieveUpdateAPIView, CreateAPIView, ListCreateAPIView, get_object_or_404 + +from measurement.models import Sensor, Measurement +from measurement.serializers import SensorSerializer, SensorsSerializer, MeasurementSerializer + + +class MeasurementCreate(CreateAPIView): + queryset = Measurement.objects.all() + serializer_class = MeasurementSerializer + + def perform_create(self, serializer): + sensor = get_object_or_404(Sensor, id=self.request.data.get('sensor')) + return serializer.save(sensor=sensor) + + +class SensorsView(ListCreateAPIView): + queryset = Sensor.objects.all() + serializer_class = SensorsSerializer + + +class SensorView(RetrieveUpdateAPIView): + queryset = Sensor.objects.all() + serializer_class = SensorSerializer diff --git a/3.1-drf-intro/smart_home/smart_home/settings.py b/3.1-drf-intro/smart_home/smart_home/settings.py index b166a601f..be37e5657 100644 --- a/3.1-drf-intro/smart_home/smart_home/settings.py +++ b/3.1-drf-intro/smart_home/smart_home/settings.py @@ -9,7 +9,7 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/3.2/ref/settings/ """ - +import os from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. @@ -79,10 +79,12 @@ DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': 'netology_smart_home', - 'HOST': '127.0.0.1', - 'PORT': '5432', + #'ENGINE': 'django.db.backends.postgresql', + #'NAME': 'netology_smart_home', + #'HOST': '127.0.0.1', + #'PORT': '5432', + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } @@ -129,3 +131,4 @@ # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + From a1d04c2cbbd86d1b8d2f4169cb7d51494ce19868 Mon Sep 17 00:00:00 2001 From: amurasm <101619423+amurasm@users.noreply.github.com> Date: Wed, 20 Apr 2022 13:53:11 +0400 Subject: [PATCH 07/11] SensorSerializer -> SensorDetailSerializer SensorsSerializer -> SensorSerializer --- 3.1-drf-intro/smart_home/measurement/serializers.py | 8 ++++++-- 3.1-drf-intro/smart_home/measurement/urls.py | 6 +++--- 3.1-drf-intro/smart_home/measurement/views.py | 11 ++++++----- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/3.1-drf-intro/smart_home/measurement/serializers.py b/3.1-drf-intro/smart_home/measurement/serializers.py index 34833ff30..ae85bfe7c 100644 --- a/3.1-drf-intro/smart_home/measurement/serializers.py +++ b/3.1-drf-intro/smart_home/measurement/serializers.py @@ -10,15 +10,19 @@ class Meta: fields = ['temperature', 'created_at', 'image'] -class SensorsSerializer(serializers.ModelSerializer): +class SensorSerializer(serializers.ModelSerializer): class Meta: model = Sensor fields = ['id', 'name', 'description'] -class SensorSerializer(serializers.ModelSerializer): +class SensorDetailSerializer(serializers.ModelSerializer): measurements = MeasurementSerializer(read_only=True, many=True) class Meta: model = Sensor fields = ['id', 'name', 'description', 'measurements'] + + + + diff --git a/3.1-drf-intro/smart_home/measurement/urls.py b/3.1-drf-intro/smart_home/measurement/urls.py index 55dab3b05..2a52bedb4 100644 --- a/3.1-drf-intro/smart_home/measurement/urls.py +++ b/3.1-drf-intro/smart_home/measurement/urls.py @@ -1,10 +1,10 @@ from django.urls import path -from measurement.views import SensorView, SensorsView, MeasurementCreate +from measurement.views import SensorView, MeasurementCreate, SingleSensorView urlpatterns = [ # TODO: зарегистрируйте необходимые маршруты path('measurements/', MeasurementCreate.as_view()), - path('sensors/', SensorsView.as_view()), - path('sensors//', SensorView.as_view()), + path('sensors/', SensorView.as_view()), + path('sensors//', SingleSensorView.as_view()), ] diff --git a/3.1-drf-intro/smart_home/measurement/views.py b/3.1-drf-intro/smart_home/measurement/views.py index f625c8034..55ee7a17e 100644 --- a/3.1-drf-intro/smart_home/measurement/views.py +++ b/3.1-drf-intro/smart_home/measurement/views.py @@ -3,7 +3,7 @@ from rest_framework.generics import RetrieveUpdateAPIView, CreateAPIView, ListCreateAPIView, get_object_or_404 from measurement.models import Sensor, Measurement -from measurement.serializers import SensorSerializer, SensorsSerializer, MeasurementSerializer +from measurement.serializers import SensorSerializer, MeasurementSerializer, SensorDetailSerializer class MeasurementCreate(CreateAPIView): @@ -15,11 +15,12 @@ def perform_create(self, serializer): return serializer.save(sensor=sensor) -class SensorsView(ListCreateAPIView): +class SensorView(ListCreateAPIView): queryset = Sensor.objects.all() - serializer_class = SensorsSerializer + serializer_class = SensorSerializer -class SensorView(RetrieveUpdateAPIView): +class SingleSensorView(RetrieveUpdateAPIView): queryset = Sensor.objects.all() - serializer_class = SensorSerializer + serializer_class = SensorDetailSerializer + From cdbc6bfb5fa3c803bb7b5d0b501a2d4d43c844de Mon Sep 17 00:00:00 2001 From: amurasm <101619423+amurasm@users.noreply.github.com> Date: Wed, 27 Apr 2022 15:57:13 +0400 Subject: [PATCH 08/11] =?UTF-8?q?=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=203.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3.2-crud/stocks_products/logistic/admin.py | 5 +++++ 3.2-crud/stocks_products/logistic/models.py | 2 ++ .../stocks_products/logistic/serializers.py | 19 ++++++++++++++++--- 3.2-crud/stocks_products/logistic/views.py | 9 ++++++++- .../stocks_products/settings.py | 4 +++- 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/3.2-crud/stocks_products/logistic/admin.py b/3.2-crud/stocks_products/logistic/admin.py index 8c38f3f3d..9fc65abe1 100644 --- a/3.2-crud/stocks_products/logistic/admin.py +++ b/3.2-crud/stocks_products/logistic/admin.py @@ -1,3 +1,8 @@ from django.contrib import admin # Register your models here. +from logistic.models import Product, Stock, StockProduct + +admin.site.register(Product) +admin.site.register(Stock) +admin.site.register(StockProduct) diff --git a/3.2-crud/stocks_products/logistic/models.py b/3.2-crud/stocks_products/logistic/models.py index effc7dd2f..2db463a3f 100644 --- a/3.2-crud/stocks_products/logistic/models.py +++ b/3.2-crud/stocks_products/logistic/models.py @@ -22,11 +22,13 @@ class StockProduct(models.Model): on_delete=models.CASCADE, related_name='positions', ) + product = models.ForeignKey( Product, on_delete=models.CASCADE, related_name='positions', ) + quantity = models.PositiveIntegerField(default=1) price = models.DecimalField( max_digits=18, diff --git a/3.2-crud/stocks_products/logistic/serializers.py b/3.2-crud/stocks_products/logistic/serializers.py index af8b3fd4e..3938d221c 100644 --- a/3.2-crud/stocks_products/logistic/serializers.py +++ b/3.2-crud/stocks_products/logistic/serializers.py @@ -1,20 +1,29 @@ from rest_framework import serializers +from logistic.models import Product, StockProduct, Stock + class ProductSerializer(serializers.ModelSerializer): # настройте сериализатор для продукта - pass + class Meta: + model = Product + fields = ['id', 'title', 'description'] class ProductPositionSerializer(serializers.ModelSerializer): # настройте сериализатор для позиции продукта на складе - pass + class Meta: + model = StockProduct + fields = ['product', 'quantity', 'price'] class StockSerializer(serializers.ModelSerializer): positions = ProductPositionSerializer(many=True) # настройте сериализатор для склада + class Meta: + model = Stock + fields = ['id', 'address', 'positions'] def create(self, validated_data): # достаем связанные данные для других таблиц @@ -26,6 +35,8 @@ def create(self, validated_data): # здесь вам надо заполнить связанные таблицы # в нашем случае: таблицу StockProduct # с помощью списка positions + for position in positions: + StockProduct.objects.create(stock=stock, **position) return stock @@ -39,5 +50,7 @@ def update(self, instance, validated_data): # здесь вам надо обновить связанные таблицы # в нашем случае: таблицу StockProduct # с помощью списка positions - + for position in positions: + StockProduct.objects.filter(stock=stock, product=position['product']).update(quantity=position['quantity'], + price=position['price']) return stock diff --git a/3.2-crud/stocks_products/logistic/views.py b/3.2-crud/stocks_products/logistic/views.py index 101131aac..1dae70f14 100644 --- a/3.2-crud/stocks_products/logistic/views.py +++ b/3.2-crud/stocks_products/logistic/views.py @@ -13,4 +13,11 @@ class ProductViewSet(ModelViewSet): class StockViewSet(ModelViewSet): queryset = Stock.objects.all() serializer_class = StockSerializer - # при необходимости добавьте параметры фильтрации + + def get_queryset(self): + queryset = Stock.objects.all() + products = self.request.query_params.get('products') + if products is not None: + queryset = queryset.filter(products__title__icontains=products) + return queryset + diff --git a/3.2-crud/stocks_products/stocks_products/settings.py b/3.2-crud/stocks_products/stocks_products/settings.py index 40a0dbc56..bbf33a0a9 100644 --- a/3.2-crud/stocks_products/stocks_products/settings.py +++ b/3.2-crud/stocks_products/stocks_products/settings.py @@ -9,7 +9,7 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/3.2/ref/settings/ """ - +import os from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. @@ -83,6 +83,8 @@ 'NAME': 'netology_stocks_products', 'HOST': '127.0.0.1', 'PORT': '5432', + 'USER': 'postgres', + 'PASSWORD': 'postgres', } } From f1171884091835ebd0a8a05a6967b024e61dc5de Mon Sep 17 00:00:00 2001 From: amurasm <101619423+amurasm@users.noreply.github.com> Date: Thu, 28 Apr 2022 10:05:25 +0400 Subject: [PATCH 09/11] update -> update_or_create +pagination --- 3.2-crud/stocks_products/logistic/serializers.py | 6 ++++-- 3.2-crud/stocks_products/stocks_products/settings.py | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/3.2-crud/stocks_products/logistic/serializers.py b/3.2-crud/stocks_products/logistic/serializers.py index 3938d221c..af7314cbe 100644 --- a/3.2-crud/stocks_products/logistic/serializers.py +++ b/3.2-crud/stocks_products/logistic/serializers.py @@ -51,6 +51,8 @@ def update(self, instance, validated_data): # в нашем случае: таблицу StockProduct # с помощью списка positions for position in positions: - StockProduct.objects.filter(stock=stock, product=position['product']).update(quantity=position['quantity'], - price=position['price']) + StockProduct.objects.update_or_create(stock=stock, product=position['product'], + defaults={'quantity': position['quantity'], + 'price': position['price']}) + return stock diff --git a/3.2-crud/stocks_products/stocks_products/settings.py b/3.2-crud/stocks_products/stocks_products/settings.py index bbf33a0a9..6226aadf4 100644 --- a/3.2-crud/stocks_products/stocks_products/settings.py +++ b/3.2-crud/stocks_products/stocks_products/settings.py @@ -131,3 +131,8 @@ # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +REST_FRAMEWORK = { + 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', + 'PAGE_SIZE': 3 +} From 028aa57e5535c96d907114e4f315a90766eeb1b5 Mon Sep 17 00:00:00 2001 From: amurasm <101619423+amurasm@users.noreply.github.com> Date: Fri, 6 May 2022 14:49:44 +0400 Subject: [PATCH 10/11] =?UTF-8?q?=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=203.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../advertisements/filters.py | 7 +++-- .../advertisements/serializers.py | 19 +++++++++++-- .../advertisements/views.py | 28 +++++++++++++++++-- .../api_with_restrictions/settings.py | 25 +++++++++++++---- .../api_with_restrictions/urls.py | 4 ++- 5 files changed, 68 insertions(+), 15 deletions(-) diff --git a/3.3-permissions/api_with_restrictions/advertisements/filters.py b/3.3-permissions/api_with_restrictions/advertisements/filters.py index 455b636e8..624d05d04 100644 --- a/3.3-permissions/api_with_restrictions/advertisements/filters.py +++ b/3.3-permissions/api_with_restrictions/advertisements/filters.py @@ -1,12 +1,13 @@ -from django_filters import rest_framework as filters - +import django_filters from advertisements.models import Advertisement -class AdvertisementFilter(filters.FilterSet): +class AdvertisementFilter(django_filters.FilterSet): """Фильтры для объявлений.""" # TODO: задайте требуемые фильтры + created_at = django_filters.DateFromToRangeFilter() class Meta: model = Advertisement + fields = ['id', 'title', 'description', 'creator', 'status', 'created_at'] diff --git a/3.3-permissions/api_with_restrictions/advertisements/serializers.py b/3.3-permissions/api_with_restrictions/advertisements/serializers.py index be8ccadfa..9c68db6d8 100644 --- a/3.3-permissions/api_with_restrictions/advertisements/serializers.py +++ b/3.3-permissions/api_with_restrictions/advertisements/serializers.py @@ -1,7 +1,6 @@ from django.contrib.auth.models import User from rest_framework import serializers - -from advertisements.models import Advertisement +from advertisements.models import Advertisement, AdvertisementStatusChoices class UserSerializer(serializers.ModelSerializer): @@ -23,7 +22,7 @@ class AdvertisementSerializer(serializers.ModelSerializer): class Meta: model = Advertisement fields = ('id', 'title', 'description', 'creator', - 'status', 'created_at', ) + 'status', 'created_at',) def create(self, validated_data): """Метод для создания""" @@ -41,5 +40,19 @@ def validate(self, data): """Метод для валидации. Вызывается при создании и обновлении.""" # TODO: добавьте требуемую валидацию + user = self.context["request"].user + advertisement_qnt = Advertisement.objects.filter(creator=user, status=AdvertisementStatusChoices.OPEN).count() + if self.instance is not None: + if self.instance.creator != user: + raise serializers.ValidationError("Нет доступа!") + advertisement_status = data.get("status") + if advertisement_status is not None: + if advertisement_status == AdvertisementStatusChoices.OPEN and advertisement_qnt >= 10: + raise serializers.ValidationError("Слишком много объявлений!") + else: + if advertisement_qnt >= 10: + raise serializers.ValidationError("Слишком много объявлений!") return data + + diff --git a/3.3-permissions/api_with_restrictions/advertisements/views.py b/3.3-permissions/api_with_restrictions/advertisements/views.py index 996b6c345..94c4a8574 100644 --- a/3.3-permissions/api_with_restrictions/advertisements/views.py +++ b/3.3-permissions/api_with_restrictions/advertisements/views.py @@ -1,15 +1,37 @@ -from rest_framework.permissions import IsAuthenticated +import django_filters +from rest_framework.permissions import IsAuthenticated, BasePermission +from rest_framework.throttling import AnonRateThrottle, UserRateThrottle from rest_framework.viewsets import ModelViewSet +from advertisements.filters import AdvertisementFilter +from advertisements.models import Advertisement + +from advertisements.serializers import AdvertisementSerializer + + +class OnlyOwnerDeletePermission(BasePermission): + def has_object_permission(self, request, view, obj): + if request.method == "DELETE": + return obj.creator_id == request.user.id # prevent fetching whole user model + return True # anyone can do any other action + class AdvertisementViewSet(ModelViewSet): """ViewSet для объявлений.""" + queryset = Advertisement.objects.all() # TODO: настройте ViewSet, укажите атрибуты для кверисета, # сериализаторов и фильтров + serializer_class = AdvertisementSerializer + throttle_classes = [AnonRateThrottle, UserRateThrottle] + filter_backends = [django_filters.rest_framework.DjangoFilterBackend] + filter_class = AdvertisementFilter + def get_permissions(self): """Получение прав для действий.""" - if self.action in ["create", "update", "partial_update"]: - return [IsAuthenticated()] + + if self.action in ["create", "update", "partial_update", "destroy"]: + return [IsAuthenticated(), OnlyOwnerDeletePermission()] return [] + diff --git a/3.3-permissions/api_with_restrictions/api_with_restrictions/settings.py b/3.3-permissions/api_with_restrictions/api_with_restrictions/settings.py index f5e94d9fc..534c453ba 100644 --- a/3.3-permissions/api_with_restrictions/api_with_restrictions/settings.py +++ b/3.3-permissions/api_with_restrictions/api_with_restrictions/settings.py @@ -56,6 +56,7 @@ ] ROOT_URLCONF = 'api_with_restrictions.urls' +#ROOT_URLCONF = 'auth.urls' TEMPLATES = [ { @@ -73,11 +74,7 @@ }, ] -REST_FRAMEWORK = { - 'DEFAULT_AUTHENTICATION_CLASSES': [ - 'rest_framework.authentication.TokenAuthentication', - ] -} + WSGI_APPLICATION = 'api_with_restrictions.wsgi.application' @@ -91,6 +88,8 @@ 'NAME': 'netology_classified_ads', 'HOST': '127.0.0.1', 'PORT': '5432', + 'USER': 'postgres', + 'PASSWORD': 'postgres', } } @@ -132,3 +131,19 @@ # https://docs.djangoproject.com/en/3.1/howto/static-files/ STATIC_URL = '/static/' + + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': [ + 'rest_framework.authentication.TokenAuthentication', + ], + 'DEFAULT_PERMISSION_CLASSES': [ + #'rest_framework.permissions.IsAuthenticated', + ], + 'DEFAULT_THROTTLE_RATES': { + 'user': '20/minute', + 'anon': '10/minute', + } +} \ No newline at end of file diff --git a/3.3-permissions/api_with_restrictions/api_with_restrictions/urls.py b/3.3-permissions/api_with_restrictions/api_with_restrictions/urls.py index b13dcf24b..64701f104 100644 --- a/3.3-permissions/api_with_restrictions/api_with_restrictions/urls.py +++ b/3.3-permissions/api_with_restrictions/api_with_restrictions/urls.py @@ -18,9 +18,11 @@ from rest_framework.routers import DefaultRouter +from advertisements.views import AdvertisementViewSet + router = DefaultRouter() # TODO: подключите `AdvertisementViewSet` - +router.register('advertisements', AdvertisementViewSet) urlpatterns = [ path('api/', include(router.urls)), From b84c2c5b9b6a62d81373d0fe50774b1474dce996 Mon Sep 17 00:00:00 2001 From: amurasm <101619423+amurasm@users.noreply.github.com> Date: Wed, 11 May 2022 09:05:13 +0400 Subject: [PATCH 11/11] ValidationError --- .../api_with_restrictions/advertisements/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3.3-permissions/api_with_restrictions/advertisements/serializers.py b/3.3-permissions/api_with_restrictions/advertisements/serializers.py index 9c68db6d8..bfe7b8765 100644 --- a/3.3-permissions/api_with_restrictions/advertisements/serializers.py +++ b/3.3-permissions/api_with_restrictions/advertisements/serializers.py @@ -44,7 +44,7 @@ def validate(self, data): advertisement_qnt = Advertisement.objects.filter(creator=user, status=AdvertisementStatusChoices.OPEN).count() if self.instance is not None: if self.instance.creator != user: - raise serializers.ValidationError("Нет доступа!") + raise serializers.ValidationError("You do not have permission to perform this action.") advertisement_status = data.get("status") if advertisement_status is not None: if advertisement_status == AdvertisementStatusChoices.OPEN and advertisement_qnt >= 10: