Django项目记录
跨域问题
浏览器跨域(Cross-Origin Resource Sharing, CORS)是浏览器实施的一种安全机制,它限制从一个源(域、协议、端口)加载的网页脚本与来自另一个源的资源进行交互。这是为了防止恶意网站窃取数据。
同源策略要求三个一致:
- 协议相同(http/https)
- 域名相同
- 端口相同
Django跨域问题表现
在Django开发中,跨域问题通常会表现为:
浏览器控制台出现
CORS
错误消息:bashAccess to fetch at 'http://api.example.com' from origin 'http://localhost:3000' has been blocked by CORS policy
前端JavaScript代码无法成功向不同源的Django后端发送请求
预检请求(
OPTIONS
)失败,返回403
或405
状态码
跨域问题解决方案
安装
django-cors-headers
:pythonpip install django-cors-headers
配置
settings.py
:- 添加
'corsheaders'
到INSTALLED_APPS
- 添加
'corsheaders.middleware.CorsMiddleware'
到MIDDLEWARE
的开头 - 添加
'CORS_ORIGIN_ALLOW_ALL = True'
允许全部跨域 - 配置
CORS_ALLOWED_ORIGINS
设置允许的源 - (可选)设置
CORS_ALLOW_CREDENTIALS
为True
如果需要传递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)