Skip to content

Django项目记录

跨域问题

浏览器跨域(Cross-Origin Resource Sharing, CORS)是浏览器实施的一种安全机制,它限制从一个源(域、协议、端口)加载的网页脚本与来自另一个源的资源进行交互。这是为了防止恶意网站窃取数据。

同源策略要求三个一致:

  • 协议相同(http/https)
  • 域名相同
  • 端口相同

Django跨域问题表现

在Django开发中,跨域问题通常会表现为:

  1. 浏览器控制台出现CORS错误消息:

    bash
    Access to fetch at 'http://api.example.com' from origin 'http://localhost:3000' has been blocked by CORS policy
  2. 前端JavaScript代码无法成功向不同源的Django后端发送请求

  3. 预检请求(OPTIONS)失败,返回403405状态码

跨域问题解决方案

  1. 安装django-cors-headers:

    python
    pip install django-cors-headers
  2. 配置settings.py:

    • 添加'corsheaders'INSTALLED_APPS
    • 添加'corsheaders.middleware.CorsMiddleware'MIDDLEWARE的开头
    • 添加'CORS_ORIGIN_ALLOW_ALL = True'允许全部跨域
    • 配置CORS_ALLOWED_ORIGINS设置允许的源
    • (可选)设置CORS_ALLOW_CREDENTIALSTrue如果需要传递Cookie
    • (可选)配置CORS_ALLOW_METHODS指定允许的HTTP方法
    • (可选)配置CORS_ALLOW_HEADERS指定允许的HTTP

json序列化

手动序列化

python
# models.py

class MainMenu(models.Model):
    main_menu_id = models.IntegerField()
    main_menu_name = models.CharField(max_length=255)
    main_menu_url = models.CharField(max_length=255, blank=True, null=True)

    # 重写__str__方法, 新建一个字典,将所有字段都放在字典中,将字典传出
    def __str__(self):
        result = {
            "main_menu_id": self.main_menu_id,
            "main_menu_name": self.main_menu_name,
            "main_menu_url": self.main_menu_url,
        }
        return json.dumps(result, ensure_ascii=False)

    class Meta:
        managed = False
        db_table = 'main_menu'
python
# views.py
class GoodsMainMenu(View):
    def get(self, request):
        # 获取MainMenu表中的所有行
        main_menu = MainMenu.objects.all()

        result_list = []
        # 对每一行数据调用序列化方法, 转换成json格式
        for m in main_menu:
            result_list.append(m.__str__())
        return HttpResponse(result_list)

DRF序列化器

decimal类型序列化

在数据库中有表示价格的字段在json转换时会报以下错误:

bash
TypeError: object of type Decimal is not JSON serializable
  • 方式一:手动对decimal格式的数据做json转换
python
# models.py
class Goods(models.Model):
    type_id = models.IntegerField(blank=True, null=True)
    name = models.CharField(max_length=255, blank=True, null=True)
    p_price = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
    create_time = models.DateTimeField(blank=True, null=True)

    def __str__(self):
        result = {
            'type_id': self.type_id,
            'name': self.name,
            'p_price': self.p_price,
            'create_time': self.create_time,
        }
        # `cls=DecimalEncoder` 用于转换decimal格式数据
        return json.dumps(result, cls=DecimalEncoder, ensure_ascii=False)   
    class Meta:
        managed = False
        db_table = 'goods'

# 针对decimal格式做json转换(固定写法)
class DecimalEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, Decimal):
            return float(o)
        return None
python
# views.py
class GoodsCategoryAPIView(APIView):
    def get(self, request):
        category_data = Goods.objects.all()

        result_list = []
        for c in category_data:
            result_list.append(c.__str__())
        return HttpResponse(result_list)
  • 方式二:使用DRF序列化器
python
# serializers.py
class GoodsSerializer(serializers.ModelSerializer):
    # 这里写的字段是想要进行自定义处理的字段

    p_price = serializers.SerializerMethodField()

    # 自定义序列化方法时,方法名必须是 get_<字段名>
    def get_p_price(self, obj):
        return float(obj.p_price)

    class Meta:
        model = Goods
        fields = '__all__'
python
# views.py
class GoodsCategoryAPIView(APIView):
    def get(self, request):
        category_data = Goods.objects.all()
        # 序列化时参数是instance , 反序列化时参数是data
        result = GoodsSerializer(instance=category_data)

        return HttpResponse(result)

datetime类型序列化

python
# serializers.py
class 模型名Serializer(serializers.ModelSerializer):

    # 自定义时间格式
    create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)

    class Meta:
        model = 模型名
        fields = '__all__'
python
# views.py
class 视图类名(APIView):
    def get(self, request):
        data = 模型名.objects.all()
        # 序列化时参数是instance , 反序列化时参数是data
        result = 模型名Serializer(instance=data)

        return HttpResponse(result)

统一Response

python
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)

密码加密

Last Updated: