Skip to content

幕熙商城编码文档

介绍

muxi_shop是一个使用Django和Vue开发的前后端分离的网上上商城项目。具有现代网上商城99%的基础功能(用户注册、浏览商品、添加购物车、下订单、查看订单等功能)

源码地址:

环境准备

  • 系统环境

    • Ubuntu Ubuntu 24.04.3 LTS
    • python 3.13
  • python安装pipenv

    python
    pip install pipenv
  • 创建文件夹django_project并开启虚拟环境

    bash
    mkdir django_project && cd django_project
    pipenv shell
  • 虚拟环境中下载django

    bash
    pip install django
  • pycharm 配置解释器

    • windows中: ~\.virtualenvs\Django_project-2tVlZDRx\Scripts\python.exe
    • Ubuntu中: /home/username/.local/share/virtualenvs/myproject-4XG6W5W9/bin/python3

创建项目和APP

  • django_project/ 目录下:
    bash
    django-admin startproject muxi_shop_api
    cd muxi_shop_api
    mkdir apps && cd apps
    python ../manage.py startapp user
    python ../manage.py startapp order 
    python ../manage.py startapp pay 
    python ../manage.py startapp menu 
    python ../manage.py startapp goods
    python ../manage.py startapp comment
    python ../manage.py startapp cart
    python ../manage.py startapp address

APP配置&静态资源配置

muxi_shop_api/settings.py文件中:

  • 注册app

    INSTALLED_APPS中添加:

    python
    INSTALLED_APPS = [
        ...
        'apps.address',
        'apps.cart',
        'apps.comment',
        'apps.goods',
        'apps.menu',
        'apps.order',
        'apps.pay',
        'apps.user',
    ]
  • 每个app/apps.py中修改Configname,例:

    python
    # apps/cart/apps.py
    from django.apps import AppConfig
    
    class CartConfig(AppConfig):
        default_auto_field = 'django.db.models.BigAutoField'
        name = 'apps.cart'  # 在原来的名字前面加上 apps.
  • 配置全局静态文件

    python
    # settings.py
    STATICFILES_DIRS = [
      os.path.join(BASE_DIR, 'static')
    ]

URL模块化

  • 在每个app中创建urls.py文件夹,初始化以下内容:

    python
    from django.urls import path
    
    urlpatterns = []
  • 在项目的urls.py中,将各app的urls.py包含进来

    python
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('address/', include('apps.address.urls')),
        path('cart/', include('apps.cart.urls')),
        path('comment/', include('apps.comment.urls')),
        path('goods/', include('apps.goods.urls')),
        path('menu/', include('apps.menu.urls')),
        path('order/', include('apps.order.urls')),
        path('pay/', include('apps.pay.urls')),
        path('user/', include('apps.user.urls')),
    ]

