-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
DominikDrabik
committed
Aug 26, 2024
1 parent
33248d1
commit 7cd0f91
Showing
13 changed files
with
237 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,6 +42,7 @@ | |
'rest_framework.authtoken', | ||
'drf_spectacular', | ||
'user', | ||
'recipe', | ||
] | ||
|
||
MIDDLEWARE = [ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Generated by Django 3.2.25 on 2024-08-24 15:09 | ||
|
||
from django.conf import settings | ||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('core', '0001_initial'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='Recipe', | ||
fields=[ | ||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('title', models.CharField(max_length=255)), | ||
('description', models.TextField(blank=True)), | ||
('time_minutes', models.IntegerField()), | ||
('price', models.DecimalField(decimal_places=2, max_digits=5)), | ||
('link', models.CharField(blank=True, max_length=255)), | ||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), | ||
], | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,13 @@ | ||
""" | ||
Test for models | ||
""" | ||
from decimal import Decimal | ||
|
||
from django.test import TestCase | ||
from django.contrib.auth import get_user_model | ||
|
||
from core import models | ||
|
||
class ModelTests(TestCase): | ||
"""Test for models""" | ||
|
||
|
@@ -42,4 +45,20 @@ def test_create_new_superuser(self): | |
"""Test creating a new superuser""" | ||
user = get_user_model().objects.create_superuser('[email protected]', 'test123') | ||
self.assertTrue(user.is_superuser) | ||
self.assertTrue(user.is_staff) | ||
self.assertTrue(user.is_staff) | ||
|
||
def test_create_recipe(self): | ||
"""Test creating a new recipe""" | ||
user = get_user_model().objects.create_user( | ||
'[email protected]', | ||
'test123') | ||
recipe = models.Recipe.objects.create( | ||
user=user, | ||
title='Sample Recipe', | ||
time_minutes=5, | ||
price=Decimal( | ||
'5.00'), | ||
description='Sample Description' | ||
) | ||
self.assertEqual(str(recipe), recipe.title) | ||
|
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class RecipeConfig(AppConfig): | ||
default_auto_field = 'django.db.models.BigAutoField' | ||
name = 'recipe' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
""" | ||
Serialiers for recipe APIs | ||
""" | ||
|
||
from rest_framework import serializers | ||
from core.models import Recipe | ||
|
||
class RecipeSerializer(serializers.ModelSerializer): | ||
"""Serializer for recipe objects""" | ||
|
||
# Here it tells django we want to use the model Recipe and the fields we want to use | ||
class Meta: | ||
model = Recipe | ||
fields = ('id', 'title', 'time_minutes', 'price', 'link') | ||
read_only_fields = ('id',) | ||
|
||
class RecipeDetailSerializer(RecipeSerializer): | ||
"""Serializer for recipe detail objects""" | ||
# Here we are extending the RecipeSerializer and adding the extra fields | ||
class Meta(RecipeSerializer.Meta): | ||
fields = RecipeSerializer.Meta.fields + ('ingredients', 'tags') | ||
read_only_fields = ('id',) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
""" | ||
Tests for recipe APIs. | ||
""" | ||
|
||
from decimal import Decimal | ||
|
||
from django.contrib.auth import get_user_model | ||
from django.test import TestCase | ||
from django.urls import reverse | ||
|
||
from rest_framework import status | ||
from rest_framework.test import APIClient | ||
|
||
from core.models import Recipe | ||
|
||
from recipe.serializers import RecipeSerializer, RecipeDetailSerializer | ||
|
||
RECIPE_URL = reverse('recipe:recipe-list') | ||
|
||
def detail_url(recipe_id): | ||
"""Return recipe detail URL""" | ||
return reverse('recipe:recipe-detail', args=[recipe_id]) | ||
|
||
def create_recipe(user, **params): | ||
"""Create and return a sample recipe""" | ||
defaults = { | ||
'title': 'Sample Recipe', | ||
'time_minutes': 10, | ||
'price': Decimal('5.00'), | ||
'description': 'Sample description', | ||
'link': 'https://sample.com/recipe' | ||
} | ||
defaults.update(params) | ||
|
||
return Recipe.objects.create(user=user, **defaults) | ||
|
||
|
||
class PublicRecipeAPITests(TestCase): | ||
"""Test unauthenticated recipe API access""" | ||
|
||
def setUp(self): | ||
self.client = APIClient() | ||
|
||
def test_auth_required(self): | ||
"""Test that authentication is required""" | ||
res = self.client.get(reverse('recipe:recipe-list')) | ||
self.assertEqual(res.status_code, status.HTTP_401_UNAUTHORIZED) | ||
|
||
|
||
class PrivateRecipeApiTests(TestCase): | ||
"""Test authenticated recipe API access""" | ||
|
||
def setUp(self): | ||
self.client = APIClient() | ||
self.user = get_user_model().objects.create_user( | ||
'[email protected]', | ||
'testpass') | ||
self.client.force_authenticate(self.user) | ||
|
||
def test_retrieve_recipes(self): | ||
"""Test retrieving a list of recipes""" | ||
create_recipe(user=self.user) | ||
create_recipe(user=self.user) | ||
|
||
res = self.client.get(RECIPE_URL) | ||
|
||
recipes = Recipe.objects.all().order_by('-id') | ||
serializer = RecipeSerializer(recipes, many=True) | ||
self.assertEqual(res.status_code, status.HTTP_200_OK) | ||
self.assertEqual(res.data, serializer.data) | ||
|
||
def test_recipes_limited_to_user(self): | ||
"""Test retrieving recipes for user""" | ||
other_user = get_user_model().objects.create_user( | ||
'[email protected]', | ||
'password123') | ||
create_recipe(user=other_user) | ||
recipe = create_recipe(user=self.user) | ||
|
||
res = self.client.get(RECIPE_URL) | ||
|
||
recipes = Recipe.objects.filter(user=self.user) | ||
serializer = RecipeSerializer(recipes, many=True) | ||
self.assertEqual(res.status_code, status.HTTP_200_OK) | ||
self.assertEqual(res.data, serializer.data) | ||
|
||
def test_get_recipe_detail(self): | ||
"""Test viewing a recipe detail""" | ||
recipe = create_recipe(user=self.user) | ||
|
||
url = detail_url(recipe.id) | ||
res = self.client.get(url) | ||
|
||
serializer = RecipeDetailSerializer(recipe) | ||
self.assertEqual(res.data, serializer.data) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
""" | ||
URL mapping for the recipe app is defined in app/recipe/urls.py. The URL mapping is then imported into the main project urls.py file. | ||
""" | ||
|
||
from django.urls import path, include | ||
from rest_framework.routers import DefaultRouter | ||
from recipe import views | ||
|
||
router = DefaultRouter() | ||
router.register('recipes', views.RecipeViewSet) | ||
|
||
app_name = 'recipe' | ||
|
||
urlpatterns = [ | ||
path('', include(router.urls)) | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
""" | ||
Views for the recipe APIs | ||
""" | ||
|
||
from rest_framework import viewsets | ||
from rest_framework.authentication import TokenAuthentication | ||
from rest_framework.permissions import IsAuthenticated | ||
|
||
|
||
from core.models import Recipe | ||
from recipe import serializers | ||
|
||
class RecipeViewSet(viewsets.ModelViewSet): | ||
"""Manage recipes in the database""" | ||
serializer_class = serializers.RecipeDetailSerializer | ||
queryset = Recipe.objects.all() | ||
authentication_classes = [TokenAuthentication] | ||
permission_classes = [IsAuthenticated] | ||
|
||
def get_queryset(self): | ||
"""Retrieve the recipes for the authenticated user""" | ||
return self.queryset.filter(user=self.request.user).order_by('-id') | ||
|
||
def get_serializer_class(self): | ||
"""Return appropriate serializer class""" | ||
if self.action == 'list': | ||
return serializers.RecipeSerializer | ||
|
||
return self.serializer_class |