Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/api products catalogue #65

Merged
merged 22 commits into from
Jan 13, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6375bbb
env: add django-filter
knop-k Dec 18, 2023
1dd9614
feat: add django-filters to instaled_apps
knop-k Dec 18, 2023
54ff4f3
test: add tests for products_catalogue api
knop-k Dec 18, 2023
4f06486
feat: settings for pagination and filters
knop-k Dec 18, 2023
6a979f4
feat: add urls for products_catalagoue
knop-k Dec 18, 2023
9b557fa
feat: add api products to main urls
knop-k Dec 18, 2023
f5da487
feat: create serializers for product
knop-k Dec 18, 2023
6fe3ae1
feat: add filter for product
knop-k Dec 18, 2023
4d35211
feat: add ProductViewSet
knop-k Dec 18, 2023
d838339
refactor: fix pylama error
knop-k Dec 19, 2023
84b0232
Merge branch 'dev' into feature/api_products_catalogue
knop-k Dec 20, 2023
b4a8614
Merge branch 'dev' into feature/api_products_catalogue
knop-k Dec 29, 2023
ad8496f
fix: poetry lock
knop-k Dec 29, 2023
e90c067
feat: add read only fields for 'id' in serializers
knop-k Dec 29, 2023
1f1586f
test: add active and inactive fixture of products
knop-k Dec 29, 2023
eb78938
refactor: replace create_product fixture to create_active_product fix…
knop-k Dec 29, 2023
2010e6d
test: extend test_access_protected_resource to add two products: acti…
knop-k Dec 29, 2023
c123e72
refactor: create authenticated_api_client fixture
knop-k Dec 29, 2023
c5bd5a4
refactor: remove comment
knop-k Dec 29, 2023
b0ecc7b
feat: add is_active in filters
knop-k Dec 29, 2023
b0efde6
refactor: update in test_access_protected_resource
knop-k Jan 6, 2024
6cb032f
refactor: update in test_access_protected_resource - update assert_ac…
knop-k Jan 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Dshop/Dshop/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
'rest_framework',
'rest_framework.authtoken',
'drf_spectacular',
'django_filters',
] + PROJECT_APPS

MIDDLEWARE = [
Expand Down Expand Up @@ -207,6 +208,10 @@
},
"TEST_REQUEST_DEFAULT_FORMAT": "json",
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
"PAGE_SIZE": 25,
"DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"]

}

# drf_spectacular
Expand Down
1 change: 1 addition & 0 deletions Dshop/Dshop/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
path('tinymce/', include('tinymce.urls')),

path('api/users/', include('apps.users.api_urls')),
path('api/products/', include('apps.products_catalogue.api_urls')),