连接数据库

  • 安装数据库

    • Ubuntu
      bash
      sudo apt update 
      sudo apt install mysql-server
    • MacOS
      bash
      brew install mysql
      
      # 启动 MySQL 服务
      brew services start mysql
      
      # 或者手动启动(不随系统启动)
      mysql.server start
  • 运行安全配置脚本(重要)

    • Ubuntu
      bash
      sudo mysql_installation
    • MacOS
      bash
      mysql_secure_installation

    按照提示进行操作:

    • 系统会问你是否要设置 VALIDATE PASSWORD COMPONENT(验证密码插件)。这个插件可以强制要求用户设置强密码。根据你的安全需求选择 Y 或 n。
    • 为 MySQL root 用户设置一个强密码。请务必记住这个密码。 后续的问题通常建议都选择 Y (Yes):
    • 移除匿名用户?Y
    • 禁止远程 root 登录?Y (这能提高安全性)
    • 移除测试数据库并对其的访问?Y
    • 立即重新加载权限表?Y
  • mysql控制命令

    • Ubuntu

      bash
      sudo systemctl status mysql # 检查mysql运行状态
      sudo systemctl start mysql  # 启动mysql
      sudo systemctl enable mysql # 设置开机自启动
    • MacOS

      bash
      # 使用 Homebrew 管理服务(推荐)
      brew services start mysql     # 启动
      brew services stop mysql      # 停止
      brew services restart mysql   # 重启
      brew services list           # 查看状态
      
      brew services enable mysql   # 启用开机自启
      brew services disable mysql  # 禁用开机自启
    • mysql

      bash
      # 使用 root 用户连接
      mysql -u root -p
      
      # 指定主机和端口
      mysql -u root -p -h 127.0.0.1 -P 3306
      
      # 使用特定用户连接
      mysql -u username -p
      
      # 连接指定数据库
      mysql -u root -p database_name
  • 为Django项目创建专用用户和数据库

    sql
    CREATE DATABASE muxi_shop CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
    CREATE USER 'admin1'@'localhost' IDENTIFIED BY '123qwe';
    GRANT ALL PRIVILEGES ON muxi_shop.* TO 'admin1'@'localhost';
    FLUSH PRIVILEGES;
    EXIT;

    说明:

    • utf8mb4 字符集支持包括表情符号在内的所有 Unicode 字符,这是现代应用的推荐设置。
    • COLLATE 用于设置默认排序规则
    • @'localhost' 表示该用户只能从本机连接。如果你的数据库和 Django 不在同一台机器上,需要修改为主机的 IP%(不推荐,有安全风险)。
  • 下载 pymsqlclient

    • Ubuntu

      bash
      # 安装开发依赖和Python3头文件
      sudo apt install python3-dev default-libmysqlclient-dev build-essential pkg-config
      python
      # 使用 pip 安装 mysqlclient
      pip install mysqlclient
    • MacOS

      bash
      brew install mysql-client pkg-config
      
      echo 'export LDFLAGS="-L/usr/local/opt/mysql-client/lib"' >> ~/.zshrc
      echo 'export CPPFLAGS="-I/usr/local/opt/mysql-client/include"' >> ~/.zshrc
      echo 'export PKG_CONFIG_PATH="/usr/local/opt/mysql-client/lib/pkgconfig"' >> ~/.zshrc
      source ~/.zshrc
      python
      # 安装mysqlclient
      pip install mysqlclient
  • 配置数据库

    python
    # settings.py 中修改DATABASE
    DATABASES = {
      'default': {
          # 数据库引擎(是mysql还是oracle等)
          'ENGINE': 'django.db.backends.mysql',
          # 数据库名
          'NAME': 'muxi_shop',
          # 连接数据库的用户名
          'USER': 'admin1',
          # 连接数据库的密码
          'PASSWORD': '123qwe',
          # 数据库的主机地址
          'HOST': '127.0.0.1',
          # 数据库的端口号
          'PORT': '3306',
      }
    }

向数据库中写入数据

  • 登入并选择数据库

    bash
    sudo mysql -u admin1 -p # 输入上面创建的admin1用户的密码123qwe
    show databases; # 查看admin1的所有数据库
    use muxi_shop;
  • 创建数据库并写入数据

    将此文件(Django/muxi_shop.sql)中的代码复制到命令行执行(近万条数据,全部复制执行即可)

根据数据库自动生成模型类

  • 检查数据库,生成model代码,将生成的代码保存至每个appmodels.py

    python
    python manage.py inspectdb --database default user_address > apps/address/models.py
    python manage.py inspectdb --database default shopping_cart > apps/cart/models.py
    python manage.py inspectdb --database default comment > apps/comment/models.py
    python manage.py inspectdb --database default goods > apps/goods/models.py
    python manage.py inspectdb --database default main_menu sub_menu >> apps/menu/models.py
    python manage.py inspectdb --database default order order_goods >> apps/order/models.py
    python manage.py inspectdb --database default user > apps/user/models.py

    > 覆盖 >> 追加

配置django后台

  • 创建超级用户,以便访问 admin 后台

    python
    python manage.py createsuperuser

    按照提示输入用户名、邮箱和密码

  • 测试

    python
    python manage.py runserver

    在浏览器中打开 http://127.0.0.1:8000/admin/,尝试用刚创建的超级用户登录,确保数据库连接正常。

