使用Django Rest框架建造购物车
#网络开发人员 #编程 #python #django

嘿,欢迎您介绍另一篇文章,我将带您浏览一个令人兴奋的概念,这是电子商务网站和应用程序中最重要的一部分。
购物车或篮子至关重要,因为它允许客户选择他们想要购买的产品,类似于现实生活,我们使用手推车或篮子来容纳我们想要购买的各种物品。同样,我们实际上使用购物车实际完成了相同的任务。

本教程旨在引导您逐步构建此类购物车逻辑,同时利用Django和Django Rest框架的力量。毫无疑问,让我们深入研究吗?

项目设置

我们将首先导航到工作空间上的一个文件夹,在那里我们将创建项目文件夹。对我来说,那是桌面文件夹,因此请打开一个终端窗口并导航到该文件夹​​:

cd desktop
mkdir drf_shopping_cart && cd drf_shopping_cart

上面,我们将导航到计算机上的桌面文件夹中,然后创建一个称为drf_shopping_cart的文件夹,然后还导航到其中。接下来,让我们发出以下命令,我很快就会解释:

virtualenv env
source env/bin/activate
pip install django djangorestframework pillow

在这里,我们正在创建一个虚拟环境,以将项目的依赖关系与系统宽的配置和设置隔离,然后我们激活它,最后安装我们的两个主要依赖项,即Django和DRF以及用于图像的第三个依赖关系枕头Python的处理。
借此来,请发出以下命令,我在一点点解释:

django-admin startproject mysite .
python manage.py startapp cart

因此,第一个命令用于创建一个新项目,然后使用startapp命令在我们的项目中创建一个新应用。 cart应用程序是我们将花费大量时间编写代码的地方。下一个至关重要的步骤是将我们新创建的cart应用与DRF一起添加到settings.py中的已安装应用程序列表,因此请继续添加:

INSTALLED_APPS = [
    ....
    'cart',
    'rest_framework',
]

设置模型

现在,如果您考虑一下,我们需要一个数据库表,该表将包含产品信息,例如名称,价格,Image E.T.C.我们可以通过使用Django模型来实现这一目标,以便我们将Python代码作为类编写,并且由于内置的​​Django Orm,它将被转换为SQL表。
前往cart/models.py并添加以下代码:

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=150)
    description = models.TextField(blank=True)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    image = models.ImageField(upload_to='products/%Y/%m/%d', blank=True)
    is_available = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    modified_at = models.DateTimeField(auto_now=True)

    def __str__(self) -> str:
        return str(self.name)

我们已经定义了一些表字段,但随时可以添加更多信息。为了确保我们的数据库已捕获,我们需要运行以下命令来同步应用更改:

python makemigrations && python manage.py migrate

此时我们的数据库准备就绪。
现在,因为我们将处理产品图像,我们需要添加更多设置,以免遇到错误和挫败感。首先打开mysite/settings.py文件,然后在底部添加以下内容:

import os

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

我们正在设置MEDIA_ROOT,这是通往目录的绝对路径,我们的应用程序将从
中提供图像 然后,我们将设置MEDIA_URL,这是将处理媒体根部提供的图像的URL。

下一个打开mysite/urls.py并添加以下代码:

#...
from django.conf.urls.static import static
from django.conf import settings

urlpatterns = [
    #...
]

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

在这一点上,我们准备移至下一章。

用产品填充数据库

现在,我们需要使用一些数据来填充数据库表的数据库表,这些数据可以用来测试购物车功能。
我们将需要创建一个端点,使我们能够创建和列出产品并实现这一目标,我们将创建一个序列化器类,以将复杂的数据类型转换为本机Python等效物,然后可以将其呈现为JSON。
接下来,我们将创建一个视图,该视图允许我们从数据库中写入并阅读。

在购物车文件夹中创建一个名为serializers.py的文件,并添加以下代码:

from rest_framework import serializers
from .models import Product

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = "__all__"

下一个打开cart/views.py并添加以下代码:

from rest_framework.views import APIView
from rest_framework import status
from rest_framework.response import Response
from .serializers import ProductSerializer
from .models import Product

class ProductAPI(APIView):
    """
    Single API to handle product operations
    """
    serializer_class = ProductSerializer

    def get(self, request, format=None):
        qs = Product.objects.all()

        return Response(
            {"data": self.serializer_class(qs, many=True).data}, 
            status=status.HTTP_200_OK
            )

    def post(self, request, *args, **kwargs):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()

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

