From f127665195bd8687d0fb38ddd46bf063f9ee76ba Mon Sep 17 00:00:00 2001 From: andreyPromaster Date: Sun, 13 Feb 2022 20:58:24 +0300 Subject: [PATCH 1/8] Add registation --- user/serializers.py | 41 +++++++++++++++++++++++++++++++++++++++++ user/urls.py | 5 +++-- user/views.py | 12 ++++++++++-- 3 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 user/serializers.py diff --git a/user/serializers.py b/user/serializers.py new file mode 100644 index 0000000..e61ba5c --- /dev/null +++ b/user/serializers.py @@ -0,0 +1,41 @@ +from rest_framework import serializers +from django.contrib.auth.models import User +from rest_framework.validators import UniqueValidator +from django.contrib.auth.password_validation import validate_password + + +class RegisterSerializer(serializers.ModelSerializer): + email = serializers.EmailField( + required=True, + validators=[UniqueValidator(queryset=User.objects.all())] + ) + + password = serializers.CharField(write_only=True, required=True, validators=[validate_password]) + password2 = serializers.CharField(write_only=True, required=True) + + class Meta: + model = User + fields = ('username', 'password', 'password2', 'email', 'first_name', 'last_name') + extra_kwargs = { + 'first_name': {'required': True}, + 'last_name': {'required': True} + } + + def validate(self, attrs): + if attrs['password'] != attrs['password2']: + raise serializers.ValidationError({"password": "Password fields didn't match."}) + + return attrs + + def create(self, validated_data): + user = User.objects.create( + username=validated_data['username'], + email=validated_data['email'], + first_name=validated_data['first_name'], + last_name=validated_data['last_name'] + ) + + user.set_password(validated_data['password']) + user.save() + + return user \ No newline at end of file diff --git a/user/urls.py b/user/urls.py index cde0152..873b32f 100644 --- a/user/urls.py +++ b/user/urls.py @@ -1,12 +1,13 @@ from django.urls import path, include -from rest_framework.authtoken.views import obtain_auth_token from rest_framework_simplejwt import views as jwt_views +from user.views import RegisterView + urlpatterns = [ path('token/obtain/', jwt_views.TokenObtainPairView.as_view(), name='token_create'), path('token/refresh/', jwt_views.TokenRefreshView.as_view(), name='token_refresh'), path('base-auth/', include('rest_framework.urls')), - path('api-token-auth/', obtain_auth_token, name='api_token_auth'), + path('register/', RegisterView.as_view(), name='auth_register'), ] \ No newline at end of file diff --git a/user/views.py b/user/views.py index 91ea44a..ee16bc7 100644 --- a/user/views.py +++ b/user/views.py @@ -1,3 +1,11 @@ -from django.shortcuts import render +from django.contrib.auth.models import User +from rest_framework.permissions import AllowAny -# Create your views here. +from .serializers import RegisterSerializer +from rest_framework import generics + + +class RegisterView(generics.CreateAPIView): + queryset = User.objects.all() + permission_classes = (AllowAny,) + serializer_class = RegisterSerializer From e5956bb9200351d6b1f0699459b002ce2de300c6 Mon Sep 17 00:00:00 2001 From: andreyPromaster Date: Wed, 23 Feb 2022 19:46:06 +0300 Subject: [PATCH 2/8] Implement base use cases --- .gitignore | 4 +- docker-compose.yaml | 2 +- entertainment/admin.py | 18 ++++++- entertainment/migrations/0001_initial.py | 26 ++++++++++ .../0002_comment_alter_place_type_rate.py | 42 +++++++++++++++ .../0003_alter_comment_related_comments.py | 18 +++++++ .../migrations/0004_alter_rate_comment.py | 19 +++++++ .../migrations/0005_alter_rate_place.py | 19 +++++++ entertainment/models.py | 52 ++++++++++++++++++- entertainment/serializers.py | 20 +++++++ entertainment/urls.py | 4 +- entertainment/views.py | 26 +++++++++- poetry.lock | 47 ++++++++++++++++- pyproject.toml | 1 + rating/settings.py | 5 +- rating/wsgi.py | 2 +- user/serializers.py | 16 +++--- 17 files changed, 303 insertions(+), 18 deletions(-) create mode 100644 entertainment/migrations/0001_initial.py create mode 100644 entertainment/migrations/0002_comment_alter_place_type_rate.py create mode 100644 entertainment/migrations/0003_alter_comment_related_comments.py create mode 100644 entertainment/migrations/0004_alter_rate_comment.py create mode 100644 entertainment/migrations/0005_alter_rate_place.py create mode 100644 entertainment/serializers.py diff --git a/.gitignore b/.gitignore index 8deeaca..6ee7283 100644 --- a/.gitignore +++ b/.gitignore @@ -128,4 +128,6 @@ dmypy.json .pyre/ #django media -media/ \ No newline at end of file +media/ + +*.jpg \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index 5e48a21..95288dc 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -3,7 +3,7 @@ version: '3' services: postgres: - image: postgres:13 + image: postgis/postgis volumes: - database-data:/var/lib/postgresql/data/ ports: diff --git a/entertainment/admin.py b/entertainment/admin.py index 8c38f3f..6564bdb 100644 --- a/entertainment/admin.py +++ b/entertainment/admin.py @@ -1,3 +1,19 @@ from django.contrib import admin -# Register your models here. +from django.contrib.gis.admin import OSMGeoAdmin +from .models import Place, Rate, Comment + + +@admin.register(Place) +class PlaceAdmin(OSMGeoAdmin): + list_display = ('name', 'location', 'address', 'image', 'type') + + +@admin.register(Rate) +class RateAdmin(admin.ModelAdmin): + list_display = ("rating", "created_at", "user", "comment") + + +@admin.register(Comment) +class CommentAdmin(admin.ModelAdmin): + list_display = ("text", "created_at",) \ No newline at end of file diff --git a/entertainment/migrations/0001_initial.py b/entertainment/migrations/0001_initial.py new file mode 100644 index 0000000..bfac0b5 --- /dev/null +++ b/entertainment/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 4.0.2 on 2022-02-19 19:04 + +import django.contrib.gis.db.models.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Place', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('location', django.contrib.gis.db.models.fields.PointField(srid=4326)), + ('address', models.CharField(max_length=255)), + ('image', models.ImageField(blank=True, null=True, upload_to='')), + ('type', models.CharField(choices=[('cafe', 'Cafe'), ('restaurant', 'Restaurant'), ('sport', 'Sport'), ('tech', 'tech'), ('shop', 'Shop')], default='cafe', max_length=20)), + ], + ), + ] diff --git a/entertainment/migrations/0002_comment_alter_place_type_rate.py b/entertainment/migrations/0002_comment_alter_place_type_rate.py new file mode 100644 index 0000000..77d05e0 --- /dev/null +++ b/entertainment/migrations/0002_comment_alter_place_type_rate.py @@ -0,0 +1,42 @@ +# Generated by Django 4.0.2 on 2022-02-20 18:59 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import entertainment.models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('entertainment', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Comment', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('text', models.TextField()), + ('created_at', models.DateField(auto_now_add=True)), + ('related_comments', models.ManyToManyField(to='entertainment.Comment')), + ], + ), + migrations.AlterField( + model_name='place', + name='type', + field=models.CharField(choices=[('cafe', 'Cafe'), ('restaurant', 'Restaurant'), ('sport', 'Sport'), ('tech', 'Tech'), ('shop', 'Shop'), ('study', 'Study')], default='cafe', max_length=20), + ), + migrations.CreateModel( + name='Rate', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('rating', models.PositiveSmallIntegerField()), + ('created_at', models.DateField(auto_now_add=True)), + ('comment', models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to='entertainment.comment')), + ('place', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='place', to='entertainment.place')), + ('user', models.OneToOneField(on_delete=models.SET(entertainment.models.get_sentinel_user), related_name='user', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/entertainment/migrations/0003_alter_comment_related_comments.py b/entertainment/migrations/0003_alter_comment_related_comments.py new file mode 100644 index 0000000..4bd0a43 --- /dev/null +++ b/entertainment/migrations/0003_alter_comment_related_comments.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.2 on 2022-02-20 19:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('entertainment', '0002_comment_alter_place_type_rate'), + ] + + operations = [ + migrations.AlterField( + model_name='comment', + name='related_comments', + field=models.ManyToManyField(blank=True, to='entertainment.Comment'), + ), + ] diff --git a/entertainment/migrations/0004_alter_rate_comment.py b/entertainment/migrations/0004_alter_rate_comment.py new file mode 100644 index 0000000..6fb3f9a --- /dev/null +++ b/entertainment/migrations/0004_alter_rate_comment.py @@ -0,0 +1,19 @@ +# Generated by Django 4.0.2 on 2022-02-20 19:08 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('entertainment', '0003_alter_comment_related_comments'), + ] + + operations = [ + migrations.AlterField( + model_name='rate', + name='comment', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='entertainment.comment'), + ), + ] diff --git a/entertainment/migrations/0005_alter_rate_place.py b/entertainment/migrations/0005_alter_rate_place.py new file mode 100644 index 0000000..8a354e5 --- /dev/null +++ b/entertainment/migrations/0005_alter_rate_place.py @@ -0,0 +1,19 @@ +# Generated by Django 4.0.2 on 2022-02-20 19:22 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('entertainment', '0004_alter_rate_comment'), + ] + + operations = [ + migrations.AlterField( + model_name='rate', + name='place', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ratings', to='entertainment.place'), + ), + ] diff --git a/entertainment/models.py b/entertainment/models.py index 71a8362..f807317 100644 --- a/entertainment/models.py +++ b/entertainment/models.py @@ -1,3 +1,51 @@ -from django.db import models +from django.contrib.gis.db import models + +from django.contrib.auth import get_user_model + +User = get_user_model() + + +def get_sentinel_user(): + return get_user_model().objects.get_or_create(username='deleted')[0] + + +class Place(models.Model): + name = models.CharField(max_length=255) + location = models.PointField() + address = models.CharField(max_length=255) + image = models.ImageField(null=True, blank=True) + CAFE = 'cafe' + RESTAURANT = 'restaurant' + SPORT = 'sport' + TECH = 'tech' + SHOP = 'shop' + STUDY = 'study' + TYPES_OF_PLACE = [ + (CAFE, 'Cafe'), + (RESTAURANT, 'Restaurant'), + (SPORT, 'Sport'), + (TECH, 'Tech'), + (SHOP, 'Shop'), + (STUDY, 'Study'), + ] + type = models.CharField( + max_length=20, + choices=TYPES_OF_PLACE, + default=CAFE, + ) + + +class Comment(models.Model): + text = models.TextField() + created_at = models.DateField(auto_now_add=True) + related_comments = models.ManyToManyField("self", symmetrical=False, blank=True) + + +class Rate(models.Model): + rating = models.PositiveSmallIntegerField() + created_at = models.DateField(auto_now_add=True) + place = models.ForeignKey(Place, on_delete=models.CASCADE, related_name="ratings") + user = models.OneToOneField(User, on_delete=models.SET(get_sentinel_user), related_name="user") + comment = models.OneToOneField(Comment, on_delete=models.SET_NULL, null=True, blank=True) + -# Create your models here. diff --git a/entertainment/serializers.py b/entertainment/serializers.py new file mode 100644 index 0000000..2a2fba8 --- /dev/null +++ b/entertainment/serializers.py @@ -0,0 +1,20 @@ +from rest_framework import serializers + +from entertainment.models import Place, Rate +from user.serializers import UserSerializer + + +class RateSerializer(serializers.ModelSerializer): + user = UserSerializer() + + class Meta: + model = Rate + fields = ("user", "rating", "created_at", "comment") + + +class PlaceSerializer(serializers.ModelSerializer): + ratings = RateSerializer(many=True, read_only=True) + + class Meta: + model = Place + fields = ("id", "name", "location", "address", "image", "type", "ratings") \ No newline at end of file diff --git a/entertainment/urls.py b/entertainment/urls.py index b4fd30e..37454c8 100644 --- a/entertainment/urls.py +++ b/entertainment/urls.py @@ -1,7 +1,9 @@ from django.urls import path from . import views +from .views import PlaceListAPIView urlpatterns = [ - path('ping', views.ping, name='index'), + path("ping", views.ping, name="index"), + path("places", PlaceListAPIView.as_view(), name="place-list") ] \ No newline at end of file diff --git a/entertainment/views.py b/entertainment/views.py index 573bd6f..f972db0 100644 --- a/entertainment/views.py +++ b/entertainment/views.py @@ -1,7 +1,31 @@ +from django.contrib.gis.measure import D from rest_framework.decorators import api_view from rest_framework.response import Response +from rest_framework import generics +from rest_framework.permissions import IsAuthenticated + +from django.contrib.gis.geos import Point +from entertainment.models import Place +from entertainment.serializers import PlaceSerializer @api_view(['GET']) def ping(request): - return Response({"text": "pong"}) \ No newline at end of file + return Response({"text": "pong"}) + + +class PlaceListAPIView(generics.ListCreateAPIView): + permission_classes = [IsAuthenticated, ] + queryset = Place.objects.all() + serializer_class = PlaceSerializer + + def filter_queryset(self, queryset): + if ("longitude" in self.request.query_params + and "latitude" in self.request.query_params): + point = Point(float(self.request.query_params["longitude"]), + float(self.request.query_params["latitude"]), + srid=4326) + queryset = queryset.filter(location__distance_lt =(point, D(km=2))) + return queryset + + diff --git a/poetry.lock b/poetry.lock index 76d6afc..f9af7ed 100644 --- a/poetry.lock +++ b/poetry.lock @@ -81,6 +81,14 @@ lint = ["flake8", "pep8", "isort"] python-jose = ["python-jose (==3.0.0)"] test = ["cryptography", "pytest-cov", "pytest-django", "pytest-xdist", "pytest", "tox"] +[[package]] +name = "pillow" +version = "9.0.1" +description = "Python Imaging Library (Fork)" +category = "main" +optional = false +python-versions = ">=3.7" + [[package]] name = "psycopg2-binary" version = "2.9.3" @@ -130,7 +138,7 @@ python-versions = ">=2" [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "b59e6f9cb0086e31a3a9d66c3b4e4d0f1a7c7cef5614de20c1b93981a97e1f8a" +content-hash = "cf54f06e3cc70fc7cf6f80e7a67a76e8fd7573a4e484c7695249c13d7732e3f5" [metadata.files] asgiref = [ @@ -171,6 +179,43 @@ djangorestframework-simplejwt = [ {file = "djangorestframework_simplejwt-5.0.0-py3-none-any.whl", hash = "sha256:ddcbeef51155d1e71410dde44b581c7e04cfb74776f5337661ac3ef4c0c367e6"}, {file = "djangorestframework_simplejwt-5.0.0.tar.gz", hash = "sha256:30b10e7732395c44d21980f773214d2b9bdeadf2a6c6809cd1a7c9abe272873c"}, ] +pillow = [ + {file = "Pillow-9.0.1-1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a5d24e1d674dd9d72c66ad3ea9131322819ff86250b30dc5821cbafcfa0b96b4"}, + {file = "Pillow-9.0.1-1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2632d0f846b7c7600edf53c48f8f9f1e13e62f66a6dbc15191029d950bfed976"}, + {file = "Pillow-9.0.1-1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b9618823bd237c0d2575283f2939655f54d51b4527ec3972907a927acbcc5bfc"}, + {file = "Pillow-9.0.1-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:9bfdb82cdfeccec50aad441afc332faf8606dfa5e8efd18a6692b5d6e79f00fd"}, + {file = "Pillow-9.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5100b45a4638e3c00e4d2320d3193bdabb2d75e79793af7c3eb139e4f569f16f"}, + {file = "Pillow-9.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:528a2a692c65dd5cafc130de286030af251d2ee0483a5bf50c9348aefe834e8a"}, + {file = "Pillow-9.0.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f29d831e2151e0b7b39981756d201f7108d3d215896212ffe2e992d06bfe049"}, + {file = "Pillow-9.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:855c583f268edde09474b081e3ddcd5cf3b20c12f26e0d434e1386cc5d318e7a"}, + {file = "Pillow-9.0.1-cp310-cp310-win32.whl", hash = "sha256:d9d7942b624b04b895cb95af03a23407f17646815495ce4547f0e60e0b06f58e"}, + {file = "Pillow-9.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:81c4b81611e3a3cb30e59b0cf05b888c675f97e3adb2c8672c3154047980726b"}, + {file = "Pillow-9.0.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:413ce0bbf9fc6278b2d63309dfeefe452835e1c78398efb431bab0672fe9274e"}, + {file = "Pillow-9.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80fe64a6deb6fcfdf7b8386f2cf216d329be6f2781f7d90304351811fb591360"}, + {file = "Pillow-9.0.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cef9c85ccbe9bee00909758936ea841ef12035296c748aaceee535969e27d31b"}, + {file = "Pillow-9.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d19397351f73a88904ad1aee421e800fe4bbcd1aeee6435fb62d0a05ccd1030"}, + {file = "Pillow-9.0.1-cp37-cp37m-win32.whl", hash = "sha256:d21237d0cd37acded35154e29aec853e945950321dd2ffd1a7d86fe686814669"}, + {file = "Pillow-9.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ede5af4a2702444a832a800b8eb7f0a7a1c0eed55b644642e049c98d589e5092"}, + {file = "Pillow-9.0.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:b5b3f092fe345c03bca1e0b687dfbb39364b21ebb8ba90e3fa707374b7915204"}, + {file = "Pillow-9.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:335ace1a22325395c4ea88e00ba3dc89ca029bd66bd5a3c382d53e44f0ccd77e"}, + {file = "Pillow-9.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db6d9fac65bd08cea7f3540b899977c6dee9edad959fa4eaf305940d9cbd861c"}, + {file = "Pillow-9.0.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f154d173286a5d1863637a7dcd8c3437bb557520b01bddb0be0258dcb72696b5"}, + {file = "Pillow-9.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14d4b1341ac07ae07eb2cc682f459bec932a380c3b122f5540432d8977e64eae"}, + {file = "Pillow-9.0.1-cp38-cp38-win32.whl", hash = "sha256:effb7749713d5317478bb3acb3f81d9d7c7f86726d41c1facca068a04cf5bb4c"}, + {file = "Pillow-9.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:7f7609a718b177bf171ac93cea9fd2ddc0e03e84d8fa4e887bdfc39671d46b00"}, + {file = "Pillow-9.0.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:80ca33961ced9c63358056bd08403ff866512038883e74f3a4bf88ad3eb66838"}, + {file = "Pillow-9.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c3c33ac69cf059bbb9d1a71eeaba76781b450bc307e2291f8a4764d779a6b28"}, + {file = "Pillow-9.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12875d118f21cf35604176872447cdb57b07126750a33748bac15e77f90f1f9c"}, + {file = "Pillow-9.0.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:514ceac913076feefbeaf89771fd6febde78b0c4c1b23aaeab082c41c694e81b"}, + {file = "Pillow-9.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3c5c79ab7dfce6d88f1ba639b77e77a17ea33a01b07b99840d6ed08031cb2a7"}, + {file = "Pillow-9.0.1-cp39-cp39-win32.whl", hash = "sha256:718856856ba31f14f13ba885ff13874be7fefc53984d2832458f12c38205f7f7"}, + {file = "Pillow-9.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:f25ed6e28ddf50de7e7ea99d7a976d6a9c415f03adcaac9c41ff6ff41b6d86ac"}, + {file = "Pillow-9.0.1-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:011233e0c42a4a7836498e98c1acf5e744c96a67dd5032a6f666cc1fb97eab97"}, + {file = "Pillow-9.0.1-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:253e8a302a96df6927310a9d44e6103055e8fb96a6822f8b7f514bb7ef77de56"}, + {file = "Pillow-9.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6295f6763749b89c994fcb6d8a7f7ce03c3992e695f89f00b741b4580b199b7e"}, + {file = "Pillow-9.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:a9f44cd7e162ac6191491d7249cceb02b8116b0f7e847ee33f739d7cb1ea1f70"}, + {file = "Pillow-9.0.1.tar.gz", hash = "sha256:6c8bc8238a7dfdaf7a75f5ec5a663f4173f8c367e5a39f87e720495e1eed75fa"}, +] psycopg2-binary = [ {file = "psycopg2-binary-2.9.3.tar.gz", hash = "sha256:761df5313dc15da1502b21453642d7599d26be88bff659382f8f9747c7ebea4e"}, {file = "psycopg2_binary-2.9.3-cp310-cp310-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:539b28661b71da7c0e428692438efbcd048ca21ea81af618d845e06ebfd29478"}, diff --git a/pyproject.toml b/pyproject.toml index 8f63f49..4121649 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ djangorestframework = "^3.13.1" django-filter = "^21.1" psycopg2-binary = "^2.9.3" djangorestframework-simplejwt = "^5.0.0" +Pillow = "^9.0.1" [tool.poetry.dev-dependencies] diff --git a/rating/settings.py b/rating/settings.py index 4a08524..8482b0b 100644 --- a/rating/settings.py +++ b/rating/settings.py @@ -40,6 +40,9 @@ 'django.contrib.staticfiles', 'rest_framework', 'rest_framework_simplejwt', + 'django.contrib.gis', + 'entertainment', + 'user', ] MIDDLEWARE = [ @@ -78,7 +81,7 @@ DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.postgresql', + 'ENGINE': 'django.contrib.gis.db.backends.postgis', 'NAME': os.environ['DATABASE'], 'USER': "rating", 'PASSWORD': os.environ['DB_PASSWORD'], diff --git a/rating/wsgi.py b/rating/wsgi.py index 57791f0..49b0d75 100644 --- a/rating/wsgi.py +++ b/rating/wsgi.py @@ -11,6 +11,6 @@ from django.core.wsgi import get_wsgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'rating1.settings') +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'rating.settings') application = get_wsgi_application() diff --git a/user/serializers.py b/user/serializers.py index e61ba5c..cca1f7f 100644 --- a/user/serializers.py +++ b/user/serializers.py @@ -15,11 +15,7 @@ class RegisterSerializer(serializers.ModelSerializer): class Meta: model = User - fields = ('username', 'password', 'password2', 'email', 'first_name', 'last_name') - extra_kwargs = { - 'first_name': {'required': True}, - 'last_name': {'required': True} - } + fields = ('username', 'password', 'password2', 'email',) def validate(self, attrs): if attrs['password'] != attrs['password2']: @@ -31,11 +27,15 @@ def create(self, validated_data): user = User.objects.create( username=validated_data['username'], email=validated_data['email'], - first_name=validated_data['first_name'], - last_name=validated_data['last_name'] ) user.set_password(validated_data['password']) user.save() - return user \ No newline at end of file + return user + + +class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ('username',) \ No newline at end of file From b5e2c6dcc7ce790a88856a83f7658759927bcca3 Mon Sep 17 00:00:00 2001 From: andreyPromaster Date: Thu, 24 Feb 2022 13:17:40 +0300 Subject: [PATCH 3/8] Add image uploading --- entertainment/serializers.py | 14 +++++++++++++- entertainment/views.py | 6 ++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/entertainment/serializers.py b/entertainment/serializers.py index 2a2fba8..5fe723e 100644 --- a/entertainment/serializers.py +++ b/entertainment/serializers.py @@ -1,3 +1,4 @@ +from django.contrib.gis.geos import Point from rest_framework import serializers from entertainment.models import Place, Rate @@ -14,7 +15,18 @@ class Meta: class PlaceSerializer(serializers.ModelSerializer): ratings = RateSerializer(many=True, read_only=True) + longitude = serializers.CharField(source="location.x") + latitude = serializers.CharField(source="location.y") class Meta: model = Place - fields = ("id", "name", "location", "address", "image", "type", "ratings") \ No newline at end of file + fields = ("id", "name", "longitude", "latitude", "address", "image", "type", "ratings") + + def create(self, validated_data): + location = Point( + float(validated_data.get("location").get('x')), + float(validated_data.get("location").get('y')), + srid=4326 + ) + validated_data.pop("location") + return Place.objects.create(location=location, **validated_data) \ No newline at end of file diff --git a/entertainment/views.py b/entertainment/views.py index f972db0..ae6218c 100644 --- a/entertainment/views.py +++ b/entertainment/views.py @@ -1,5 +1,6 @@ from django.contrib.gis.measure import D from rest_framework.decorators import api_view +from rest_framework.parsers import MultiPartParser, FormParser, FileUploadParser from rest_framework.response import Response from rest_framework import generics from rest_framework.permissions import IsAuthenticated @@ -18,6 +19,11 @@ class PlaceListAPIView(generics.ListCreateAPIView): permission_classes = [IsAuthenticated, ] queryset = Place.objects.all() serializer_class = PlaceSerializer + parser_classes = (MultiPartParser, FormParser, FileUploadParser) + + def perform_create(self, serializer): + photo = self.request.data['photo'] + serializer.save(image=photo) def filter_queryset(self, queryset): if ("longitude" in self.request.query_params From 0fa9b5e5a8a0a6ba86c79c6d79533933093c0fcd Mon Sep 17 00:00:00 2001 From: andreyPromaster Date: Sat, 26 Feb 2022 21:11:01 +0300 Subject: [PATCH 4/8] Add support comment, fix models, add validators --- .../migrations/0006_alter_rate_user.py | 21 +++++++++ .../migrations/0007_alter_rate_rating.py | 19 ++++++++ .../0008_alter_rate_unique_together.py | 19 ++++++++ ...omment_created_at_alter_rate_created_at.py | 23 ++++++++++ entertainment/models.py | 12 +++-- entertainment/serializers.py | 12 ++++- entertainment/urls.py | 5 ++- entertainment/validators.py | 6 +++ entertainment/views.py | 45 ++++++++++++++++--- 9 files changed, 147 insertions(+), 15 deletions(-) create mode 100644 entertainment/migrations/0006_alter_rate_user.py create mode 100644 entertainment/migrations/0007_alter_rate_rating.py create mode 100644 entertainment/migrations/0008_alter_rate_unique_together.py create mode 100644 entertainment/migrations/0009_alter_comment_created_at_alter_rate_created_at.py create mode 100644 entertainment/validators.py diff --git a/entertainment/migrations/0006_alter_rate_user.py b/entertainment/migrations/0006_alter_rate_user.py new file mode 100644 index 0000000..fe42839 --- /dev/null +++ b/entertainment/migrations/0006_alter_rate_user.py @@ -0,0 +1,21 @@ +# Generated by Django 4.0.2 on 2022-02-26 15:39 + +from django.conf import settings +from django.db import migrations, models +import entertainment.models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('entertainment', '0005_alter_rate_place'), + ] + + operations = [ + migrations.AlterField( + model_name='rate', + name='user', + field=models.ForeignKey(on_delete=models.SET(entertainment.models.get_sentinel_user), related_name='user', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/entertainment/migrations/0007_alter_rate_rating.py b/entertainment/migrations/0007_alter_rate_rating.py new file mode 100644 index 0000000..2e8cd2e --- /dev/null +++ b/entertainment/migrations/0007_alter_rate_rating.py @@ -0,0 +1,19 @@ +# Generated by Django 4.0.2 on 2022-02-26 15:47 + +from django.db import migrations, models +import entertainment.validators + + +class Migration(migrations.Migration): + + dependencies = [ + ('entertainment', '0006_alter_rate_user'), + ] + + operations = [ + migrations.AlterField( + model_name='rate', + name='rating', + field=models.PositiveSmallIntegerField(validators=[entertainment.validators.validate_rating]), + ), + ] diff --git a/entertainment/migrations/0008_alter_rate_unique_together.py b/entertainment/migrations/0008_alter_rate_unique_together.py new file mode 100644 index 0000000..be97a9d --- /dev/null +++ b/entertainment/migrations/0008_alter_rate_unique_together.py @@ -0,0 +1,19 @@ +# Generated by Django 4.0.2 on 2022-02-26 15:51 + +from django.conf import settings +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('entertainment', '0007_alter_rate_rating'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='rate', + unique_together={('place', 'user')}, + ), + ] diff --git a/entertainment/migrations/0009_alter_comment_created_at_alter_rate_created_at.py b/entertainment/migrations/0009_alter_comment_created_at_alter_rate_created_at.py new file mode 100644 index 0000000..94acb6c --- /dev/null +++ b/entertainment/migrations/0009_alter_comment_created_at_alter_rate_created_at.py @@ -0,0 +1,23 @@ +# Generated by Django 4.0.2 on 2022-02-26 16:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('entertainment', '0008_alter_rate_unique_together'), + ] + + operations = [ + migrations.AlterField( + model_name='comment', + name='created_at', + field=models.DateTimeField(auto_now_add=True), + ), + migrations.AlterField( + model_name='rate', + name='created_at', + field=models.DateTimeField(auto_now_add=True), + ), + ] diff --git a/entertainment/models.py b/entertainment/models.py index f807317..00038c2 100644 --- a/entertainment/models.py +++ b/entertainment/models.py @@ -2,6 +2,8 @@ from django.contrib.auth import get_user_model +from entertainment.validators import validate_rating + User = get_user_model() @@ -37,15 +39,17 @@ class Place(models.Model): class Comment(models.Model): text = models.TextField() - created_at = models.DateField(auto_now_add=True) + created_at = models.DateTimeField(auto_now_add=True) related_comments = models.ManyToManyField("self", symmetrical=False, blank=True) class Rate(models.Model): - rating = models.PositiveSmallIntegerField() - created_at = models.DateField(auto_now_add=True) + rating = models.PositiveSmallIntegerField(validators=[validate_rating,]) + created_at = models.DateTimeField(auto_now_add=True) place = models.ForeignKey(Place, on_delete=models.CASCADE, related_name="ratings") - user = models.OneToOneField(User, on_delete=models.SET(get_sentinel_user), related_name="user") + user = models.ForeignKey(User, on_delete=models.SET(get_sentinel_user), related_name="user") comment = models.OneToOneField(Comment, on_delete=models.SET_NULL, null=True, blank=True) + class Meta: + unique_together = [["place", "user"]] diff --git a/entertainment/serializers.py b/entertainment/serializers.py index 5fe723e..ca71074 100644 --- a/entertainment/serializers.py +++ b/entertainment/serializers.py @@ -1,12 +1,20 @@ from django.contrib.gis.geos import Point from rest_framework import serializers -from entertainment.models import Place, Rate +from entertainment.models import Place, Rate, Comment from user.serializers import UserSerializer +class CommentSerializer(serializers.ModelSerializer): + class Meta: + model = Comment + fields = ("id", "created_at", "text", "related_comments") + read_only_fields = ("related_comments", ) + + class RateSerializer(serializers.ModelSerializer): - user = UserSerializer() + user = UserSerializer(required=False) + comment = CommentSerializer(required=False) class Meta: model = Rate diff --git a/entertainment/urls.py b/entertainment/urls.py index 37454c8..0d137ef 100644 --- a/entertainment/urls.py +++ b/entertainment/urls.py @@ -1,9 +1,10 @@ from django.urls import path from . import views -from .views import PlaceListAPIView +from .views import PlaceListAPIView, RatingsAPIView urlpatterns = [ path("ping", views.ping, name="index"), - path("places", PlaceListAPIView.as_view(), name="place-list") + path("places", PlaceListAPIView.as_view({"get": "list", "post": "create"}), name="place-list"), + path("places//ratings", PlaceListAPIView.as_view({"post": "create_rating"}), name="comment-list"), ] \ No newline at end of file diff --git a/entertainment/validators.py b/entertainment/validators.py new file mode 100644 index 0000000..51722fe --- /dev/null +++ b/entertainment/validators.py @@ -0,0 +1,6 @@ +from django.core.exceptions import ValidationError + + +def validate_rating(value): + if value < 0 or value > 5: + raise ValidationError("Rating must be in [0, 5]") diff --git a/entertainment/views.py b/entertainment/views.py index ae6218c..0d03db7 100644 --- a/entertainment/views.py +++ b/entertainment/views.py @@ -1,13 +1,14 @@ from django.contrib.gis.measure import D -from rest_framework.decorators import api_view -from rest_framework.parsers import MultiPartParser, FormParser, FileUploadParser +from rest_framework.decorators import api_view, action +from rest_framework.exceptions import ValidationError +from rest_framework.parsers import MultiPartParser, FormParser, FileUploadParser, JSONParser from rest_framework.response import Response -from rest_framework import generics +from rest_framework import generics, mixins, viewsets, status from rest_framework.permissions import IsAuthenticated from django.contrib.gis.geos import Point -from entertainment.models import Place -from entertainment.serializers import PlaceSerializer +from entertainment.models import Place, Rate, Comment +from entertainment.serializers import PlaceSerializer, RateSerializer @api_view(['GET']) @@ -15,11 +16,11 @@ def ping(request): return Response({"text": "pong"}) -class PlaceListAPIView(generics.ListCreateAPIView): +class PlaceListAPIView(mixins.CreateModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet): permission_classes = [IsAuthenticated, ] queryset = Place.objects.all() serializer_class = PlaceSerializer - parser_classes = (MultiPartParser, FormParser, FileUploadParser) + parser_classes = (MultiPartParser, FormParser, JSONParser) def perform_create(self, serializer): photo = self.request.data['photo'] @@ -34,4 +35,34 @@ def filter_queryset(self, queryset): queryset = queryset.filter(location__distance_lt =(point, D(km=2))) return queryset + @action(detail=True, methods=['post']) + def create_rating(self, request, pk=None): + breakpoint() + place = self.get_object() + user = self.request.user + serializer = RateSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + if Rate.objects.filter(user=user, place=place): + raise ValidationError({"message": "User can create comment only ones"}) + + comment_data = serializer.validated_data.pop('comment', None) + if comment_data: + comment = Comment.objects.create(text=comment_data["text"]) + else: + comment = None + rate = Rate(**serializer.validated_data, place=place, user=user, comment=comment) + rate.save() + return Response({"message": "Rate was created"}, status=status.HTTP_201_CREATED) + + +class RatingsAPIView(generics.CreateAPIView): + permission_classes = [IsAuthenticated, ] + queryset = Rate.objects.all() + serializer_class = RateSerializer + + def perform_create(self, serializer): + serializer.save(user=self.request.user) + + From 31458f1ef66933ebe2b8e2b3cc02f64b76d37fe0 Mon Sep 17 00:00:00 2001 From: andreyPromaster Date: Sat, 26 Feb 2022 23:33:49 +0300 Subject: [PATCH 5/8] Add related comment --- ...ent_options_alter_rate_options_and_more.py | 30 +++++++++++++++++++ .../migrations/0011_alter_comment_options.py | 17 +++++++++++ entertainment/models.py | 8 +++-- entertainment/serializers.py | 16 ++++++++-- entertainment/urls.py | 3 +- entertainment/views.py | 26 +++++++++++----- 6 files changed, 88 insertions(+), 12 deletions(-) create mode 100644 entertainment/migrations/0010_alter_comment_options_alter_rate_options_and_more.py create mode 100644 entertainment/migrations/0011_alter_comment_options.py diff --git a/entertainment/migrations/0010_alter_comment_options_alter_rate_options_and_more.py b/entertainment/migrations/0010_alter_comment_options_alter_rate_options_and_more.py new file mode 100644 index 0000000..4780123 --- /dev/null +++ b/entertainment/migrations/0010_alter_comment_options_alter_rate_options_and_more.py @@ -0,0 +1,30 @@ +# Generated by Django 4.0.2 on 2022-02-26 19:25 + +from django.conf import settings +from django.db import migrations, models +import entertainment.models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('entertainment', '0009_alter_comment_created_at_alter_rate_created_at'), + ] + + operations = [ + migrations.AlterModelOptions( + name='comment', + options={'ordering': ['-created_at']}, + ), + migrations.AlterModelOptions( + name='rate', + options={'ordering': ['-created_at']}, + ), + migrations.AddField( + model_name='comment', + name='user', + field=models.ForeignKey(default=1, on_delete=models.SET(entertainment.models.get_sentinel_user), related_name='users_comment', to=settings.AUTH_USER_MODEL), + preserve_default=False, + ), + ] diff --git a/entertainment/migrations/0011_alter_comment_options.py b/entertainment/migrations/0011_alter_comment_options.py new file mode 100644 index 0000000..b713c92 --- /dev/null +++ b/entertainment/migrations/0011_alter_comment_options.py @@ -0,0 +1,17 @@ +# Generated by Django 4.0.2 on 2022-02-26 20:33 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('entertainment', '0010_alter_comment_options_alter_rate_options_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='comment', + options={'ordering': ['created_at']}, + ), + ] diff --git a/entertainment/models.py b/entertainment/models.py index 00038c2..c71f45e 100644 --- a/entertainment/models.py +++ b/entertainment/models.py @@ -41,6 +41,10 @@ class Comment(models.Model): text = models.TextField() created_at = models.DateTimeField(auto_now_add=True) related_comments = models.ManyToManyField("self", symmetrical=False, blank=True) + user = models.ForeignKey(User, on_delete=models.SET(get_sentinel_user), related_name="users_comment") + + class Meta: + ordering = ['created_at'] class Rate(models.Model): @@ -51,5 +55,5 @@ class Rate(models.Model): comment = models.OneToOneField(Comment, on_delete=models.SET_NULL, null=True, blank=True) class Meta: - unique_together = [["place", "user"]] - + unique_together = ["place", "user"] + ordering = ['-created_at'] diff --git a/entertainment/serializers.py b/entertainment/serializers.py index ca71074..e1633be 100644 --- a/entertainment/serializers.py +++ b/entertainment/serializers.py @@ -5,11 +5,23 @@ from user.serializers import UserSerializer +class RelatedCommentSerializer(serializers.ModelSerializer): + username = serializers.CharField(source="user.username", required=False) + + class Meta: + model = Comment + fields = ("id", "created_at", "text", "username") + read_only_fields = ("username", ) + + class CommentSerializer(serializers.ModelSerializer): + username = serializers.CharField(source="user.username", required=False) + related_comments = RelatedCommentSerializer(many=True, read_only=True) + class Meta: model = Comment - fields = ("id", "created_at", "text", "related_comments") - read_only_fields = ("related_comments", ) + fields = ("id", "created_at", "text", "related_comments", "username") + read_only_fields = ("related_comments", "username") class RateSerializer(serializers.ModelSerializer): diff --git a/entertainment/urls.py b/entertainment/urls.py index 0d137ef..7d10959 100644 --- a/entertainment/urls.py +++ b/entertainment/urls.py @@ -1,10 +1,11 @@ from django.urls import path from . import views -from .views import PlaceListAPIView, RatingsAPIView +from .views import PlaceListAPIView, RelatedCommentAPIView urlpatterns = [ path("ping", views.ping, name="index"), path("places", PlaceListAPIView.as_view({"get": "list", "post": "create"}), name="place-list"), path("places//ratings", PlaceListAPIView.as_view({"post": "create_rating"}), name="comment-list"), + path("comments//releted-comments", RelatedCommentAPIView.as_view({"post": "create_related_comment"}), name="related-comments") ] \ No newline at end of file diff --git a/entertainment/views.py b/entertainment/views.py index 0d03db7..7f16766 100644 --- a/entertainment/views.py +++ b/entertainment/views.py @@ -8,7 +8,7 @@ from django.contrib.gis.geos import Point from entertainment.models import Place, Rate, Comment -from entertainment.serializers import PlaceSerializer, RateSerializer +from entertainment.serializers import PlaceSerializer, RateSerializer, CommentSerializer @api_view(['GET']) @@ -37,7 +37,6 @@ def filter_queryset(self, queryset): @action(detail=True, methods=['post']) def create_rating(self, request, pk=None): - breakpoint() place = self.get_object() user = self.request.user serializer = RateSerializer(data=request.data) @@ -48,7 +47,7 @@ def create_rating(self, request, pk=None): comment_data = serializer.validated_data.pop('comment', None) if comment_data: - comment = Comment.objects.create(text=comment_data["text"]) + comment = Comment.objects.create(text=comment_data["text"], user=user) else: comment = None rate = Rate(**serializer.validated_data, place=place, user=user, comment=comment) @@ -56,13 +55,26 @@ def create_rating(self, request, pk=None): return Response({"message": "Rate was created"}, status=status.HTTP_201_CREATED) -class RatingsAPIView(generics.CreateAPIView): +class RelatedCommentAPIView(mixins.CreateModelMixin, viewsets.GenericViewSet): permission_classes = [IsAuthenticated, ] - queryset = Rate.objects.all() + queryset = Comment.objects.all() serializer_class = RateSerializer - def perform_create(self, serializer): - serializer.save(user=self.request.user) + @action(detail=True, methods=['post']) + def create_related_comment(self, request, pk=None): + comment = self.get_object() + user = self.request.user + serializer = CommentSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + related_comment = Comment.objects.create(text=serializer.validated_data["text"], user=user) + comment.related_comments.add(related_comment) + return Response({"id": related_comment.id, + "created_at": related_comment.created_at, + "text": serializer.validated_data["text"], + "username": user.username}, + status=status.HTTP_201_CREATED) + + From 3b1a8425398d6ce4845e2461c23c49893e74015e Mon Sep 17 00:00:00 2001 From: andreyPromaster Date: Tue, 8 Mar 2022 15:47:18 +0300 Subject: [PATCH 6/8] Set media url --- rating/settings.py | 5 ++++- rating/urls.py | 9 ++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/rating/settings.py b/rating/settings.py index 8482b0b..c977b1e 100644 --- a/rating/settings.py +++ b/rating/settings.py @@ -147,4 +147,7 @@ SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(days=365), 'REFRESH_TOKEN_LIFETIME': timedelta(days=366), -} \ No newline at end of file +} + +MEDIA_URL = '/media/' +MEDIA_ROOT = BASE_DIR / 'media' \ No newline at end of file diff --git a/rating/urls.py b/rating/urls.py index da8ab58..263e556 100644 --- a/rating/urls.py +++ b/rating/urls.py @@ -15,9 +15,16 @@ """ from django.contrib import admin from django.urls import path, include +from django.conf.urls.static import static + +from rating import settings + urlpatterns = [ path('admin/', admin.site.urls), path('api/', include('entertainment.urls')), path('api/', include('user.urls')), -] \ No newline at end of file +] + +if settings.DEBUG: + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) From d2aa0365e724419123e20527e64679cdbac83e13 Mon Sep 17 00:00:00 2001 From: andreyPromaster Date: Wed, 9 Mar 2022 15:15:07 +0300 Subject: [PATCH 7/8] Add image like base64 --- entertainment/serializers.py | 2 ++ entertainment/views.py | 5 ----- poetry.lock | 21 ++++++++++++++++++++- pyproject.toml | 1 + 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/entertainment/serializers.py b/entertainment/serializers.py index e1633be..16ceec8 100644 --- a/entertainment/serializers.py +++ b/entertainment/serializers.py @@ -1,4 +1,5 @@ from django.contrib.gis.geos import Point +from drf_extra_fields.fields import Base64ImageField from rest_framework import serializers from entertainment.models import Place, Rate, Comment @@ -37,6 +38,7 @@ class PlaceSerializer(serializers.ModelSerializer): ratings = RateSerializer(many=True, read_only=True) longitude = serializers.CharField(source="location.x") latitude = serializers.CharField(source="location.y") + image = Base64ImageField(required=False) class Meta: model = Place diff --git a/entertainment/views.py b/entertainment/views.py index 7f16766..45e817f 100644 --- a/entertainment/views.py +++ b/entertainment/views.py @@ -20,11 +20,6 @@ class PlaceListAPIView(mixins.CreateModelMixin, mixins.ListModelMixin, viewsets. permission_classes = [IsAuthenticated, ] queryset = Place.objects.all() serializer_class = PlaceSerializer - parser_classes = (MultiPartParser, FormParser, JSONParser) - - def perform_create(self, serializer): - photo = self.request.data['photo'] - serializer.save(image=photo) def filter_queryset(self, queryset): if ("longitude" in self.request.query_params diff --git a/poetry.lock b/poetry.lock index f9af7ed..c785bd0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -81,6 +81,21 @@ lint = ["flake8", "pep8", "isort"] python-jose = ["python-jose (==3.0.0)"] test = ["cryptography", "pytest-cov", "pytest-django", "pytest-xdist", "pytest", "tox"] +[[package]] +name = "drf-extra-fields" +version = "3.4.0" +description = "Additional fields for Django Rest Framework." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +Django = ">=2.2" +djangorestframework = ">=3.9.2" + +[package.extras] +base64imagefield = ["Pillow (>=6.2.1)"] + [[package]] name = "pillow" version = "9.0.1" @@ -138,7 +153,7 @@ python-versions = ">=2" [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "cf54f06e3cc70fc7cf6f80e7a67a76e8fd7573a4e484c7695249c13d7732e3f5" +content-hash = "760db677810e45fbb15c685eaef06901e4f820d864809e0edb91521c50688ccc" [metadata.files] asgiref = [ @@ -179,6 +194,10 @@ djangorestframework-simplejwt = [ {file = "djangorestframework_simplejwt-5.0.0-py3-none-any.whl", hash = "sha256:ddcbeef51155d1e71410dde44b581c7e04cfb74776f5337661ac3ef4c0c367e6"}, {file = "djangorestframework_simplejwt-5.0.0.tar.gz", hash = "sha256:30b10e7732395c44d21980f773214d2b9bdeadf2a6c6809cd1a7c9abe272873c"}, ] +drf-extra-fields = [ + {file = "drf-extra-fields-3.4.0.tar.gz", hash = "sha256:6224ae29ddfe80f38e36a1a5f7f1837cec1a4ec90e8960b473d43167f6d427d0"}, + {file = "drf_extra_fields-3.4.0-py3-none-any.whl", hash = "sha256:cc4e35e8b0fbef92a4295dfa5adac1436c0d51402ecc4facadb406e45e24f3bb"}, +] pillow = [ {file = "Pillow-9.0.1-1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a5d24e1d674dd9d72c66ad3ea9131322819ff86250b30dc5821cbafcfa0b96b4"}, {file = "Pillow-9.0.1-1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2632d0f846b7c7600edf53c48f8f9f1e13e62f66a6dbc15191029d950bfed976"}, diff --git a/pyproject.toml b/pyproject.toml index 4121649..ff6d4b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ django-filter = "^21.1" psycopg2-binary = "^2.9.3" djangorestframework-simplejwt = "^5.0.0" Pillow = "^9.0.1" +drf-extra-fields = "^3.4.0" [tool.poetry.dev-dependencies] From 35ff5237c5dd7786e45f557413ebcf0740bed832 Mon Sep 17 00:00:00 2001 From: andreyPromaster Date: Thu, 10 Mar 2022 13:08:12 +0300 Subject: [PATCH 8/8] Change type place --- .../migrations/0012_alter_place_type.py | 18 ++++++++++++++++++ entertainment/models.py | 19 ++----------------- 2 files changed, 20 insertions(+), 17 deletions(-) create mode 100644 entertainment/migrations/0012_alter_place_type.py diff --git a/entertainment/migrations/0012_alter_place_type.py b/entertainment/migrations/0012_alter_place_type.py new file mode 100644 index 0000000..11b312a --- /dev/null +++ b/entertainment/migrations/0012_alter_place_type.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.2 on 2022-03-10 09:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('entertainment', '0011_alter_comment_options'), + ] + + operations = [ + migrations.AlterField( + model_name='place', + name='type', + field=models.CharField(default='shop', max_length=200), + ), + ] diff --git a/entertainment/models.py b/entertainment/models.py index c71f45e..8b4c38b 100644 --- a/entertainment/models.py +++ b/entertainment/models.py @@ -16,24 +16,9 @@ class Place(models.Model): location = models.PointField() address = models.CharField(max_length=255) image = models.ImageField(null=True, blank=True) - CAFE = 'cafe' - RESTAURANT = 'restaurant' - SPORT = 'sport' - TECH = 'tech' - SHOP = 'shop' - STUDY = 'study' - TYPES_OF_PLACE = [ - (CAFE, 'Cafe'), - (RESTAURANT, 'Restaurant'), - (SPORT, 'Sport'), - (TECH, 'Tech'), - (SHOP, 'Shop'), - (STUDY, 'Study'), - ] type = models.CharField( - max_length=20, - choices=TYPES_OF_PLACE, - default=CAFE, + max_length=200, + default="shop", )