跨域请求配置

  • 安装django-cors-headers

    python
    pip install django-cors-headers
  • settings.py文件中配置跨域:

    python
    # 添加corsheaders到INSTALLED_APPS
    INSTALLED_APPS = [
      ...
      'corsheaders',
      ...
    ]
    
    # 允许所有域名跨域(生产环境慎用!)
    CORS_ORIGIN_ALLOW_ALL = True
    
    # 允许携带cookie
    CORS_ALLOW_CREDENTIALS = True
    
    # 允许的HTTP方法(可选)
    CORS_ALLOW_METHODS = [
      'DELETE',
      'GET',
      'OPTIONS',
      'PATCH',
      'POST',
      'PUT',
    ]
    
    # 允许的HTTP头(可选)
    CORS_ALLOW_HEADERS = [
        'accept',
        'accept-encoding',
        'authorization',
        'content-type',
        'dnt',
        'origin',
        'user-agent',
        'x-csrftoken',
        'x-requested-with',
    ]
    
    # 允许特定路径的跨域请求(可选)
    CORS_URLS_REGEX = r'^/api/.*$'
    
    # 预检请求缓存时间(秒)(可选)
    CORS_PREFLIGHT_MAX_AGE = 86400
    
    # 暴露给浏览器的响应头(可选)
    CORS_EXPOSE_HEADERS = []
    
    
    # 添加CorsMiddleware到MIDDLEWARE第一行
    # 注释掉CsrfViewMiddleware
    MIDDLEWARE = [
        'corsheaders.middleware.CorsMiddleware',
        ...
        # 'django.middleware.csrf.CsrfViewMiddleware',
        ...
    ]

安装Django Rest Framework

接下来的模块开发、序列化与反序列化都需要Django Rest Framework,在这里先安装下

bash
pip install djangorestframework

封装全局HttpResponse类

项目根目录新建 utils/BaseResponse.py 用于定义响应体和状态码模板

python
# utils/BaseResponse.py
from django.http import JsonResponse
from rest_framework import status
from typing import Any, Dict, Optional, List


class APIResponse:
    """
    通用API响应类
    使用示例:return APIResponse.success(data={'id': 1}, message='操作成功')
    响应示例:
        {
            "success": true,
            "code": 200,
            "message": "Success",
            "data": {}
        }
    """

    @classmethod
    def success(
            cls,
            data: Any = None,
            message: str = "Success",
            status_code: int = status.HTTP_200_OK
    ) -> JsonResponse:
        """
        成功响应
        """
        response_data = {
            "success": True,
            "code": status_code,
            "message": message,
            "data": data if data is not None else {}
        }
        return JsonResponse(response_data, status=status_code)

    @classmethod
    def created(
            cls,
            data: Any = None,
            message: str = "Resource created successfully",
            status_code: int = status.HTTP_201_CREATED
    ) -> JsonResponse:
        """
        创建成功响应
        """
        return cls.success(data, message, status_code)

    @classmethod
    def error(
            cls,
            message: str = "Error occurred",
            status_code: int = status.HTTP_400_BAD_REQUEST,
            errors: Optional[List[Dict]] = None
    ) -> JsonResponse:
        """
        错误响应
        """
        response_data = {
            "success": False,
            "code": status_code,
            "message": message,
            "errors": errors if errors else []
        }
        return JsonResponse(response_data, status=status_code)

    @classmethod
    def not_found(
            cls,
            message: str = "Resource not found",
            status_code: int = status.HTTP_404_NOT_FOUND
    ) -> JsonResponse:
        """
        资源未找到响应
        """
        return cls.error(message, status_code)

    @classmethod
    def validation_error(
            cls,
            message: str = "Validation error",
            errors: Optional[List[Dict]] = None,
            status_code: int = status.HTTP_422_UNPROCESSABLE_ENTITY
    ) -> JsonResponse:
        """
        验证错误响应
        """
        return cls.error(message, status_code, errors)

    @classmethod
    def unauthorized(
            cls,
            message: str = "Unauthorized access",
            status_code: int = status.HTTP_401_UNAUTHORIZED
    ) -> JsonResponse:
        """
        未授权响应
        """
        return cls.error(message, status_code)

    @classmethod
    def forbidden(
            cls,
            message: str = "Forbidden access",
            status_code: int = status.HTTP_403_FORBIDDEN
    ) -> JsonResponse:
        """
        禁止访问响应
        """
        return cls.error(message, status_code)

    @classmethod
    def server_error(
            cls,
            message: str = "Internal server error",
            status_code: int = status.HTTP_500_INTERNAL_SERVER_ERROR
    ) -> JsonResponse:
        """
        服务器错误响应
        """
        return cls.error(message, status_code)

    @classmethod
    def paginated(
            cls,
            data: List[Any],
            total: int,
            page: int,
            page_size: int,
            message: str = "Success",
            status_code: int = status.HTTP_200_OK
    ) -> JsonResponse:
        """
        分页响应
        """
        response_data = {
            "success": True,
            "code": status_code,
            "message": message,
            "data": {
                "items": data,
                "pagination": {
                    "total": total,
                    "page": page,
                    "page_size": page_size,
                    "total_pages": (total + page_size - 1) // page_size if page_size > 0 else 0
                }
            }
        }
        return JsonResponse(response_data, status=status_code)

    @classmethod
    def custom(
            cls,
            success: bool,
            code: int,
            message: str,
            data: Optional[Any] = None,
            errors: Optional[List[Dict]] = None
    ) -> JsonResponse:
        """
        自定义响应
        """
        response_data = {
            "success": success,
            "code": code,
            "message": message,
        }

        if data is not None:
            response_data["data"] = data

        if errors is not None:
            response_data["errors"] = errors

        return JsonResponse(response_data, status=code)

