added marketplace endpoints, permissions

This commit is contained in:
Alexander Karpov 2022-10-08 11:21:53 +03:00
parent 50a6c9d39c
commit 4e4735d7e2
18 changed files with 201 additions and 31 deletions

0
app/common/__init__.py Normal file
View File

23
app/common/permissions.py Normal file
View File

@ -0,0 +1,23 @@
from rest_framework import permissions
class IsManager(permissions.BasePermission):
"""
Checks if request user is an admin or hr
"""
def has_permission(self, request, view):
if request.method in permissions.SAFE_METHODS:
return True
return request.user.is_manager
class IsAdmin(permissions.BasePermission):
"""
Checks if request user is an admin or hr
"""
def has_permission(self, request, view):
if request.method in permissions.SAFE_METHODS:
return True
return request.user.is_admin

View File

@ -1,6 +1,8 @@
from django.urls import path, include from django.urls import path, include
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
from marketplace.api.views import ListCreateProductApi, RetireUpdateDestroyProductApi
urlpatterns = [ urlpatterns = [
path( path(
"auth/", "auth/",
@ -12,9 +14,19 @@ urlpatterns = [
), ),
), ),
path( path(
"user/", "marketplace/",
include( include(
[ [
path(
"product/",
ListCreateProductApi.as_view(),
name="list_create_product",
),
path(
"product/<str:slug>",
RetireUpdateDestroyProductApi.as_view(),
name="get_update_destroy_product",
),
] ]
), ),
), ),

View File

@ -62,7 +62,7 @@ DJANGO_APPS = [
] ]
THIRD_PARTY_APPS = ["rest_framework", "corsheaders", "django_celery_beat", "drf_yasg"] THIRD_PARTY_APPS = ["rest_framework", "corsheaders", "django_celery_beat", "drf_yasg"]
LOCAL_APPS = ["users"] LOCAL_APPS = ["users", "marketplace", "events"]
# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps # https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS

View File

View File

@ -0,0 +1,30 @@
from rest_framework import serializers
from marketplace.models import Product
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = [
"name",
"slug",
"description",
"image",
"image_cropped",
"nft",
"price",
"creator",
]
extra_kwargs = {
"image": {"write_only": True},
"nft": {"read_only": True},
"slug": {"read_only": True},
"image_cropped": {"read_only": True},
"creator": {"read_only": True},
}
def create(self, validated_data):
return Product.objects.create(
**validated_data, creator=self.context["request"].user
)

View File

@ -0,0 +1,29 @@
from rest_framework import generics
from rest_framework.generics import get_object_or_404
from rest_framework.permissions import IsAuthenticated
from rest_framework.parsers import FormParser, MultiPartParser
from marketplace.api.serializers import ProductSerializer
from marketplace.models import Product
from common.permissions import IsManager
class ListCreateProductApi(generics.ListCreateAPIView):
serializer_class = ProductSerializer
parser_classes = [FormParser, MultiPartParser]
permission_classes = [IsAuthenticated, IsManager]
queryset = Product.objects.all()
class RetireUpdateDestroyProductApi(generics.RetrieveUpdateDestroyAPIView):
serializer_class = ProductSerializer
parser_classes = [FormParser, MultiPartParser]
permission_classes = [IsAuthenticated, IsManager]
queryset = Product.objects.all()
def get_object(self):
block = get_object_or_404(
Product,
slug=self.request.parser_context["kwargs"]["slug"],
)
return block

View File

@ -4,3 +4,6 @@ from django.apps import AppConfig
class MarketplaceConfig(AppConfig): class MarketplaceConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField' default_auto_field = 'django.db.models.BigAutoField'
name = 'marketplace' name = 'marketplace'
def ready(self):
import marketplace.signals

View File

@ -0,0 +1,30 @@
# Generated by Django 4.0.8 on 2022-10-08 06:31
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Product',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('description', models.TextField()),
('image', models.ImageField(upload_to='uploads/')),
('image_cropped', models.ImageField(blank=True, upload_to='cropped/')),
('nft', models.CharField(blank=True, max_length=500)),
('price', models.IntegerField()),
('creator', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='products', to=settings.AUTH_USER_MODEL)),
],
),
]

View File

@ -1,3 +1,19 @@
from django.db import models from django.db import models
# Create your models here. from users.models import User
class Product(models.Model):
name = models.CharField(max_length=200)
slug = models.SlugField(max_length=20)
description = models.TextField(blank=True)
image = models.ImageField(upload_to="uploads/")
image_cropped = models.ImageField(upload_to="cropped/", blank=True)
nft = models.CharField(max_length=500, blank=True)
price = models.IntegerField()
creator = models.ForeignKey(User, related_name="products", on_delete=models.CASCADE)
def __str__(self):
return self.name

View File

@ -0,0 +1,25 @@
from django.core.files import File
from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver
from utils.file import crop_image
from utils.generators import generate_charset
from .models import Product
@receiver(pre_save, sender=Product)
def create_product(sender, instance, **kwargs):
slug = generate_charset(5)
while Product.objects.filter(slug=slug).exists():
slug = generate_charset(5)
instance.slug = slug
@receiver(post_save, sender=Product)
def process_product(sender, instance, created, **kwargs):
if instance.image and kwargs["update_fields"] != frozenset({"image_cropped"}):
instance.image_cropped = File(
crop_image(instance.image.path, cut_to=(250, 250)),
name=instance.image.path.split(".")[0].split("/")[-1] + ".png",
)
instance.save(update_fields=["image_cropped"])

View File

@ -1,3 +0,0 @@
from django.shortcuts import render
# Create your views here.

View File

@ -4,7 +4,7 @@ Django==4.0.8
django-cors-headers==3.13.0 django-cors-headers==3.13.0
django-environ==0.9.0 django-environ==0.9.0
"drf-yasg[validation]" "drf-yasg[validation]"
Pillow==9.2.0
redis==4.3.4 redis==4.3.4
psycopg2-binary==2.9.4 psycopg2-binary==2.9.4

View File

@ -1,23 +0,0 @@
# Generated by Django 4.0.8 on 2022-10-07 21:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='user',
name='salary',
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name='user',
name='type',
field=models.CharField(choices=[('WORKER', 'worker'), ('HR', 'human resources'), ('ADMIN', 'administrator')], default='WORKER', max_length=6),
),
]

View File

@ -28,8 +28,12 @@ class User(AbstractUser):
super(AbstractUser, self).save(*args, **kwargs) super(AbstractUser, self).save(*args, **kwargs)
@property @property
def can_create_events(self): def is_manager(self):
return self.type in [self.WorkerType.HR, self.WorkerType.ADMIN] return self.type in [self.WorkerType.HR, self.WorkerType.ADMIN]
@property
def is_admin(self):
return self.type == self.WorkerType.ADMIN
class Meta: class Meta:
ordering = ["-id"] ordering = ["-id"]

0
app/utils/__init__.py Normal file
View File

16
app/utils/file.py Normal file
View File

@ -0,0 +1,16 @@
from PIL import Image
from io import BytesIO
def crop_image(image_path: str, cut_to=(500, 500)):
"""Makes image's thumbnail bt given parameters. By default, crops to 500x500"""
img = Image.open(image_path)
blob = BytesIO()
try:
img.thumbnail(cut_to, Image.ANTIALIAS)
except IOError:
print("Can't crop")
img.save(blob, "PNG")
return blob

8
app/utils/generators.py Normal file
View File

@ -0,0 +1,8 @@
import string
import secrets
def generate_charset(length: int):
return "".join(
secrets.choice(string.digits + string.ascii_letters) for _ in range(length)
)