DannyJamesHidalgo / client-pitty-party

0 stars 0 forks source link

Views for dogs #7

Closed DannyJamesHidalgo closed 2 months ago

DannyJamesHidalgo commented 3 months ago

Getting All Rock Types

You will be creating a View in this chapter. In Django, the View handles the request from the client, does the work to get the requested data, and sends a response back to the client.

Type View

Inside the views folder create a file named type_view.py. Here’s the view skeleton with the imports and methods we’ll add to next:

rockproject/rockapi/views/type_view.py

"""View module for handling requests for type data"""
from django.http import HttpResponseServerError
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
from rest_framework import serializers, status
from rockapi.models import Type

class TypeView(ViewSet):
    """Rock API types view"""

    def list(self, request):
        """Handle GET requests to get all types

        Returns:
            Response -- JSON serialized list of types
        """

        types = Type.objects.all()
        serialized = TypeSerializer(types, many=True)
        return Response(serialized.data, status=status.HTTP_200_OK)

    def retrieve(self, request, pk=None):
        """Handle GET requests for single type

        Returns:
            Response -- JSON serialized type record
        """

        rock_type = Type.objects.get(pk=pk)
        serialized = TypeSerializer(rock_type)
        return Response(serialized.data, status=status.HTTP_200_OK)

class TypeSerializer(serializers.ModelSerializer):
    """JSON serializer for types"""
    class Meta:
        model = Type
        fields = ('id', 'label', )

Add View to Package

rockproject/rockapi/views/__init__.py

from .type_view import TypeView

Adding the URL

So far we’ve set up the view and serializer but not which URL to use for the view. We need to add /types to be supported by the API.

If a client sends a GET request to either http://localhost:8000/types or http://localhost:8000/types/1, we want the server to respond with the appropriate method.

You will use a built-in class in Django REST called the DefaultRouter. The DefaultRouter sets up the resource for each method that is present on the view.

rockproject/rockproject/urls.py

Add the following import statements at the top of the urls.py module.

from django.conf.urls import include
from rest_framework import routers
from rockapi.views import TypeView

In the same file, above the current urlpatterns variable, add the following:

router = routers.DefaultRouter(trailing_slash=False)
router.register(r'types', TypeView, 'type')

The trailing_slash=False tells the router to accept /types instead of /types/. It’s a very annoying error to come across, when your server is not responding and the code looks right, the only issue is your fetch url is missing a / at the end.

The next line is what sets up the /types resource. The first parameter, r'types, is setting up the URL. The second TypeView is telling the server which view to use when it sees that url.

The third, type, is called the base name. You’ll only see the base name if you get an error in the server. It acts as a nickname for the resource and is usually the singular version of the URL.

Video Walkthrough

Here is a 5:55 minute walkthrough of implementing the code with explanations.

Test Your View

Try it out in your API client!

Do not test this functionality in the React client until after the fixtures chapter is done

  1. Open the authtoken_token table in your database and examine the pk key on each object. These are unique tokens assigned to every user in the system.
  2. Copy any one of them to use in your API client.
  3. Start a new GET request.
  4. Add a new header to the request named Authorization
  5. Grab a token from your authtoken_tokens table.
  6. The value will be the word Token, followed by a space, and then the unique token you copied
  7. Send a GET request to http://localhost:8000/types and you should get an empty JSON array in the response.
DannyJamesHidalgo commented 3 months ago

you will also need this in the future for the admin side of the tickent # Creation of Rocks

The client application already has a form component for you. What you need to implement in the API is to capture the request from the client and insert the data into the database using the Django ORM magic.

Make sure that you import the Type model into the rock view module.

Expand to get your API create code ```py def create(self, request): """Handle POST requests for rocks Returns: Response: JSON serialized representation of newly created rock """ # Get an object instance of a rock type chosen_type = Type.objects.get(pk=request.data['typeId']) # Create a rock object and assign it property values rock = Rock() rock.user = request.auth.user rock.weight = request.data['weight'] rock.name = request.data['name'] rock.type = chosen_type rock.save() serialized = RockSerializer(rock, many=False) return Response(serialized.data, status=status.HTTP_201_CREATED) ```