我们创建了一个单个端点,它将允许我们执行GET请求,以检索数据库中的所有产品和POST请求,以将新产品保存到数据库中。
下一个打开mysite/urls.py并添加以下代码:

#...
from cart.views import ProductAPI

urlpatterns = [
    #...
    path('products', ProductAPI.as_view(), name='products'),

]
#...

我们准备在数据库中添加新产品,因此可以通过:
旋转服务器

python manage.py runserver

访问http://localhost:8000/products并添加几个产品。

products api

建造购物车

在这一点 购物车应在客户浏览网站之前暂时存储任何产品,直到下订单为止。

为此,我们将利用Django的会话框架来坚持购物车并将其保持在会话中,直到下订单或会话到期。

Django的会话框架支持匿名和用户会话,并且存储的数据在每个站点访问者中都是唯一的。当我们创建项目时,Django添加了一个会话中间件django.contrib.sessions.middleware.SessionMiddleware,以管理项目中的所有会话。此中间件将当前的会话用作字典中的当前会话,因此我们可以通过调用request.session访问它,并且可以序列化。

足够的文献,让我们编写一些代码:)。这是我们要做的:

  • 创建一个手推车服务来处理购物车
  • 创建一个调用购物车服务的API端点
  • 测试每个购物车操作

如上所述,我们需要创建一项服务来放置购物车的逻辑。在购物车文件夹中创建一个名为service.py的文件,并添加以下代码:


from decimal import Decimal

from django.conf import settings

from .serializers import ProductSerializer
from .models import Product


class Cart:
    def __init__(self, request):
        """
        initialize the cart
        """
        self.session = request.session
        cart = self.session.get(settings.CART_SESSION_ID)
        if not cart:
            # save an empty cart in session
            cart = self.session[settings.CART_SESSION_ID] = {}
        self.cart = cart

    def save(self):
        self.session.modified = True

    def add(self, product, quantity=1, overide_quantity=False):
        """
        Add product to the cart or update its quantity
        """

        product_id = str(product["id"])
        if product_id not in self.cart:
            self.cart[product_id] = {
                "quantity": 0,
                "price": str(product["price"])
            }
        if overide_quantity:
            self.cart[product_id]["quantity"] = quantity
        else:
            self.cart[product_id]["quantity"] += quantity
        self.save()

    def remove(self, product):
        """
        Remove a product from the cart
        """
        product_id = str(product["id"])

        if product_id in self.cart:
            del self.cart[product_id]
            self.save()

    def __iter__(self):
        """
        Loop through cart items and fetch the products from the database
        """
        product_ids = self.cart.keys()
        products = Product.objects.filter(id__in=product_ids)
        cart = self.cart.copy()
        for product in products:
            cart[str(product.id)]["product"] = ProductSerializer(product).data
        for item in cart.values():
            item["price"] = Decimal(item["price"]) 
            item["total_price"] = item["price"] * item["quantity"]
            yield item

    def __len__(self):
        """
        Count all items in the cart
        """
        return sum(item["quantity"] for item in self.cart.values())

    def get_total_price(self):
        return sum(Decimal(item["price"]) * item["quantity"] for item in self.cart.values())

    def clear(self):
        # remove cart from session
        del self.session[settings.CART_SESSION_ID]
        self.save()

现在让我们浏览上面的代码,因为那里发生了很多事情。
我们定义了一个Cart课程,使我们能够管理购物车。

__init__方法是我们的​​构造函数,需要一个请求对象才能初始化购物车。我们还存储了当前的会话,使其可以访问购物车类的其他方法。如前所述,我们尝试从当前会话中获取购物车,如果缺少它,我们通过在会话中设置一个空词典来创建一个空购物。

add方法是我们的​​下一个方法,用于将产品添加到购物车或更新其数量。它将product, quantity, override_quantity作为参数。产品ID充当字典的关键,而数量和价格数字成为字典的价值。

save方法简单地将会话标记为修改,因此Django知道会话更改并需要保存

remove方法确实可以从购物车中取出给定的产品。它还调用保存方法来确保所有保存所有内容。

然后,我们定义了一种__iter__方法,该方法使我们可以循环浏览购物车的项目并访问相关的产品对象。在这里,我们获取了购物车中存在的产品实例。我们将在我们看来使用它。

__len__方法返回购物车中的总项目数。

get_total_price方法计算购物车中物品的总成本。

最后但并非最不重要的一点是,clear方法在调用save方法之前删除整个字典中的卡车对象。

最后,我们将以下设置添加到mysite/settings.py文件。

CART_SESSION_ID = 'cart

有了所有这些,我们的Cart班准备好管理购物车。