路由模块化

  • 在每个app下创建文件urls.py
  • 在项目的urls.py中包含每个appurls.py
    python
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('menu/', include('apps.menu.urls')),
        path('goods/', include('apps.goods.urls')),
        path('order/', include('apps.order.urls')),
        path('pay/', include('apps.pay.urls')),
        path('user/', include('apps.user.urls')),
        path('cart/', include('apps.cart.urls')),
        path('address/', include('apps.address.urls')),
        path('comment/', include('apps.comment.urls')),
    ]

APIs

模块描述api状态
分类菜单一级分类菜单GET /menu/main_menu已完成
分类菜单二级分类菜单GET menu/sub_menu?main_menu_id={main_menu_id}已完成

商品分类菜单模块开发(/apps/menu/)

功能展示

商品分类菜单就是首页中用于商品分类的一级二级菜单:

商品分类菜单

整体规划

一共设计两个接口:

  • GET /menu/main_menu - 用于返回一级菜单信息
  • GET menu/sub_menu?main_menu_id={main_menu_id} - 根据 main_menu_id 返回相应的 sub_menu 信息

视图开发

python
# apps/menu/views.py
from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.views import APIView

from apps.menu.models import MainMenu, SubMenu
from apps.menu.serializers import MainMenuSerializer, SubMenuSerializer
from utils.BaseResponse import APIResponse


# Create your views here.
class GoodsMainMenuAPIView(APIView):

    # 获取一级菜单:`GET /main_manu`
    def get(self, request):
        try:
            # 获取main_menu表中所有数据
            main_menu = MainMenu.objects.all()

            # 序列化操作 django object -> json
            result = MainMenuSerializer(instance=main_menu, many=True)

            return APIResponse.success(result.data,message='获取一级菜单成功!')
        except Exception as e:
            return APIResponse.error(message=str(e))



class GoodsSubMenuAPIView(APIView):

    # 获取二级菜单:`GET /sub_menu/{main_menu_id}`
    def get(self, request, main_menu_id):

        try:
            sub_menu = SubMenu.objects.filter(main_menu_id=main_menu_id)

            # 序列化操作 django object -> json
            result = SubMenuSerializer(instance=sub_menu, many=True)

            return APIResponse.success(result.data, message=f"获取{main_menu_id}的二级菜单成功!")
        except Exception as e:
            return APIResponse.error(message=str(e))

序列化器

/apps/menu/ 新建 serializers.py:

python
# /apps/menu/serializers.py
from rest_framework import serializers

from apps.menu.models import MainMenu, SubMenu

class MainMenuSerializer(serializers.ModelSerializer):

    class Meta:
        model = MainMenu
        fields = "__all__"