path("api/schema/", SpectacularAPIView.as_view(), name="api-schema"),
path(
Expand Down
10 changes: 10 additions & 0 deletions Dshop/apps/products_catalogue/api_urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.urls import path, include
from .api_views import ProductViewSet
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be a good idea to remove the trailing slashes. My front-end friend recently pointed out that it was easier for them that way.
router = DefaultRouter(trailing_slash=False)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont have preference @knop-k. Your call.

router.register('', ProductViewSet, basename='products-api')

urlpatterns = [
path('', include(router.urls)),
]
12 changes: 12 additions & 0 deletions Dshop/apps/products_catalogue/api_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from .models import Product
from .serializers import ProductSerializer
from .filters import ProductFilter
from rest_framework import viewsets
from django_filters.rest_framework import DjangoFilterBackend
Comment on lines +1 to +5
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP8 import order rules not followed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not followed in the whole project. Good pointing out, we need to add something to guard id. Right now, let's ignore.



class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.filter(is_active=True, parent_product=None)
serializer_class = ProductSerializer
filter_backends = [DjangoFilterBackend]
filterset_class = ProductFilter
10 changes: 10 additions & 0 deletions Dshop/apps/products_catalogue/filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django_filters import rest_framework as filters
from .models import Product
Comment on lines +1 to +2
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP8 import order rules not followed



class ProductFilter(filters.FilterSet):
name = filters.CharFilter(field_name='name', lookup_expr='icontains')
jacoor marked this conversation as resolved.
Show resolved Hide resolved

class Meta:
model = Product
fields = ['name']
9 changes: 9 additions & 0 deletions Dshop/apps/products_catalogue/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from rest_framework import serializers
from .models import Product


class ProductSerializer(serializers.ModelSerializer):

class Meta:
model = Product
fields = ['id', 'category', 'name', 'price', 'short_description', 'full_description', 'parent_product']
Comment on lines +7 to +9
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a suggestion: you can add read_only_fields to Meta (with e.g. id field)

85 changes: 85 additions & 0 deletions Dshop/apps/products_catalogue/tests/test_api_products.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import pytest
from django.urls import reverse
from apps.products_catalogue.models import Category, Product
# pylama:ignore=W0404, W0611
from apps.users.conftest import api_client, login_url, login_data, user_instance, user_instance_token
Comment on lines +1 to +5
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP8 import order rules not followed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not followed in the whole project. Good pointing out, we need to add something to guard id. Right now, let's ignore.



@pytest.fixture
def create_category():
return Category.objects.create(name='Test Category', is_active=True)


@pytest.fixture
def create_product():
category = Category.objects.create(name='Test Category', is_active=True)
return Product.objects.create(
name="main product",
category=category,
price=11,
short_description="short desc",
full_description="full_description"
)


@pytest.mark.django_db
def test_access_protected_resource(api_client, user_instance_token):
jacoor marked this conversation as resolved.
Show resolved Hide resolved
api_client.credentials(HTTP_AUTHORIZATION=f'Token {user_instance_token.key}')
jacoor marked this conversation as resolved.
Show resolved Hide resolved

url = reverse('products-api-list')
response = api_client.get(url)
assert response.status_code == 200


def test_access_protected_resource_without_authentication(api_client):
url = reverse('products-api-list')
response = api_client.get(url)

assert response.status_code == 401


@pytest.mark.django_db
def test_product_detail(api_client, user_instance_token, create_product):
api_client.credentials(HTTP_AUTHORIZATION=f'Token {user_instance_token.key}')

url = reverse('products-api-detail', kwargs={'pk': create_product.id})
response = api_client.get(url)

assert response.status_code == 200
assert response.data['id'] == create_product.id
assert response.data['name'] == "main product"
assert response.data['price'] == "11.00"
assert response.data['short_description'] == "short desc"
assert response.data['full_description'] == "full_description"


@pytest.mark.django_db
def test_create_product(api_client, user_instance_token, create_category):
api_client.credentials(HTTP_AUTHORIZATION=f'Token {user_instance_token.key}')

url = reverse('products-api-list')
data = {
'category': create_category.id, # replace with an existing category ID
jacoor marked this conversation as resolved.
Show resolved Hide resolved
'name': 'Test Product',
'price': '19.99',
'short_description': 'Test short description',
'full_description': 'Test full description',
}
response = api_client.post(url, data, format='json')

assert response.status_code == 201
assert response.data['name'] == "Test Product"
assert response.data['price'] == "19.99"


@pytest.mark.django_db
def test_update_product(api_client, user_instance_token, create_product):
api_client.credentials(HTTP_AUTHORIZATION=f'Token {user_instance_token.key}')

url = reverse('products-api-detail', kwargs={'pk': create_product.id})
data = {'name': 'Updated product name'}
response = api_client.patch(url, data, format='json')

assert response.status_code == 200
assert response.data['name'] == "Updated product name"
assert response.data['short_description'] == "short desc"
28 changes: 16 additions & 12 deletions Dshop/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Dshop/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ django-tinymce = "^3.6.1"
dj-shop-cart = "^7.0.3"
djangorestframework="3.14.0"
drf-spectacular="0.26.5"
django-filter = "^23.5"

[tool.poetry.group.dev.dependencies]
pytest = "^7.4.0"
Expand Down