创建购物车API视图

好吧,我们有一个Cart类来管理购物车,我们现在需要创建API端点以列出,添加,更新,删除项目并清除购物车。
考虑到这一点,让我们创建视图。打开cart/views.py并添加以下代码:

class CartAPI(APIView):
    """
    Single API to handle cart operations
    """
    def get(self, request, format=None):
        cart = Cart(request)

        return Response(
            {"data": list(cart.__iter__()), 
            "cart_total_price": cart.get_total_price()},
            status=status.HTTP_200_OK
            )

    def post(self, request, **kwargs):
        cart = Cart(request)

        if "remove" in request.data:
            product = request.data["product"]
            cart.remove(product)

        elif "clear" in request.data:
            cart.clear()

        else:
            product = request.data
            cart.add(
                    product=product["product"],
                    quantity=product["quantity"],
                    overide_quantity=product["overide_quantity"] if "overide_quantity" in product else False
                )

        return Response(
            {"message": "cart updated"},
            status=status.HTTP_202_ACCEPTED)

那里发生了很多事情,所以让我们将其分解。我们是子类REST框架的Apiview,并定义了两种http方法。

get方法中,我们发送了一个GET请求并初始化一个购物车对象,然后我们调用其__iter__方法,该方法将使我们归还生成器对象。然后,我们将内置的list拨打,以获取一份列表,然后将其作为响应的一部分序列化。另外,我们正在通过致电get_total_price来返回购物车总数。

post方法中,我们发送了一个POST请求,然后初始化卡车传递的实例,然后我们检查请求有效载荷中是否存在一些东西。

我们检查有效载荷中是否有一个名为remove的密钥,如果存在,则我们从有效载荷中调用了产品中的购物车的remove()

然后,我们检查有效载荷中是否有一个名为clear的密钥,如果存在,则我们调用购物车的clear()以将其从当前会话中删除。

如果我们没有这两个密钥,那么我们将通过产品中的购物车的add()称为add(),数量以及是否覆盖数量。

在一天结束时,我们还会回复带有消息和状态的响应,但我们可以做更多的事情。在测试逻辑之前,我们只有最后一步。
打开mysite/urls.py并向它添加以下代码:

#...
from cart.views import CartAPI

urlpatterns = [
    #...
    path('cart', CartAPI.as_view(), name='cart'),
    #...
]
#...

让我们测试购物车

确保服务器仍在运行。前往购物车的URL,即http://localhost:8000/cart。让我们用follwoing有效载荷进行帖子:

{
  "product": {
            "id": 1,
            "name": "Macbook Pro",
            "description": "Our most powerful laptops, MacBook Pros are supercharged by M1 and M2 chips. Featuring Magic Keyboard, Touch ID, and brilliant Retina display.",
            "price": "1800.00",
            "image": "/products/2023/08/30/sp809-mbp16touch-silver-2019.jpeg",
            "is_available": true,
            "created_at": "2023-08-30T11:58:54.476688Z",
            "modified_at": "2023-08-30T11:58:54.476720Z"
        },
  "quantity": 5
}

现在,在Get请求时,我们应该看到以下屏幕以显示添加作用。

api
要从购物车中删除特定产品的有效载荷发送看起来像:

{
  "product": {
            "id": 1,
            "name": "Macbook Pro",
            "description": "Our most powerful laptops, MacBook Pros are supercharged by M1 and M2 chips. Featuring Magic Keyboard, Touch ID, and brilliant Retina display.",
            "price": "1800.00",
            "image": "/products/2023/08/30/sp809-mbp16touch-silver-2019.jpeg",
            "is_available": true,
            "created_at": "2023-08-30T11:58:54.476688Z",
            "modified_at": "2023-08-30T11:58:54.476720Z"
        },
  "remove": true
}

和清除购物车,有效载荷将简单:

{
  "clear": true
}

继续测试它们,看看您最终的结果:)

这是一个包装

是的,这是一个包裹!如果您到达教程的末尾,那么对您来说是一个很大的拥抱。感谢您抽出宝贵的时间阅读和关注,希望您学到了一两件事,可以帮助您进行编码之旅。
如果您有任何想法,问题等,请将它们放在下面。

我也在寻找我的下一个角色,因此,如果您的团队正在招聘或认识任何人,那么如果我得到推荐,我会很有责任。

我可以通过Twitter或我的linkedin或我的website提供,我的电子邮件是nicksonlangat95@gmail.com

本教程中使用的代码可以是found here.
暂时再见,在下一个见到你。欢呼ð¥