class SubMenuSerializer(serializers.ModelSerializer):

    class Meta:
        model = SubMenu
        fields = "__all__"

路由

python
# apps/menu/urls.py
from django.urls import path

from apps.menu.views import GoodsMainMenuAPIView, GoodsSubMenuAPIView

urlpatterns = [
    path('main_menu/', GoodsMainMenuAPIView.as_view(), name='main_menu'),
    path('sub_menu/<int:main_menu_id>', GoodsSubMenuAPIView.as_view(), name='submenu'),
]

商品类型模块开发(/apps/goods/)

功能展示

商品类型

整体规划

一共设计两个接口:

  • GET /category/<int:category_id>/<int:page>/ - 获取商品列表
  • GET /detail/<int:sku_id> - 获取商品详情

视图开发

python
# apps/goods/views.py

from django.core.paginator import Paginator
from django.http import JsonResponse
from django.shortcuts import render
from rest_framework.views import APIView

from apps.goods.models import Goods
from apps.goods.serializers import GoodsCategorySerializer
from utils.BaseResponse import APIResponse


# Create your views here.
class GoodsCategoryAPIView(APIView):

    # 获取商品列表:/category/<int:category_id>/<int:page>/
    def get(self, request, category_id, page):
        try:
            # 获取查询集
            goods_category = Goods.objects.filter(
                type_id=category_id,
            ).all()

            # 分页
            paginator = Paginator(object_list=goods_category, per_page=20)
            page_obj = paginator.get_page(page)

            result = GoodsCategorySerializer(instance=page_obj, many=True)
            return APIResponse.success(result.data, message=f"获取{category_id}类别下所有商品成功!")

        except Exception as e:
            return APIResponse.error(message=str(e))

class GoodsDetailAPIView(APIView):

    # 获取商品详情:/detail/<int:sku_id>
    def get(self, request, sku_id):

        try:
            # 获取查询集
            goods_detail = Goods.objects.filter(
                sku_id=sku_id,
            ).all()

            result = GoodsCategorySerializer(instance=goods_detail, many=True)

            return APIResponse.success(result.data, message="获取商品详情成功!")
        except Exception as e:
            return APIResponse.error(message=str(e))

序列化器

python
from rest_framework import serializers

from apps.goods.models import Goods


class GoodsCategorySerializer(serializers.ModelSerializer):

    class Meta:
        model = Goods
        fields = '__all__'

路由

python
from django.urls import path

from apps.goods.views import GoodsCategoryAPIView, GoodsDetailAPIView

urlpatterns = [
    path('category/<int:category_id>/<int:page>/', GoodsCategoryAPIView.as_view(), name='goods_category'),
    path('detail/<int:sku_id>/', GoodsDetailAPIView.as_view(), name='goods_detail'),
]

购物车模块开发(/apps/cart/)

整体规划

一共设计两个接口:

  • 获取购物车信息:GET /cart?email={email}
  • 添加购物车: POST /cart

视图开发

python
from django.db import transaction
from rest_framework.views import APIView

from apps.cart.models import ShoppingCart
from apps.cart.serializers import ShoppingCartSerializer
from utils.BaseResponse import APIResponse


# Create your views here.
class ShoppingCartAPIView(APIView):
    # @todo 登录权限验证

    # 获取购物车信息:GET `/cart?email={email}`
    def get(self, request):

        try:
            email = request.GET.get('email')
            shopping_cart = ShoppingCart.objects.filter(email=email).all()

            result = ShoppingCartSerializer(shopping_cart, many=True)


            return APIResponse.success(data=result.data, message="获取用户购物车信息成功!")
        except Exception as e:
            return APIResponse.error(message=str(e))

    # 添加购物车 POST `/cart`
    def post(self, request):
        # 数据验证
        serializer = ShoppingCartSerializer(data=request.data)
        if not serializer.is_valid():
            return APIResponse.error(errors=serializer.errors,message="请求体验证失败!")
        request_data = serializer.validated_data

        try:
            # 使用原子事务确保数据一致性
            with transaction.atomic():
                # 查找或创建购物车项(使用原子操作)
                existed, created = ShoppingCart.objects.select_for_update().get_or_create(
                    email=request_data['email'],
                    sku_id=request_data['sku_id'],
                    is_delete=0,
                    defaults={
                        'nums': request_data['nums'],
                        'is_delete': 0
                    }
                )

                # 如果购物车中已存在则更新数量,否则新建加购信息
                if not created:
                    existed.nums += request_data['nums']
                    existed.save()

                # 4. 序列化返回结果
                result = ShoppingCartSerializer(instance=existed)

                return APIResponse.success(
                    data=result.data,
                    message="商品已存在,数量更新成功!" if not created
                    else "新商品添加购物车成功!"
                )

        except Exception as e:
            # 异常处理
            return APIResponse.error(message=f"添加购物车操作失败: {str(e)}")