Full Stack Work

Once you have implemented the create method in the API for rocks, go to the rock form in the React browser client. Fill out the form and see if a new rock is added to the database.

Debugging the Client

If you've made it this far, you have noticed that there is something wrong with the client code. Submitting the rock form isn't doing what you expect.

DannyJamesHidalgo commented 2 months ago
"""View module for handling requests about products"""

from rest_framework.decorators import action
from bangazonapi.models.recommendation import Recommendation
import base64
from django.core.files.base import ContentFile
from django.http import HttpResponseServerError
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework import status
from bangazonapi.models import Product, Customer, ProductCategory, Store, LikedProduct
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from rest_framework.parsers import MultiPartParser, FormParser

class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = ProductCategory
        fields = ['id', 'name']

class StoreSerializer(serializers.ModelSerializer):
    class Meta:
        model = Store
        fields = ['id', 'name']

class ProductSerializer(serializers.ModelSerializer):
    """JSON serializer for products"""
    category = CategorySerializer()
    store = StoreSerializer()

    class Meta:
        model = Product
        fields = (
            "id",
            "name",
            "price",
            "number_sold",
            "description",
            "quantity",
            "created_date",
            "location",
            "image_path",
            "average_rating",
            "can_be_rated",
            "category",
            "store"
        )
        depth = 1