序列化器

python
from rest_framework import serializers

from apps.cart.models import ShoppingCart


class ShoppingCartSerializer(serializers.ModelSerializer):
    class Meta:
        model = ShoppingCart
        fields = '__all__'

路由

python
from django.urls import path

from apps.cart.views import ShoppingCartAPIView

urlpatterns = [
    path('', ShoppingCartAPIView.as_view(),name='shopping_cart'),
]

订单模块开发(/apps/order/)

整体规划

  • 创建订单预览:POST /goods
  • 获取订单详情:GET /goods/{trade_no}

视图开发

python
from django.contrib.admin.utils import lookup_field
from rest_framework.generics import GenericAPIView

from apps.order.models import OrderGoods
from apps.order.serializers import OrderGoodsSerializer
from utils.BaseResponse import APIResponse


# Create your views here.

class OrderGoodsGenericAPIView(GenericAPIView):
    queryset = OrderGoods.objects.all()
    serializer_class = OrderGoodsSerializer

    lookup_field = 'trade_no'
    lookup_url_kwarg = 'trade_no'

    # 创建订单预览:POST `/goods`
    def post(self, request):
        try:
            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)

            serializer.save()
            return APIResponse.success(message="订单创建成功!")
        except Exception as e:
            return APIResponse.error(message="创建订单失败!",errors=[{
                    'errmsg': str(e)
                }])

    # 获取订单详情:GET `/goods/{trade_no}`
    def get(self, request, trade_no):
        try:
            order_info = self.get_object()
            result = self.get_serializer(instance=order_info)
            return APIResponse.success(data=result.data, message="获取订单详情成功!")

        except Exception as e:
            return APIResponse.error(message="获取订单详情失败!", errors=[
                {"errmsg":str(e)}
            ])

序列化器

python
from rest_framework import serializers

from apps.order.models import OrderGoods


class OrderGoodsSerializer(serializers.ModelSerializer):

    class Meta:
        model = OrderGoods
        fields = '__all__'

路由

python
from django.urls import path, re_path

from apps.order.views import OrderGoodsGenericAPIView

urlpatterns = [
    path('goods/', OrderGoodsGenericAPIView.as_view(), name='order_goods'),
    re_path('goods/(?P<trade_no>.*)', OrderGoodsGenericAPIView.as_view(), name='order_goods'),
]

地址模块开发(/apps/address/)

整体规划

  • 查看某条地址信息: GET /address/{pk}
  • 新建地址:POST /address
  • 更改某条地址信息: PUT /address/{pk}
  • 查询当前所有地址列表: GET /address/list

视图开发

python
from rest_framework import serializers
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response

from apps.address.models import UserAddress
from apps.address.serializers import AddressSerializer
from utils.BaseResponse import APIResponse


# Create your views here.
class AddressGenericAPIView(GenericAPIView):

    queryset = UserAddress.objects.all()
    serializer_class = AddressSerializer

    # 查看某条地址信息: GET `/address/{pk}`
    def get(self, request,pk):
        """
        获取单个地址详情
        - pk: 地址的主键ID,从URL参数中获取
        - 使用get_object()方法自动根据pk查找对象
        - 返回序列化后的地址数据
        """
        try:
            # 自动获取url中的pk,根据pk获取具体的地址对象
            address_info = self.get_object()
            # 将对象序列化为JSON格式
            result = self.get_serializer(instance=address_info)
            # 返回序列化后的数据
            return APIResponse.success(data=result.data,message="地址信息获取成功!")
        except UserAddress.DoesNotExist:
            # 如果地址不存在,返回404错误
            return APIResponse.error(message="地址不存在!")

    # 新建地址:POST `/address`
    def post(self, request):
        """
        创建新地址
        - 从请求体中获取数据
        - 验证数据有效性
        - 保存到数据库
        - 返回创建成功的地址数据
        """

        try:
            # 接收并反序列化请求体数据
            serializer = self.get_serializer(data=request.data)
            # 验证数据有效性
            serializer.is_valid(raise_exception=True)
            # 保存数据至数据库
            serializer.save()

            return APIResponse.success("新建地址成功!")

        except Exception as e:
            return APIResponse.error(message="创建地址失败",errors=[{
                    'errmsg': str(e)
                }])


    # 更改某条地址信息: PUT `/address/{pk}`
    def put(self, request,pk):
        """
        完整更新地址信息
        - pk: 要更新的地址主键ID
        - 需要提供所有必填字段(PUT是完整更新)
        - 验证数据后保存更新
        """
        try:
            # 自动获取url中的pk,根据pk获取具体的地址对象
            address_info = self.get_object()
            # 从数据库中获取的现有对象address_info,告诉序列化器:"用户想要将address_info更新成request.data"
            serializer = self.get_serializer(instance=address_info, data=request.data)
            # 验证数据有效性
            serializer.is_valid(raise_exception=True)
            serializer.save()

            return  APIResponse.success(message="地址更新成功!")

        except Exception as e:
            return  APIResponse.error(message="地址更新失败",errors=[{
                    'errmsg': str(e)
                }])


    # 删除某条地址: DELETE `/address/{pk}`
    def delete(self, request,pk):
        try:
            address_info = self.get_object()  # 自动从URL获取pk
            address_info.delete()
            return APIResponse.success(message="地址删除成功!")
        except UserAddress.DoesNotExist:
            return APIResponse.error(message="地址删除失败:地址不存在!")
        except Exception as e:
            return APIResponse.error(message="地址删除失败",errors=[{
                    'errmsg': str(e)
                }])


class AddressListGenericAPIView(GenericAPIView):

    queryset = UserAddress.objects.all()
    serializer_class = AddressSerializer

    # 查询当前所有地址列表: GET `/address/list`
    def get(self,request):
        try:
            address_list = self.get_queryset()
            result = self.get_serializer(instance=address_list,many=True)

            return APIResponse.success(data=result.data,message="地址列表获取成功!")
        except Exception as e:
            # 异常处理
            return APIResponse.error(
                message="获取地址列表失败",
                errors=[{
                    'errmsg': str(e)
                }]
            )

序列化器

python
from rest_framework import serializers

from apps.address.models import UserAddress


class AddressSerializer(serializers.ModelSerializer):

    class Meta:
        model = UserAddress
        fields = '__all__'

路由

python
from django.urls import path, re_path

from apps.address.views import AddressGenericAPIView, AddressListGenericAPIView

urlpatterns = [
    path('', AddressGenericAPIView.as_view()),
    path('list/', AddressListGenericAPIView.as_view()),
    re_path('(?P<pk>.*)', AddressGenericAPIView.as_view()),
]

用户模块开发(/apps/xxx/)

整体规划

  • 查看个人信息: GET /user/detail
  • 用户注册: POST /user/register
  • 用户登录: POST /user/login

视图开发

python
from django.db import transaction
from django.shortcuts import render
from rest_framework.views import APIView

from apps.user.models import User
from apps.user.serializers import UserSerializer
from utils.BaseResponse import APIResponse