class Products(ViewSet):
    """Request handlers for Products in the Bangazon Platform"""

    permission_classes = (IsAuthenticatedOrReadOnly,)

    def create(self, request):
        """
        @api {POST} /products POST new product
        @apiName CreateProduct
        @apiGroup Product

        @apiHeader {String} Authorization Auth token
        @apiHeaderExample {String} Authorization
            Token 9ba45f09651c5b0c404f37a2d2572c026c146611

        @apiParam {String} name Short form name of product
        @apiParam {Number} price Cost of product
        @apiParam {String} description Long form description of product
        @apiParam {Number} quantity Number of items to sell
        @apiParam {String} location City where product is located
        @apiParam {Number} category_id Category of product
        @apiParamExample {json} Input
            {
                "name": "Kite",
                "price": 14.99,
                "description": "It flies high",
                "quantity": 60,
                "location": "Pittsburgh",
                "category_id": 4
            }

        @apiSuccess (200) {Object} product Created product
        @apiSuccess (200) {id} product.id Product Id
        @apiSuccess (200) {String} product.name Short form name of product
        @apiSuccess (200) {String} product.description Long form description of product
        @apiSuccess (200) {Number} product.price Cost of product
        @apiSuccess (200) {Number} product.quantity Number of items to sell
        @apiSuccess (200) {Date} product.created_date City where product is located
        @apiSuccess (200) {String} product.location City where product is located
        @apiSuccess (200) {String} product.image_path Path to product image
        @apiSuccess (200) {Number} product.average_rating Average customer rating of product
        @apiSuccess (200) {Number} product.number_sold How many items have been purchased
        @apiSuccess (200) {Object} product.category Category of product
        @apiSuccessExample {json} Success
            {
                "id": 101,
                "url": "http://localhost:8000/products/101",
                "name": "Kite",
                "price": 14.99,
                "number_sold": 0,
                "description": "It flies high",
                "quantity": 60,
                "created_date": "2019-10-23",
                "location": "Pittsburgh",
                "image_path": null,
                "average_rating": 0,
                "category": {
                    "url": "http://localhost:8000/productcategories/6",
                    "name": "Games/Toys"
                }
            }
        """
        new_product = Product()
        new_product.name = request.data["name"]
        new_product.price = request.data["price"]
        new_product.description = request.data["description"]
        new_product.quantity = request.data["quantity"]
        new_product.location = request.data["location"]

        customer = Customer.objects.get(user=request.auth.user)
        new_product.customer = customer

        product_category = ProductCategory.objects.get(pk=request.data["category_id"])
        new_product.category = product_category
        product_store = Store.objects.get(pk=request.data["store_id"])
        new_product.store = product_store

        if "image_path" in request.data:
            image_path = request.data["image_path"]
            if ";base64," in image_path:
                format, imgstr = image_path.split(";base64,", 1)
                ext = format.split("/")[-1]
                data = ContentFile(
                    base64.b64decode(imgstr),
                    name=f'{new_product.id}-{request.data["name"]}.{ext}',
                )
                new_product.image_path = data
            else:
                new_product.image_path = request.data["image_path"]
                # Handle the case where image_path is not in the expected format
                print("Warning: Image path is not in the expected format.")

        new_product.save()

        serializer = ProductSerializer(new_product, context={"request": request})

        return Response(serializer.data, status=status.HTTP_201_CREATED)

    def retrieve(self, request, pk=None):
        """
        @api {GET} /products/:id GET product
        @apiName GetProduct
        @apiGroup Product

        @apiParam {id} id Product Id

        @apiSuccess (200) {Object} product Created product
        @apiSuccess (200) {id} product.id Product Id
        @apiSuccess (200) {String} product.name Short form name of product
        @apiSuccess (200) {String} product.description Long form description of product
        @apiSuccess (200) {Number} product.price Cost of product
        @apiSuccess (200) {Number} product.quantity Number of items to sell
        @apiSuccess (200) {Date} product.created_date City where product is located
        @apiSuccess (200) {String} product.location City where product is located
        @apiSuccess (200) {String} product.image_path Path to product image
        @apiSuccess (200) {Number} product.average_rating Average customer rating of product
        @apiSuccess (200) {Number} product.number_sold How many items have been purchased
        @apiSuccess (200) {Object} product.category Category of product
        @apiSuccessExample {json} Success
            {
                "id": 101,
                "url": "http://localhost:8000/products/101",
                "name": "Kite",
                "price": 14.99,
                "number_sold": 0,
                "description": "It flies high",
                "quantity": 60,
                "created_date": "2019-10-23",
                "location": "Pittsburgh",
                "image_path": null,
                "average_rating": 0,
                "category": {
                    "url": "http://localhost:8000/productcategories/6",
                    "name": "Games/Toys"
                }
            }
        """
        try:
            product = Product.objects.get(pk=pk)
            serializer = ProductSerializer(product, context={"request": request})
            return Response(serializer.data)

        except Exception as ex:
            return HttpResponseServerError(ex)

    def update(self, request, pk=None):
        """
        @api {PUT} /products/:id PUT changes to product
        @apiName UpdateProduct
        @apiGroup Product

        @apiHeader {String} Authorization Auth token
        @apiHeaderExample {String} Authorization
            Token 9ba45f09651c5b0c404f37a2d2572c026c146611

        @apiParam {id} id Product Id to update
        @apiSuccessExample {json} Success
            HTTP/1.1 204 No Content
        """
        product = Product.objects.get(pk=pk)
        product.name = request.data["name"]
        product.price = request.data["price"]
        product.description = request.data["description"]
        product.quantity = request.data["quantity"]
        product.created_date = request.data["created_date"]
        product.location = request.data["location"]

        customer = Customer.objects.get(user=request.auth.user)
        product.customer = customer

        product_category = ProductCategory.objects.get(pk=request.data["category_id"])
        product.category = product_category
        product.save()

        return Response({}, status=status.HTTP_204_NO_CONTENT)

    def destroy(self, request, pk=None):
        """
        @api {DELETE} /products/:id DELETE product
        @apiName DeleteProduct
        @apiGroup Product

        @apiHeader {String} Authorization Auth token
        @apiHeaderExample {String} Authorization
            Token 9ba45f09651c5b0c404f37a2d2572c026c146611

        @apiParam {id} id Product Id to delete
        @apiSuccessExample {json} Success
            HTTP/1.1 204 No Content
        """
        try:
            product = Product.objects.get(pk=pk)
            product.delete()

            return Response({}, status=status.HTTP_204_NO_CONTENT)

        except Product.DoesNotExist as ex:
            return Response({"message": ex.args[0]}, status=status.HTTP_404_NOT_FOUND)

        except Exception as ex:
            return Response(
                {"message": ex.args[0]}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    def list(self, request):
        """
        @api {GET} /products GET all products
        @apiName ListProducts
        @apiGroup Product

        @apiSuccess (200) {Object[]} products Array of products
        @apiSuccessExample {json} Success
            [
                {
                    "id": 101,
                    "url": "http://localhost:8000/products/101",
                    "name": "Kite",
                    "price": 14.99,
                    "number_sold": 0,
                    "description": "It flies high",
                    "quantity": 60,
                    "created_date": "2019-10-23",
                    "location": "Pittsburgh",
                    "image_path": null,
                    "average_rating": 0,
                    "category": {
                        "url": "http://localhost:8000/productcategories/6",
                        "name": "Games/Toys"
                    }
                }
            ]
        """
        products = Product.objects.all()

        # Support filtering by category and/or quantity
        category = self.request.query_params.get("category", None)
        quantity = self.request.query_params.get("quantity", None)
        order = self.request.query_params.get("order_by", None)
        direction = self.request.query_params.get("direction", None)
        number_sold = self.request.query_params.get("number_sold", None)
        location = self.request.query_params.get("location", None)
        min_price = self.request.query_params.get("min_price", None)

        if order is not None:
            order_filter = order

            if direction is not None:
                if direction == "desc":
                    order_filter = f"-{order}"

            products = products.order_by(order_filter)

        if category is not None:
            products = products.filter(category__id=category)

        if location is not None:
            products = products.filter(location__icontains=location)

        if quantity is not None:
            products = products.order_by("-created_date")[: int(quantity)]

        if number_sold is not None:

            def sold_filter(product):
                if product.number_sold <= int(number_sold):
                    return True
                return False

            products = filter(sold_filter, products)

        if min_price is not None:
            products = products.filter(price__gte=min_price)

        serializer = ProductSerializer(
            products, many=True, context={"request": request}
        )
        return Response(serializer.data)

    @action(methods=["post"], detail=True)
    def recommend(self, request, pk=None):
        """Recommend products to other users"""

        if request.method == "POST":
            rec = Recommendation()
            rec.recommender = Customer.objects.get(user=request.auth.user)
            rec.customer = Customer.objects.get(user__id=request.data["recipient"])
            rec.product = Product.objects.get(pk=pk)

            rec.save()

            return Response(None, status=status.HTTP_204_NO_CONTENT)

        return Response(None, status=status.HTTP_405_METHOD_NOT_ALLOWED)

    @action(methods=["post", "delete"], detail=True, permission_classes=[IsAuthenticatedOrReadOnly])
    def like(self, request, pk=None):
        customer = Customer.objects.get(user=request.auth.user)
        product = Product.objects.get(pk=pk)

        if request.method == "POST":
            if LikedProduct.objects.filter(customer=customer, product=product).exists():
                return Response({"detail": "Product already liked"}, status=status.HTTP_400_BAD_REQUEST)

            LikedProduct.objects.create(customer=customer, product=product)
            return Response({"detail": "Product liked"}, status=status.HTTP_201_CREATED)

        elif request.method == "DELETE":
            try:
                liked_product = LikedProduct.objects.get(customer=customer, product=product)
                liked_product.delete()
                return Response({"detail": "Product unliked"}, status=status.HTTP_204_NO_CONTENT)
            except LikedProduct.DoesNotExist:
                return Response({"detail": "Product not liked"}, status=status.HTTP_400_BAD_REQUEST)

    @action(methods=["get"], detail=False, permission_classes=[IsAuthenticatedOrReadOnly])
    def liked(self, request):
        customer = Customer.objects.get(user=request.auth.user)
        liked_products = LikedProduct.objects.filter(customer=customer).select_related('product')
        products = [liked_product.product for liked_product in liked_products]

        serializer = ProductSerializer(products, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)
DannyJamesHidalgo commented 2 months ago

dogs will look similar to this