# Create your views here.
class UserAPIView(APIView):

    # 查看个人信息: GET `/user?email={email}`
    def get(self, request):
        email = request.GET.get("email")
        try:
            user_info = User.objects.get(email=email)
            result = UserSerializer(instance=user_info)
            return APIResponse.success(data=result.data, message="个人信息查询成功")
        except User.DoesNotExist:
            return APIResponse.error(message="个人信息查询失败,用户不存在!")


    # 用户注册: POST `/user`
    def post(self, request):
        """
        用户注册接口
        - 验证用户数据
        - 创建用户账户
        - 返回创建的用户信息
        """

        # 复制数据以避免修改原始请求数据
        request_data = request.data.copy()
        try:
            # 验证请求数据
            serializer = UserSerializer(data=request_data)
            serializer.is_valid(raise_exception=True)
            # 创建用户(使用事务保证原子性)
            with transaction.atomic():
                user = serializer.save()

            # 返回前端
            result = UserSerializer(instance=user)
            return APIResponse.success(data=result.data, message="注册成功")

        except Exception as e:
            # 记录日志(实际项目中应该添加日志记录)
            # logger.error(f"用户注册失败: {str(e)}")

            return APIResponse.error(
                message="注册失败,请稍后重试",
                errors=[{"errmsg": str(e)}],
            )

信息加密

python
# apps/user/pwd_encoder.py
import hashlib

# 明文密码md5加密
def get_md5(password):
    md5 = hashlib.md5()
    md5.update(password.encode())
    return md5.hexdigest()

序列化器

python
from rest_framework import serializers
from rest_framework.validators import UniqueValidator

from apps.user.models import User
from apps.user.pwd_encoder import get_md5
import datetime



class UserSerializer(serializers.ModelSerializer):
    # email作为账户名, 需要唯一性验证
    email = serializers.EmailField(
        # 必须
        required=True,
        # 不允许空
        allow_null=False,
        # 唯一性校验
        validators=[UniqueValidator(queryset=User.objects.all(), message="用户已存在")]
    )

    # 时间格式化
    birthday = serializers.DateTimeField('%Y-%m-%d %H:%M:%S')
    create_time = serializers.DateTimeField('%Y-%m-%d %H:%M:%S', required=False)

    # 在调用save()时自动被调用,在数据存储之前进行加工
    def create(self, validated_data):
        # 密码md5加密
        validated_data['password'] = get_md5(validated_data['password'])

        # 取当前时间为注册时间
        validated_data['create_time'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')

        result = User.objects.create(**validated_data)
        return result

    class Meta:
        model = User
        fields = "__all__"
        # 创建用户后密码不返回给前端
        extra_kwargs = {'password': {'write_only': True}}

路由

python
from django.urls import path

from apps.user.views import UserAPIView

urlpatterns = [
    path('', UserAPIView.as_view(), name="user"),
]

商品评论模块开发(/apps/comment/)

整体规划

视图开发

python
from django.shortcuts import render
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin
from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet

from apps.comment.models import Comment
from apps.comment.serializers import CommentSerializer


# Create your views here.
class CommentGenericAPIView(ModelViewSet, ReadOnlyModelViewSet):
    queryset = Comment.objects
    serializer_class = CommentSerializer

    
    def single(self, request, pk):
        print("我是查询一个")
        return self.retrieve(request, pk)

    def my_list(self, request):
        print("我是查询多个")
        return self.list(request)

    def edit(self, request, pk):
        print("更新")
        return self.update(request, pk)

    def my_save(self, request):
        print("我是保存")
        return self.create(request)

    def my_delete(self, request, pk):
        print("我是删除")
        return self.destroy(request, pk)

序列化器

python
from rest_framework import serializers

from apps.address.models import UserAddress
from apps.comment.models import Comment


class CommentSerializer(serializers.ModelSerializer):


    class Meta:
        model = Comment
        fields = '__all__'

路由

python
from django.urls import path, re_path

from apps.address.views import AddressGenericAPIView, AddressListGenericAPIView
from apps.comment.views import CommentGenericAPIView

urlpatterns = [
    path('', CommentGenericAPIView.as_view({
            "get":"my_list",
        }
    )),
    re_path('(?P<pk>.*)', CommentGenericAPIView.as_view({
            "get":"single"
        })),
]

xxx 模块开发(/apps/xxx/)

功能展示

整体规划

视图开发

python

序列化器

python

路由

python

xxx 模块开发(/apps/xxx/)

功能展示

整体规划

视图开发

python

序列化器

python

路由

python

xxx 模块开发(/apps/xxx/)

功能展示

整体规划

视图开发

python

序列化器

python

路由

python

Released under the MIT License.