django模型过滤视图

Posted by Shallow Dreameron June 3, 2024

当然,以下是每一行代码的详细解释,帮助你理解如何实现多表联合查询并进行过滤。

1. 定义模型

your_app/models.py 中定义 AuthorBook 模型:

# your_app/models.py

from django.db import models

# 定义Author模型
class Author(models.Model):
    # 定义作者姓名字段,最大长度为100
    name = models.CharField(max_length=100)
    # 定义作者出生日期字段
    birth_date = models.DateField()

    # 定义字符串表示形式,返回作者姓名
    def __str__(self):
        return self.name

# 定义Book模型
class Book(models.Model):
    # 定义书名字段,最大长度为200
    title = models.CharField(max_length=200)
    # 定义外键字段,关联到Author模型,删除Author时删除关联的Book
    author = models.ForeignKey(Author, related_name='books', on_delete=models.CASCADE)
    # 定义书籍出版日期字段
    published_date = models.DateField()

    # 定义字符串表示形式,返回书名
    def __str__(self):
        return self.title

2. 创建序列化器

your_app/serializers.py 中创建两个模型的序列化器:

# your_app/serializers.py

from rest_framework import serializers
from .models import Author, Book

# 定义Author模型的序列化器
class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        # 指定序列化器对应的模型是Author
        model = Author
        # 指定需要序列化的字段
        fields = ['id', 'name', 'birth_date']

# 定义Book模型的序列化器
class BookSerializer(serializers.ModelSerializer):
    # 嵌套Author序列化器,用于序列化Author字段
    author = AuthorSerializer()

    class Meta:
        # 指定序列化器对应的模型是Book
        model = Book
        # 指定需要序列化的字段
        fields = ['id', 'title', 'author', 'published_date']

3. 创建过滤类

your_app/filters.py 中创建过滤类:

# your_app/filters.py

import django_filters
from .models import Book

# 定义Book模型的过滤类
class BookFilter(django_filters.FilterSet):
    # 定义过滤器,按书名模糊查询
    title = django_filters.CharFilter(lookup_expr='icontains')
    # 定义过滤器,按作者姓名模糊查询
    author_name = django_filters.CharFilter(field_name='author__name', lookup_expr='icontains')
    # 定义过滤器,按出版日期精确查询
    published_date = django_filters.DateFilter()

    class Meta:
        # 指定过滤器对应的模型是Book
        model = Book
        # 指定可以被过滤的字段
        fields = ['title', 'author_name', 'published_date']

4. 创建视图

your_app/views.py 中创建视图,并添加过滤功能:

# your_app/views.py

from rest_framework import viewsets
from django_filters.rest_framework import DjangoFilterBackend
from .models import Book
from .serializers import BookSerializer
from .filters import BookFilter

# 定义Book模型的视图集
class BookViewSet(viewsets.ModelViewSet):
    # 指定视图集使用的查询集
    queryset = Book.objects.all()
    # 指定视图集使用的序列化器
    serializer_class = BookSerializer
    # 指定视图集使用的过滤后端
    filter_backends = [DjangoFilterBackend]
    # 指定视图集使用的过滤类
    filterset_class = BookFilter

5. 配置 URL

your_app/urls.py 中配置路由:

# your_app/urls.py

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import BookViewSet

# 创建一个默认的路由对象
router = DefaultRouter()
# 注册Book视图集,路由前缀为'books'
router.register(r'books', BookViewSet, basename='book')

# 定义URL模式
urlpatterns = [
    # 包含路由对象生成的所有URL模式
    path('', include(router.urls)),
]

在项目的 urls.py 中包含应用的 URL:

# your_project/urls.py

from django.contrib import admin
from django.urls import path, include

# 定义项目级别的URL模式
urlpatterns = [
    # 管理后台的URL模式
    path('admin/', admin.site.urls),
    # 包含应用的URL模式,前缀为'api/'
    path('api/', include('your_app.urls')),
]

6. 运行项目

启动开发服务器:

python manage.py runserver

总结

访问 http://127.0.0.1:8000/api/books/,并使用以下参数进行过滤,例如:

  • http://127.0.0.1:8000/api/books/?title=some_title:按书名模糊查询
  • http://127.0.0.1:8000/api/books/?author_name=some_author:按作者姓名模糊查询
  • http://127.0.0.1:8000/api/books/?published_date=2023-01-01:按出版日期精确查询

示例

假设数据库中有以下数据:

  • Author: J.K. Rowling, Birth Date: 1965-07-31
  • Book: Harry Potter and the Philosopher’s Stone, Author: J.K. Rowling, Published Date: 1997-06-26

你可以通过访问 http://127.0.0.1:8000/api/books/?author_name=rowling 来获取作者名中包含 “rowling” 的所有书籍。返回的结果将包含 “Harry Potter and the Philosopher’s Stone” 这本书。

通过这个示例,你可以了解如何在视图中实现多表联合查询并进行过滤。希望这对你有所帮助!如果你有任何问题或需要进一步的解释,请随时告诉我。


当然,下面是每行代码的详细解释。

1. 定义模型

your_app/models.py 中定义 Book 模型:

# your_app/models.py

from django.db import models

# 定义 Book 模型
class Book(models.Model):
    # 定义书名字段,最大长度为200
    title = models.CharField(max_length=200)
    # 定义作者字段,最大长度为100
    author = models.CharField(max_length=100)
    # 定义出版日期字段
    published_date = models.DateField()

    # 定义字符串表示形式,返回书名
    def __str__(self):
        return self.title

2. 创建过滤类

your_app/filters.py 中创建过滤类:

# your_app/filters.py

import django_filters
from django_filters import rest_framework as filters
from .models import Book

# 定义一个用于处理字符类型列表的自定义过滤器
class CharInFilter(filters.BaseInFilter, filters.CharFilter):
    pass

# 定义 Book 模型的过滤器类
class BookFilter(filters.FilterSet):
    # 使用自定义过滤器来处理书名的列表过滤
    title = CharInFilter(field_name='title', lookup_expr='in')
    # 使用自定义过滤器来处理作者的列表过滤
    author = CharInFilter(field_name='author', lookup_expr='in')
    # 使用标准日期过滤器来处理出版日期的精确过滤
    published_date = filters.DateFilter()

    class Meta:
        # 指定过滤器对应的模型是 Book
        model = Book
        # 指定可以被过滤的字段
        fields = ['title', 'author', 'published_date']

3. 创建视图以处理 XLSX 文件

your_app/views.py 中创建一个视图来处理 XLSX 文件,并检查数据库中是否存在这些数据:

# your_app/views.py

import pandas as pd
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django_filters.rest_framework import DjangoFilterBackend
from .models import Book
from .filters import BookFilter

# 定义一个处理 XLSX 文件上传的视图
class UploadXLSXView(APIView):
    # 指定使用 DjangoFilterBackend 作为过滤后端
    filter_backends = [DjangoFilterBackend]
    # 指定使用的过滤器类
    filterset_class = BookFilter

    # 处理 POST 请求的方法
    def post(self, request, *args, **kwargs):
        # 从请求中获取上传的文件
        file = request.FILES.get('file')
        if not file:
            # 如果没有提供文件,返回400错误响应
            return Response({"error": "No file provided"}, status=status.HTTP_400_BAD_REQUEST)

        # 使用 Pandas 读取 XLSX 文件
        df = pd.read_excel(file)

        # 期望的列名
        expected_columns = ['title', 'author', 'published_date']
        # 检查文件中是否包含所有期望的列
        if not all(column in df.columns for column in expected_columns):
            # 如果缺少任何期望的列,返回400错误响应
            return Response({"error": "Invalid file format"}, status=status.HTTP_400_BAD_REQUEST)

        # 获取所有标题、作者和出版日期,转换为列表
        titles = df['title'].tolist()
        authors = df['author'].tolist()
        published_dates = df['published_date'].tolist()

        # 创建过滤器集实例,用于过滤数据库中的数据
        filterset = BookFilter(data={
            'title': titles,
            'author': authors,
            'published_date': published_dates
        }, queryset=Book.objects.all())

        results = []
        if filterset.is_valid():
            # 如果过滤器集有效,逐行检查数据是否存在于数据库中
            for title, author, published_date in zip(titles, authors, published_dates):
                # 使用过滤器集查询数据库
                exists = filterset.qs.filter(title=title, author=author, published_date=published_date).exists()
                # 将结果添加到结果列表中
                results.append({
                    "title": title,
                    "author": author,
                    "published_date": published_date,
                    "exists": exists
                })
        else:
            # 如果过滤器集无效,返回400错误响应
            return Response(filterset.errors, status=status.HTTP_400_BAD_REQUEST)

        # 返回结果列表作为响应
        return Response(results, status=status.HTTP_200_OK)

4. 配置 URL

your_app/urls.py 中添加新视图的路由:

# your_app/urls.py

from django.urls import path
from .views import UploadXLSXView

# 定义应用的URL模式
urlpatterns = [
    # 配置处理 XLSX 文件上传的视图
    path('upload/', UploadXLSXView.as_view(), name='upload-xlsx'),
]

5. 更新项目的 URL 配置

在项目的 urls.py 中包含应用的 URL:

# your_project/urls.py

from django.contrib import admin
from django.urls import path, include

# 定义项目级别的URL模式
urlpatterns = [
    # 管理后台的URL模式
    path('admin/', admin.site.urls),
    # 包含应用的URL模式,前缀为'api/'
    path('api/', include('your_app.urls')),
]

6. 启动项目

启动开发服务器:

python manage.py runserver

使用说明

http://127.0.0.1:8000/api/upload/ 发起 POST 请求,上传包含 titleauthorpublished_date 列的 XLSX 文件。视图将检查这些数据是否在数据库中存在,并返回检查结果。

示例 XLSX 文件格式

title author published_date
Book Title 1 Author Name 1 2023-01-01
Book Title 2 Author Name 2 2023-01-02
Book Title 3 Author Name 3 2023-01-03

代码详细解释

  1. 模型定义
    • Book 模型定义了书籍的基本信息:书名、作者和出版日期。
  2. 过滤类
    • CharInFilter 是一个自定义过滤器,用于处理字符类型的列表。
    • BookFilter 定义了过滤 Book 模型的字段:书名、作者和出版日期。
  3. 视图定义
    • UploadXLSXView 是一个 API 视图,用于处理 XLSX 文件上传。
    • post 方法处理上传的文件,读取 XLSX 文件内容,验证列名,并使用过滤器集检查数据是否存在于数据库中。
    • filterset 用于创建一个过滤器集实例,使用上传的数据进行查询。
  4. URL 配置
    • your_app/urls.py 配置了 UploadXLSXView 视图的路由。
    • your_project/urls.py 包含应用的 URL 配置,确保路由正确。

通过这个示例,你可以了解如何使用 BaseInFilterdjango-filter 来处理从 XLSX 文件中获取的数据,并检查这些数据是否存在于数据库中。如果你有任何问题或需要进一步的解释,请随时告诉我。


为了实现从 BookColumn 表中获取校验规则并应用于前端传递的数据或 XLSX 文件中的数据,首先我们需要在 BookColumn 表中存储这些校验规则。然后,我们需要在视图中读取这些规则,并在处理数据时应用这些规则进行校验。下面是一个完整的示例代码,包括定义模型、视图和校验逻辑。

1. 定义模型

首先,定义 BookBookColumn 模型:

# your_app/models.py

from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.CharField(max_length=100)
    published_date = models.DateField()

    def __str__(self):
        return self.title

class BookColumn(models.Model):
    FIELD_TYPE_CHOICES = (
        ('char', 'CharField'),
        ('date', 'DateField'),
    )

    name = models.CharField(max_length=100)  # 字段名
    field_type = models.CharField(max_length=10, choices=FIELD_TYPE_CHOICES)  # 字段类型
    regex = models.CharField(max_length=200, blank=True, null=True)  # 正则表达式校验
    required = models.BooleanField(default=True)  # 是否必填

    def __str__(self):
        return self.name

2. 创建过滤类

如果需要的话,可以继续使用过滤类。这里我们不改变之前的过滤逻辑。

# your_app/filters.py

import django_filters
from django_filters import rest_framework as filters
from .models import Book

class CharInFilter(filters.BaseInFilter, filters.CharFilter):
    pass

class BookFilter(filters.FilterSet):
    title = CharInFilter(field_name='title', lookup_expr='in')
    author = CharInFilter(field_name='author', lookup_expr='in')
    published_date = filters.DateFilter()

    class Meta:
        model = Book
        fields = ['title', 'author', 'published_date']

3. 创建视图以处理校验

your_app/views.py 中创建一个视图来处理数据校验:

# your_app/views.py

import pandas as pd
import re
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import Book, BookColumn

class UploadXLSXView(APIView):

    def post(self, request, *args, **kwargs):
        file = request.FILES.get('file')
        if not file:
            return Response({"error": "No file provided"}, status=status.HTTP_400_BAD_REQUEST)

        # 读取 XLSX 文件
        df = pd.read_excel(file)

        # 获取所有的校验规则
        validation_rules = BookColumn.objects.all()
        errors = []

        for index, row in df.iterrows():
            row_errors = {}
            for rule in validation_rules:
                field_name = rule.name
                field_value = row.get(field_name)

                # 校验必填项
                if rule.required and (field_value is None or pd.isna(field_value)):
                    row_errors[field_name] = 'This field is required.'
                    continue

                # 正则表达式校验
                if rule.regex and field_value:
                    if not re.match(rule.regex, str(field_value)):
                        row_errors[field_name] = 'Invalid format.'

            if row_errors:
                errors.append({"row": index + 1, "errors": row_errors})

        if errors:
            return Response({"errors": errors}, status=status.HTTP_400_BAD_REQUEST)

        # 如果没有错误,处理并保存数据
        results = []
        for index, row in df.iterrows():
            book_data = {
                'title': row['title'],
                'author': row['author'],
                'published_date': row['published_date']
            }
            # 在数据库中创建或更新 Book 实例
            book, created = Book.objects.update_or_create(
                title=book_data['title'],
                defaults=book_data
            )
            results.append(book_data)

        return Response(results, status=status.HTTP_200_OK)

4. 配置 URL

your_app/urls.py 中添加新视图的路由:

# your_app/urls.py

from django.urls import path
from .views import UploadXLSXView

urlpatterns = [
    path('upload/', UploadXLSXView.as_view(), name='upload-xlsx'),
]

5. 更新项目的 URL 配置

在项目的 urls.py 中包含应用的 URL:

# your_project/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('your_app.urls')),
]

6. 启动项目

启动开发服务器:

python manage.py runserver

使用说明

http://127.0.0.1:8000/api/upload/ 发起 POST 请求,上传包含 titleauthorpublished_date 列的 XLSX 文件。视图将根据 BookColumn 表中的校验规则检查这些数据,如果有错误将返回错误信息。

示例 XLSX 文件格式

title author published_date
Book Title 1 Author Name 1 2023-01-01
Book Title 2 Author Name 2 2023-01-02
Book Title 3 Author Name 3 2023-01-03

代码详细解释

  1. 模型定义
    • Book 模型定义了书籍的基本信息:书名、作者和出版日期。
    • BookColumn 模型定义了字段的校验规则,包括字段名、字段类型、正则表达式和是否必填。
  2. 视图定义
    • UploadXLSXView 是一个 API 视图,用于处理 XLSX 文件上传。
    • post 方法处理上传的文件,读取 XLSX 文件内容,获取校验规则,并应用这些规则对数据进行校验。
    • validation_rules 获取所有的校验规则。
    • 对每一行数据,根据校验规则检查字段值是否符合要求,如果有错误,记录错误信息。
    • 如果有错误,返回400错误响应。
    • 如果没有错误,处理并保存数据。

通过这个示例,你可以了解如何使用从 BookColumn 表中获取的校验规则,对前端传递的数据或 XLSX 文件中的数据进行校验。如果你有任何问题或需要进一步的解释,请随时告诉我。


为了实现这个功能,我们需要以下步骤:

  1. 创建 BookColumn 模型 用于存储字段的校验信息。
  2. 创建一个函数,从 BookColumn 表中获取校验信息,并对传递的数据或从 XLSX 文件中读取的数据进行校验。
  3. 在视图中使用这个校验函数,对数据进行校验并返回错误信息。

1. 创建 BookColumn 模型

your_app/models.py 中定义 BookColumn 模型:

from django.db import models

# 定义 Book 模型
class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.CharField(max_length=100)
    published_date = models.DateField()

    def __str__(self):
        return self.title

# 定义 BookColumn 模型
class BookColumn(models.Model):
    field_name = models.CharField(max_length=100)  # Book 模型中的字段名
    validation_type = models.CharField(max_length=50)  # 校验类型(如 regex, max_length, min_value)
    validation_value = models.CharField(max_length=200)  # 校验值(如正则表达式)

    def __str__(self):
        return f'{self.field_name} - {self.validation_type}'

2. 创建数据校验函数

your_app/validators.py 中创建校验函数:

import re
from django.core.exceptions import ValidationError
from .models import BookColumn

def validate_data(data):
    errors = []
    columns = BookColumn.objects.all()
    
    for column in columns:
        field_name = column.field_name
        validation_type = column.validation_type
        validation_value = column.validation_value

        value = data.get(field_name)
        
        if validation_type == 'regex':
            if not re.match(validation_value, str(value)):
                errors.append(f'{field_name} does not match the required pattern.')
        
        elif validation_type == 'max_length':
            if len(str(value)) > int(validation_value):
                errors.append(f'{field_name} exceeds maximum length of {validation_value}.')
        
        elif validation_type == 'min_length':
            if len(str(value)) < int(validation_value):
                errors.append(f'{field_name} is less than minimum length of {validation_value}.')
        
        elif validation_type == 'max_value':
            if float(value) > float(validation_value):
                errors.append(f'{field_name} exceeds maximum value of {validation_value}.')
        
        elif validation_type == 'min_value':
            if float(value) < float(validation_value):
                errors.append(f'{field_name} is less than minimum value of {validation_value}.')

    return errors

3. 在视图中使用校验函数

修改 your_app/views.py,在处理数据时进行校验:

import pandas as pd
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django_filters.rest_framework import DjangoFilterBackend
from .models import Book
from .filters import BookFilter
from .validators import validate_data

class UploadXLSXView(APIView):
    filter_backends = [DjangoFilterBackend]
    filterset_class = BookFilter

    def post(self, request, *args, **kwargs):
        file = request.FILES.get('file')
        if not file:
            return Response({"error": "No file provided"}, status=status.HTTP_400_BAD_REQUEST)

        df = pd.read_excel(file)
        expected_columns = ['title', 'author', 'published_date']
        if not all(column in df.columns for column in expected_columns):
            return Response({"error": "Invalid file format"}, status=status.HTTP_400_BAD_REQUEST)

        titles = df['title'].tolist()
        authors = df['author'].tolist()
        published_dates = df['published_date'].tolist()

        results = []
        for index, row in df.iterrows():
            row_data = row.to_dict()
            errors = validate_data(row_data)
            if errors:
                results.append({
                    "row": index + 1,
                    "errors": errors
                })
                continue
            
            filterset = BookFilter(data=row_data, queryset=Book.objects.all())
            if filterset.is_valid():
                exists = filterset.qs.filter(title=row_data['title'], author=row_data['author'], published_date=row_data['published_date']).exists()
                results.append({
                    "title": row_data['title'],
                    "author": row_data['author'],
                    "published_date": row_data['published_date'],
                    "exists": exists
                })
            else:
                results.append({
                    "row": index + 1,
                    "errors": filterset.errors
                })

        return Response(results, status=status.HTTP_200_OK)

4. 配置 URL

your_app/urls.py 中添加新视图的路由:

from django.urls import path
from .views import UploadXLSXView

urlpatterns = [
    path('upload/', UploadXLSXView.as_view(), name='upload-xlsx'),
]

5. 更新项目的 URL 配置

在项目的 urls.py 中包含应用的 URL:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('your_app.urls')),
]

6. 启动项目

启动开发服务器:

python manage.py runserver

使用说明

http://127.0.0.1:8000/api/upload/ 发起 POST 请求,上传包含 titleauthorpublished_date 列的 XLSX 文件。视图将校验这些数据是否符合 BookColumn 表中的校验规则,并返回错误信息。

示例 XLSX 文件格式

title author published_date
Book Title 1 Author Name 1 2023-01-01
Book Title 2 Author Name 2 2023-01-02
Book Title 3 Author Name 3 2023-01-03

示例 BookColumn 数据

field_name validation_type validation_value
title max_length 200
author max_length 100
published_date regex ^\d{4}-\d{2}-\d{2}$

通过这个示例,你可以了解如何从 XLSX 文件中获取数据,并使用存储在 BookColumn 表中的校验规则对这些数据进行校验。如果你有任何问题或需要进一步的解释,请随时告诉我。


如果不需要自动解析,而是需要在序列化器中进行解析和校验,可以在序列化器的 validate 方法中实现。这样可以对数据进行校验,然后将解析后的数据存储到 JSON 字段中。以下是如何实现这一需求的完整示例代码:

1. 修改模型

首先,确保模型中包含一个 JSON 字段来存储解析后的数据:

# your_app/models.py

from django.db import models
from django.contrib.postgres.fields import JSONField  # 使用 PostgreSQL 的 JSONField

class YourModel(models.Model):
    file_name = models.CharField(max_length=255)
    file_metadata = JSONField(blank=True, null=True)  # JSON 字段

    def __str__(self):
        return self.file_name

2. 创建序列化器

在序列化器中进行解析和校验,并将解析后的数据存储在 file_metadata 字段中:

# your_app/serializers.py

import re
from rest_framework import serializers
from .models import YourModel

class YourModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = YourModel
        fields = ['id', 'file_name', 'file_metadata']

    def validate_file_name(self, value):
        pattern = r'\[(.*?)\]-(.*)\.s4p'
        match = re.match(pattern, value)
        if not match:
            raise serializers.ValidationError("Invalid file name format.")
        
        details = match.group(1).split('_')
        type_ = details[0]
        metadata = {"type": type_}

        if type_ == 'thru':
            metadata["channel"] = details[1]
        elif type_ == 'fext':
            metadata["victim_pair"] = details[1]
            metadata["attacker_pair"] = details[3]
        else:
            metadata["details"] = details[1:]

        # 将解析后的数据存储到 file_metadata 中
        self.context['file_metadata'] = metadata

        return value

    def create(self, validated_data):
        file_metadata = self.context.get('file_metadata')
        instance = YourModel.objects.create(
            file_name=validated_data['file_name'],
            file_metadata=file_metadata
        )
        return instance

    def update(self, instance, validated_data):
        instance.file_name = validated_data.get('file_name', instance.file_name)
        instance.file_metadata = self.context.get('file_metadata', instance.file_metadata)
        instance.save()
        return instance

3. 创建视图

使用 Django REST Framework 的视图来处理 API 请求:

# your_app/views.py

from rest_framework import viewsets
from .models import YourModel
from .serializers import YourModelSerializer

class YourModelViewSet(viewsets.ModelViewSet):
    queryset = YourModel.objects.all()
    serializer_class = YourModelSerializer

4. 配置 URL

将视图添加到 URL 配置中:

# your_app/urls.py

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import YourModelViewSet

router = DefaultRouter()
router.register(r'yourmodel', YourModelViewSet)

urlpatterns = [
    path('', include(router.urls)),
]

5. 启动项目

启动开发服务器:

python manage.py runserver

使用说明

现在,当你通过 API 创建或更新 YourModel 实例时,文件名将通过序列化器进行解析和校验,解析出来的数据将被存储在 file_metadata JSON 字段中。

示例请求和响应

假设你有一个 YourModel 实例,其 file_name 字段为 [thru_rx1]-xxx.s4p。通过 API 创建或更新这个实例时,你会看到:

{
    "id": 1,
    "file_name": "[thru_rx1]-xxx.s4p",
    "file_metadata": {
        "type": "thru",
        "channel": "rx1"
    }
}

如果 file_name[fext_V_V1_A_A1]-xxx.s4p,你会看到:

{
    "id": 2,
    "file_name": "[fext_V_V1_A_A1]-xxx.s4p",
    "file_metadata": {
        "type": "fext",
        "victim_pair": "V1",
        "attacker_pair": "A1"
    }
}

通过这种方式,你可以在序列化器中对数据进行解析和校验,并灵活地存储解析后的数据。如果有新的格式,只需更新序列化器中的解析逻辑即可。


要在序列化器中为每个字段动态添加自定义校验类,你可以在序列化器的 validate 方法中根据配置或其他动态数据添加校验逻辑。以下是如何实现这一需求的完整示例代码:

1. 修改模型

首先,确保模型中包含所需的字段:

# your_app/models.py

from django.db import models
from django.contrib.postgres.fields import JSONField  # 使用 PostgreSQL 的 JSONField

class YourModel(models.Model):
    file_name = models.CharField(max_length=255)
    file_metadata = JSONField(blank=True, null=True)  # JSON 字段

    def __str__(self):
        return self.file_name

2. 创建动态校验类

创建一个校验类,用于根据字段名称和规则进行校验:

# your_app/validators.py

import re
from rest_framework import serializers

class DynamicFieldValidator:
    def __init__(self, field_name, validation_rules):
        self.field_name = field_name
        self.validation_rules = validation_rules

    def __call__(self, value):
        for rule in self.validation_rules:
            if rule['type'] == 'regex':
                if not re.match(rule['pattern'], value):
                    raise serializers.ValidationError(f"{self.field_name} does not match pattern {rule['pattern']}")
            # 可以添加更多规则类型
            # elif rule['type'] == 'min_length':
            #     if len(value) < rule['value']:
            #         raise serializers.ValidationError(f"{self.field_name} length is less than {rule['value']}")

3. 修改序列化器

在序列化器中应用动态校验逻辑:

# your_app/serializers.py

import re
from rest_framework import serializers
from .models import YourModel
from .validators import DynamicFieldValidator

class YourModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = YourModel
        fields = ['id', 'file_name', 'file_metadata']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.dynamic_validators = {
            'file_name': [DynamicFieldValidator('file_name', [{'type': 'regex', 'pattern': r'\[.*?\]-.*\.s4p'}])]
        }
        # 可以根据需要添加更多字段和其校验规则

    def validate(self, data):
        # 逐字段应用动态校验
        for field, validators in self.dynamic_validators.items():
            value = data.get(field)
            if value is not None:
                for validator in validators:
                    validator(value)

        # 自定义解析逻辑
        pattern = r'\[(.*?)\]-(.*)\.s4p'
        match = re.match(pattern, data['file_name'])
        if match:
            details = match.group(1).split('_')
            type_ = details[0]
            metadata = {"type": type_}
            if type_ == 'thru':
                metadata["channel"] = details[1]
            elif type_ == 'fext':
                metadata["victim_pair"] = details[1]
                metadata["attacker_pair"] = details[3]
            else:
                metadata["details"] = details[1:]
            data['file_metadata'] = metadata
        else:
            data['file_metadata'] = None
        
        return data

    def create(self, validated_data):
        instance = YourModel.objects.create(
            file_name=validated_data['file_name'],
            file_metadata=validated_data['file_metadata']
        )
        return instance

    def update(self, instance, validated_data):
        instance.file_name = validated_data.get('file_name', instance.file_name)
        instance.file_metadata = validated_data.get('file_metadata', instance.file_metadata)
        instance.save()
        return instance

4. 创建视图

使用 Django REST Framework 的视图来处理 API 请求:

# your_app/views.py

from rest_framework import viewsets
from .models import YourModel
from .serializers import YourModelSerializer

class YourModelViewSet(viewsets.ModelViewSet):
    queryset = YourModel.objects.all()
    serializer_class = YourModelSerializer

5. 配置 URL

将视图添加到 URL 配置中:

# your_app/urls.py

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import YourModelViewSet

router = DefaultRouter()
router.register(r'yourmodel', YourModelViewSet)

urlpatterns = [
    path('', include(router.urls)),
]

6. 启动项目

启动开发服务器:

python manage.py runserver

使用说明

现在,当你通过 API 创建或更新 YourModel 实例时,文件名将通过序列化器进行解析和校验,解析出来的数据将被存储在 file_metadata JSON 字段中,并且动态校验逻辑将被应用。

示例请求和响应

假设你有一个 YourModel 实例,其 file_name 字段为 [thru_rx1]-xxx.s4p。通过 API 创建或更新这个实例时,你会看到:

{
    "id": 1,
    "file_name": "[thru_rx1]-xxx.s4p",
    "file_metadata": {
        "type": "thru",
        "channel": "rx1"
    }
}

通过这种方式,你可以在序列化器中对数据进行解析和校验,并灵活地存储解析后的数据。如果有新的格式或新的校验规则,只需更新 DynamicFieldValidator 或在序列化器中添加新的校验逻辑即可。


好的,假设你有一个自定义的校验类来校验数据,并且你需要先解析文件名,然后根据校验表对数据进行校验。在这个示例中,我们会:

  1. 解析文件名
  2. 根据校验表对需要校验的数据进行校验
  3. 其他正常字段也需要进行校验

我们将一步一步地构建代码。

1. 定义模型

首先定义两个模型:YourModel 用于存储主要数据,ValidationRule 用于存储校验规则。

# your_app/models.py

from django.db import models
from django.contrib.postgres.fields import JSONField

class YourModel(models.Model):
    file_name = models.CharField(max_length=255)  # 文件名字段
    file_metadata = JSONField(blank=True, null=True)  # JSON 字段,用于存储文件元数据

    def __str__(self):
        return self.file_name  # 返回文件名作为字符串表示

class ValidationRule(models.Model):
    field_name = models.CharField(max_length=255)  # 需要校验的字段名称
    rule_type = models.CharField(max_length=255)  # 校验规则类型,例如 'regex' 或 'min_length'
    rule_value = models.CharField(max_length=255)  # 校验规则的具体值,例如正则表达式或最小长度

    def __str__(self):
        return f"{self.field_name} - {self.rule_type}: {self.rule_value}"  # 返回校验规则的字符串表示

2. 创建校验类

自定义校验类,用于校验数据并返回错误信息:

# your_app/validators.py

import re
from rest_framework import serializers

class DynamicFieldValidator:
    def __init__(self, field_name, rule_type, rule_value):
        self.field_name = field_name  # 字段名称
        self.rule_type = rule_type  # 校验规则类型
        self.rule_value = rule_value  # 校验规则的具体值

    def __call__(self, value):
        if self.rule_type == 'regex':
            if not re.match(self.rule_value, value):
                raise serializers.ValidationError(f"{self.field_name} does not match pattern {self.rule_value}")
        elif self.rule_type == 'min_length':
            if len(value) < int(self.rule_value):
                raise serializers.ValidationError(f"{self.field_name} length is less than {self.rule_value}")
        # 可以添加更多规则类型

3. 解析文件名

解析文件名的函数:

# your_app/utils.py

import re

def parse_file_name(file_name):
    match = re.match(r'\[(.*?)\]-(.*?)\.s4p', file_name)
    if match:
        parsed_data = match.groups()
        return {
            "type": parsed_data[0],
            "channel": parsed_data[1]
        }
    return {}

4. 修改序列化器

在序列化器中应用解析文件名和动态校验逻辑:

# your_app/serializers.py

from rest_framework import serializers
from .models import YourModel, ValidationRule
from .validators import DynamicFieldValidator
from .utils import parse_file_name

class YourModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = YourModel  # 绑定的模型
        fields = ['id', 'file_name', 'file_metadata']  # 序列化器的字段

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)  # 调用父类的初始化方法
        self.dynamic_validators = {}  # 动态校验器字典
        # 从数据库中读取校验规则
        validation_rules = ValidationRule.objects.all()
        for rule in validation_rules:
            if rule.field_name not in self.dynamic_validators:
                self.dynamic_validators[rule.field_name] = []
            self.dynamic_validators[rule.field_name].append(DynamicFieldValidator(rule.field_name, rule.rule_type, rule.rule_value))

    def validate(self, data):
        # 解析文件名
        parsed_data = parse_file_name(data['file_name'])
        data.update(parsed_data)
        
        # 逐字段应用动态校验
        for field, validators in self.dynamic_validators.items():
            value = data.get(field)
            if value is not None:
                for validator in validators:
                    validator(value)

        # 调用自定义校验方法
        self.custom_validate(data)
        return data

    def custom_validate(self, data):
        # 对于需要自定义校验的字段进行校验
        # 比如:
        if 'type' in data and not data['type'].startswith('thru'):
            raise serializers.ValidationError({'type': 'Invalid type value'})
        # 添加更多自定义校验

    def create(self, validated_data):
        instance = YourModel.objects.create(
            file_name=validated_data['file_name'],
            file_metadata=validated_data.get('file_metadata', {})
        )
        return instance

    def update(self, instance, validated_data):
        instance.file_name = validated_data.get('file_name', instance.file_name)
        instance.file_metadata = validated_data.get('file_metadata', instance.file_metadata)
        instance.save()
        return instance

5. 创建视图

使用 Django REST Framework 的视图来处理 API 请求:

# your_app/views.py

from rest_framework import viewsets
from .models import YourModel
from .serializers import YourModelSerializer

class YourModelViewSet(viewsets.ModelViewSet):
    queryset = YourModel.objects.all()  # 查询所有 YourModel 实例
    serializer_class = YourModelSerializer  # 使用 YourModelSerializer 序列化器

6. 配置 URL

将视图添加到 URL 配置中:

# your_app/urls.py

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import YourModelViewSet

router = DefaultRouter()
router.register(r'yourmodel', YourModelViewSet)  # 注册 YourModelViewSet 视图集

urlpatterns = [
    path('', include(router.urls)),  # 包含默认路由
]

7. 启动项目

启动开发服务器:

python manage.py runserver

示例说明

  1. 当你通过 API 创建或更新 YourModel 实例时,序列化器会先解析文件名。
  2. 解析结果会与原始数据合并,进行动态校验。
  3. 根据校验表中的规则和自定义校验类进行校验。
  4. 如果校验失败,API 会返回相应的错误信息。

通过这种方式,您可以灵活地管理校验规则,并根据需要进行扩展。如果需要添加新的校验规则,只需向 ValidationRule 表中添加相应的记录即可。


为了确保数据在通过所有校验后才写入数据库,并在校验失败时返回具体的错误信息,我们需要调整序列化器的逻辑。在校验过程中,如果有任何错误,我们将收集这些错误并返回给客户端。以下是完整的实现:

1. 定义模型

与之前的模型定义保持一致:

# your_app/models.py

from django.db import models
from django.contrib.postgres.fields import JSONField

class YourModel(models.Model):
    file_name = models.CharField(max_length=255)  # 文件名字段
    file_metadata = JSONField(blank=True, null=True)  # JSON 字段,用于存储文件元数据

    def __str__(self):
        return self.file_name  # 返回文件名作为字符串表示

class ValidationRule(models.Model):
    field_name = models.CharField(max_length=255)  # 需要校验的字段名称
    rule_type = models.CharField(max_length=255)  # 校验规则类型,例如 'regex' 或 'min_length'
    rule_value = models.CharField(max_length=255)  # 校验规则的具体值,例如正则表达式或最小长度

    def __str__(self):
        return f"{self.field_name} - {self.rule_type}: {self.rule_value}"  # 返回校验规则的字符串表示

2. 创建校验类

自定义校验类,用于校验数据并返回错误信息:

# your_app/validators.py

import re
from rest_framework import serializers

class DynamicFieldValidator:
    def __init__(self, field_name, rule_type, rule_value):
        self.field_name = field_name  # 字段名称
        self.rule_type = rule_type  # 校验规则类型
        self.rule_value = rule_value  # 校验规则的具体值

    def __call__(self, value):
        if self.rule_type == 'regex':
            if not re.match(self.rule_value, value):
                raise serializers.ValidationError(f"{self.field_name} does not match pattern {self.rule_value}")
        elif self.rule_type == 'min_length':
            if len(value) < int(self.rule_value):
                raise serializers.ValidationError(f"{self.field_name} length is less than {self.rule_value}")
        # 可以添加更多规则类型

3. 解析文件名

解析文件名的函数:

# your_app/utils.py

import re

def parse_file_name(file_name):
    match = re.match(r'\[(.*?)\]-(.*?)\.s4p', file_name)
    if match:
        parsed_data = match.groups()
        return {
            "type": parsed_data[0],
            "channel": parsed_data[1]
        }
    return {}

4. 修改序列化器

在序列化器中应用解析文件名和动态校验逻辑,并返回具体的错误信息:

# your_app/serializers.py

from rest_framework import serializers
from .models import YourModel, ValidationRule
from .validators import DynamicFieldValidator
from .utils import parse_file_name

class YourModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = YourModel  # 绑定的模型
        fields = ['id', 'file_name', 'file_metadata']  # 序列化器的字段

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)  # 调用父类的初始化方法
        self.dynamic_validators = {}  # 动态校验器字典
        # 从数据库中读取校验规则
        validation_rules = ValidationRule.objects.all()
        for rule in validation_rules:
            if rule.field_name not in self.dynamic_validators:
                self.dynamic_validators[rule.field_name] = []
            self.dynamic_validators[rule.field_name].append(DynamicFieldValidator(rule.field_name, rule.rule_type, rule.rule_value))

    def validate(self, data):
        # 解析文件名
        parsed_data = parse_file_name(data['file_name'])
        data.update(parsed_data)

        errors = {}  # 用于存储错误信息

        # 逐字段应用动态校验
        for field, validators in self.dynamic_validators.items():
            value = data.get(field)
            if value is not None:
                for validator in validators:
                    try:
                        validator(value)
                    except serializers.ValidationError as e:
                        errors[field] = str(e)

        # 调用自定义校验方法
        self.custom_validate(data, errors)

        if errors:
            raise serializers.ValidationError(errors)

        return data

    def custom_validate(self, data, errors):
        # 对于需要自定义校验的字段进行校验
        # 比如:
        if 'type' in data and not data['type'].startswith('thru'):
            errors['type'] = 'Invalid type value'
        # 添加更多自定义校验

    def create(self, validated_data):
        instance = YourModel.objects.create(
            file_name=validated_data['file_name'],
            file_metadata=validated_data.get('file_metadata', {})
        )
        return instance

    def update(self, instance, validated_data):
        instance.file_name = validated_data.get('file_name', instance.file_name)
        instance.file_metadata = validated_data.get('file_metadata', instance.file_metadata)
        instance.save()
        return instance

5. 创建视图

使用 Django REST Framework 的视图来处理 API 请求:

# your_app/views.py

from rest_framework import viewsets
from .models import YourModel
from .serializers import YourModelSerializer

class YourModelViewSet(viewsets.ModelViewSet):
    queryset = YourModel.objects.all()  # 查询所有 YourModel 实例
    serializer_class = YourModelSerializer  # 使用 YourModelSerializer 序列化器

6. 配置 URL

将视图添加到 URL 配置中:

# your_app/urls.py

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import YourModelViewSet

router = DefaultRouter()
router.register(r'yourmodel', YourModelViewSet)  # 注册 YourModelViewSet 视图集

urlpatterns = [
    path('', include(router.urls)),  # 包含默认路由
]

7. 启动项目

启动开发服务器:

python manage.py runserver

示例说明

  1. 当你通过 API 创建或更新 YourModel 实例时,序列化器会先解析文件名。
  2. 解析结果会与原始数据合并,进行动态校验。
  3. 根据校验表中的规则和自定义校验类进行校验。
  4. 如果校验失败,API 会返回具体的错误信息。
  5. 只有在所有校验通过后,数据才会被写入数据库。

这样,你可以确保数据在通过所有校验后才会被写入数据库,并在校验失败时返回详细的错误信息。


好的,我们可以将数据校验和数据保存分成两个视图分别处理。以下是实现完整代码的步骤:

1. 模型定义

# your_app/models.py

from django.db import models
from django.contrib.postgres.fields import JSONField
import uuid

class YourModel(models.Model):
    uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    file_name = models.CharField(max_length=255)
    file_metadata = JSONField(blank=True, null=True)
    is_deleted = models.BooleanField(default=False)

    def __str__(self):
        return self.file_name

class FieldInfo(models.Model):
    field_name = models.CharField(max_length=255)
    is_unique_together = models.BooleanField(default=False)

    def __str__(self):
        return f"{self.field_name} - {'Unique Together' if self.is_unique_together else 'Not Unique Together'}"

2. 自定义校验类

# your_app/validators.py

import re
from rest_framework import serializers

class DynamicFieldValidator:
    def __init__(self, field_name, rule_type, rule_value):
        self.field_name = field_name
        self.rule_type = rule_type
        self.rule_value = rule_value

    def __call__(self, value):
        if self.rule_type == 'regex':
            if not re.match(self.rule_value, value):
                raise serializers.ValidationError(f"{self.field_name} does not match pattern {self.rule_value}")
        elif self.rule_type == 'min_length':
            if len(value) < int(self.rule_value):
                raise serializers.ValidationError(f"{self.field_name} length is less than {self.rule_value}")
        # 可以添加更多规则类型

3. 解析文件名的函数

# your_app/utils.py

import re

def parse_file_name(file_name):
    match = re.match(r'\[(.*?)\]-(.*?)\.s4p', file_name)
    if match:
        parsed_data = match.groups()
        return {
            "type": parsed_data[0],
            "channel": parsed_data[1]
        }
    return {}

4. 序列化器

# your_app/serializers.py

from rest_framework import serializers
from .models import YourModel, FieldInfo
from .validators import DynamicFieldValidator
from .utils import parse_file_name
import uuid

class YourModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = YourModel
        fields = ['id', 'uuid', 'file_name', 'file_metadata', 'is_deleted']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.dynamic_validators = {}
        validation_rules = FieldInfo.objects.all()
        for rule in validation_rules:
            if rule.field_name not in self.dynamic_validators:
                self.dynamic_validators[rule.field_name] = []
            self.dynamic_validators[rule.field_name].append(DynamicFieldValidator(rule.field_name, 'regex', r'.*'))  # 示例:默认使用regex规则,实际情况中应从数据库读取

        # 动态生成unique_together字段
        self.unique_together_fields = []
        field_infos = FieldInfo.objects.filter(is_unique_together=True)
        for field_info in field_infos:
            self.unique_together_fields.append(field_info.field_name)

    def validate(self, data):
        # 解析文件名
        parsed_data = parse_file_name(data['file_name'])
        data.update(parsed_data)

        errors = {}

        # 逐字段应用动态校验
        for field, validators in self.dynamic_validators.items():
            value = data.get(field)
            if value is not None:
                for validator in validators:
                    try:
                        validator(value)
                    except serializers.ValidationError as e:
                        errors[field] = str(e)

        # 检查动态unique_together约束
        unique_filter = {field: data[field] for field in self.unique_together_fields}
        if YourModel.objects.filter(**unique_filter, is_deleted=False).exists():
            existing_instance = YourModel.objects.get(**unique_filter, is_deleted=False)
            data['uuid'] = existing_instance.uuid
            data['is_deleted'] = False
        else:
            data['uuid'] = uuid.uuid4()

        if errors:
            raise serializers.ValidationError(errors)

        return data

5. 视图定义

分开校验和保存的视图:

# your_app/views.py

from rest_framework import viewsets, status
from rest_framework.response import Response
from rest_framework.decorators import api_view
from .models import YourModel
from .serializers import YourModelSerializer

@api_view(['POST'])
def validate_data(request):
    serializer = YourModelSerializer(data=request.data)
    if serializer.is_valid():
        return Response(serializer.data, status=status.HTTP_200_OK)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['POST'])
def save_data(request):
    serializer = YourModelSerializer(data=request.data)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

6. 配置 URL

将视图添加到 URL 配置中:

# your_app/urls.py

from django.urls import path
from .views import validate_data, save_data

urlpatterns = [
    path('yourmodel/validate/', validate_data, name='validate_data'),
    path('yourmodel/save/', save_data, name='save_data'),
]

7. 启动项目

启动开发服务器:

python manage.py runserver

8. 测试流程

通过以下步骤可以测试整个流程:

第一步:校验数据

向 API 发送带有文件名和其他字段的 POST 请求进行数据校验。

请求

POST /yourmodel/validate/
Content-Type: application/json

{
    "file_name": "[thru_rx1]-example.s4p",
    "file_metadata": {"key": "value"}
}

响应(校验通过)

HTTP/1.1 200 OK
Content-Type: application/json

{
    "id": null,
    "uuid": "generated_or_existing_uuid",
    "file_name": "[thru_rx1]-example.s4p",
    "file_metadata": {"key": "value"},
    "is_deleted": false,
    "type": "thru",
    "channel": "rx1"
}

响应(校验失败)

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
    "type": "Invalid type value",
    "field_name": "error message"
}

第二步:保存数据

用户检查数据无误后,将数据再次发送给后端进行保存。

请求

POST /yourmodel/save/
Content-Type: application/json

{
    "file_name": "[thru_rx1]-example.s4p",
    "file_metadata": {"key": "value"},
    "uuid": "generated_or_existing_uuid",
    "is_deleted": false
}

响应(保存成功)

HTTP/1.1 201 Created
Content-Type: application/json

{
    "id": 1,
    "uuid": "generated_or_existing_uuid",
    "file_name": "[thru_rx1]-example.s4p",
    "file_metadata": {"key": "value"},
    "is_deleted": false,
    "type": "thru",
    "channel": "rx1"
}

代码解释

模型

  • YourModel: 代表文件的数据模型,包括 UUID、文件名、文件元数据和删除标记。
  • FieldInfo: 存储字段信息,用于动态生成校验和唯一性约束。

校验类

  • DynamicFieldValidator: 自定义校验类,用于动态校验字段。

工具函数

  • parse_file_name: 解析文件名,提取类型和通道信息。

序列化器

  • YourModelSerializer: 序列化器用于验证和序列化 YourModel 数据,包括动态生成校验规则和唯一性约束。

视图

  • validate_data: 用于校验数据的视图函数,返回校验结果。
  • save_data: 用于保存数据的视图函数,在校验成功后将数据保存到数据库。

URL 配置

  • 配置校验和保存数据的 URL。

通过这种方式,数据在校验通过后会返回给前端,由用户检查后再提交进行保存,确保数据的完整性和正确性。


了解您的需求后,下面的代码将会对您的问题进行实现。具体内容包括:

  1. 批量更新和添加数据。
  2. 更新数据时,新增一条数据,并将旧数据标记为删除。
  3. 删除数据时,新添加的数据也标记为删除。
  4. 动态地根据不同模型的 unique_together 进行数据标记。

1. 模型定义

# your_app/models.py

from django.db import models
from django.contrib.postgres.fields import JSONField
import uuid

class YourModel(models.Model):
    uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    file_name = models.CharField(max_length=255)
    file_metadata = JSONField(blank=True, null=True)
    is_deleted = models.BooleanField(default=False)

    def __str__(self):
        return self.file_name

class FieldInfo(models.Model):
    field_name = models.CharField(max_length=255)
    is_unique_together = models.BooleanField(default=False)

    def __str__(self):
        return f"{self.field_name} - {'Unique Together' if self.is_unique_together else 'Not Unique Together'}"

2. 自定义校验类

# your_app/validators.py

import re
from rest_framework import serializers

class DynamicFieldValidator:
    def __init__(self, field_name, rule_type, rule_value):
        self.field_name = field_name
        self.rule_type = rule_type
        self.rule_value = rule_value

    def __call__(self, value):
        if self.rule_type == 'regex':
            if not re.match(self.rule_value, value):
                raise serializers.ValidationError(f"{self.field_name} does not match pattern {self.rule_value}")
        elif self.rule_type == 'min_length':
            if len(value) < int(self.rule_value):
                raise serializers.ValidationError(f"{self.field_name} length is less than {self.rule_value}")
        # 可以添加更多规则类型

3. 解析文件名的函数

# your_app/utils.py

import re

def parse_file_name(file_name):
    match = re.match(r'\[(.*?)\]-(.*?)\.s4p', file_name)
    if match:
        parsed_data = match.groups()
        return {
            "type": parsed_data[0],
            "channel": parsed_data[1]
        }
    return {}

4. 序列化器

# your_app/serializers.py

from rest_framework import serializers
from .models import YourModel, FieldInfo
from .validators import DynamicFieldValidator
from .utils import parse_file_name
import uuid

class YourModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = YourModel
        fields = ['id', 'uuid', 'file_name', 'file_metadata', 'is_deleted']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.dynamic_validators = {}
        validation_rules = FieldInfo.objects.all()
        for rule in validation_rules:
            if rule.field_name not in self.dynamic_validators:
                self.dynamic_validators[rule.field_name] = []
            self.dynamic_validators[rule.field_name].append(DynamicFieldValidator(rule.field_name, 'regex', r'.*'))  # 示例:默认使用regex规则,实际情况中应从数据库读取

        # 动态生成unique_together字段
        self.unique_together_fields = []
        field_infos = FieldInfo.objects.filter(is_unique_together=True)
        for field_info in field_infos:
            self.unique_together_fields.append(field_info.field_name)

    def validate(self, data):
        # 解析文件名
        parsed_data = parse_file_name(data['file_name'])
        data.update(parsed_data)

        errors = {}

        # 逐字段应用动态校验
        for field, validators in self.dynamic_validators.items():
            value = data.get(field)
            if value is not None:
                for validator in validators:
                    try:
                        validator(value)
                    except serializers.ValidationError as e:
                        errors[field] = str(e)

        if errors:
            raise serializers.ValidationError(errors)

        return data

    def create(self, validated_data):
        unique_filter = {field: validated_data[field] for field in self.unique_together_fields}
        if YourModel.objects.filter(**unique_filter, is_deleted=False).exists():
            existing_instance = YourModel.objects.get(**unique_filter, is_deleted=False)
            existing_instance.is_deleted = True
            existing_instance.save()
            validated_data['uuid'] = existing_instance.uuid
        return super().create(validated_data)

    def update(self, instance, validated_data):
        unique_filter = {field: validated_data[field] for field in self.unique_together_fields}
        if YourModel.objects.filter(**unique_filter, is_deleted=False).exists():
            existing_instance = YourModel.objects.get(**unique_filter, is_deleted=False)
            existing_instance.is_deleted = True
            existing_instance.save()
        return super().create(validated_data)

5. 视图定义

# your_app/views.py

from rest_framework import viewsets, status
from rest_framework.response import Response
from rest_framework.decorators import api_view
from .models import YourModel
from .serializers import YourModelSerializer

@api_view(['POST'])
def validate_data(request):
    serializer = YourModelSerializer(data=request.data, many=True)
    if serializer.is_valid():
        return Response(serializer.data, status=status.HTTP_200_OK)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['POST'])
def save_data(request):
    serializer = YourModelSerializer(data=request.data, many=True)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

6. 配置 URL

# your_app/urls.py

from django.urls import path
from .views import validate_data, save_data

urlpatterns = [
    path('yourmodel/validate/', validate_data, name='validate_data'),
    path('yourmodel/save/', save_data, name='save_data'),
]

代码解释

模型

  • YourModel: 代表文件的数据模型,包括 UUID、文件名、文件元数据和删除标记。
  • FieldInfo: 存储字段信息,用于动态生成校验和唯一性约束。

校验类

  • DynamicFieldValidator: 自定义校验类,用于动态校验字段。

工具函数

  • parse_file_name: 解析文件名,提取类型和通道信息。

序列化器

  • YourModelSerializer: 序列化器用于验证和序列化 YourModel 数据,包括动态生成校验规则和唯一性约束。根据 unique_together 规则,在 createupdate 方法中对数据进行标记和更新。

视图

  • validate_data: 用于校验数据的视图函数,返回校验结果。
  • save_data: 用于批量保存数据的视图函数,在校验成功后将数据保存到数据库,并根据 unique_together 规则进行数据更新和标记。

URL 配置

  • 配置校验和保存数据的 URL。

通过这种方式,数据在校验通过后会返回给前端,由用户检查后再提交进行保存,确保数据的完整性和正确性。批量处理数据时,符合 unique_together 规则的数据会被标记和更新。


要实现批量更新和添加,同时确保删除和更新操作符合业务需求,并动态地根据不同模型的 unique_together 字段来判断数据是否是同一条数据,可以进行以下修改:

模型定义

我们保留之前的模型定义,确保 FieldInfo 表中有每个字段的信息。

自定义校验类和工具函数

保留之前的 DynamicFieldValidatorparse_file_name 工具函数。

序列化器

我们在序列化器中添加对批量数据的处理逻辑。

# your_app/serializers.py

from rest_framework import serializers
from .models import YourModel, FieldInfo
from .validators import DynamicFieldValidator
from .utils import parse_file_name
import uuid

class YourModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = YourModel
        fields = ['id', 'uuid', 'file_name', 'file_metadata', 'is_deleted']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.dynamic_validators = {}
        validation_rules = FieldInfo.objects.all()
        for rule in validation_rules:
            if rule.field_name not in self.dynamic_validators:
                self.dynamic_validators[rule.field_name] = []
            self.dynamic_validators[rule.field_name].append(DynamicFieldValidator(rule.field_name, 'regex', r'.*'))

        self.unique_together_fields = []
        field_infos = FieldInfo.objects.filter(is_unique_together=True)
        for field_info in field_infos:
            self.unique_together_fields.append(field_info.field_name)

    def validate(self, data):
        parsed_data = parse_file_name(data['file_name'])
        data.update(parsed_data)

        errors = {}
        for field, validators in self.dynamic_validators.items():
            value = data.get(field)
            if value is not None:
                for validator in validators:
                    try:
                        validator(value)
                    except serializers.ValidationError as e:
                        errors[field] = str(e)

        unique_filter = {field: data[field] for field in self.unique_together_fields}
        existing_instance = YourModel.objects.filter(**unique_filter, is_deleted=False).first()
        if existing_instance:
            data['uuid'] = existing_instance.uuid
            data['is_deleted'] = False
        else:
            data['uuid'] = uuid.uuid4()

        if errors:
            raise serializers.ValidationError(errors)

        return data

    def create(self, validated_data):
        unique_filter = {field: validated_data[field] for field in self.unique_together_fields}
        YourModel.objects.filter(**unique_filter).update(is_deleted=True)
        return YourModel.objects.create(**validated_data)

class BulkYourModelSerializer(serializers.ListSerializer):
    def create(self, validated_data):
        instances = []
        for data in validated_data:
            serializer = YourModelSerializer(data=data)
            serializer.is_valid(raise_exception=True)
            instances.append(serializer.save())
        return instances

视图定义

我们添加批量处理逻辑的视图函数。

# your_app/views.py

from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import YourModel
from .serializers import YourModelSerializer, BulkYourModelSerializer

@api_view(['POST'])
def validate_data(request):
    serializer = YourModelSerializer(data=request.data, many=True)
    if serializer.is_valid():
        return Response(serializer.data, status=status.HTTP_200_OK)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['POST'])
def save_data(request):
    serializer = BulkYourModelSerializer(data=request.data, many=True)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

配置 URL

更新 URL 配置以支持批量操作。

# your_app/urls.py

from django.urls import path
from .views import validate_data, save_data

urlpatterns = [
    path('yourmodel/validate/', validate_data, name='validate_data'),
    path('yourmodel/save/', save_data, name='save_data'),
]

代码解释

  1. 模型定义:
    • 保持之前的模型定义。
    • FieldInfo 表继续存储字段信息和 unique_together 标志。
  2. 校验类和工具函数:
    • DynamicFieldValidator: 保持之前的自定义校验类,用于动态校验字段。
    • parse_file_name: 保持之前的文件名解析函数。
  3. 序列化器:
    • YourModelSerializer: 用于验证和序列化 YourModel 数据,包含动态校验规则和唯一性约束。
    • BulkYourModelSerializer: 用于批量处理的序列化器,调用 YourModelSerializer 来处理每个数据项,并确保校验和保存逻辑。
  4. 视图定义:
    • validate_data: 校验数据的视图函数,支持批量校验。
    • save_data: 保存数据的视图函数,支持批量保存。在保存前,检查 unique_together 字段,标记已有数据为删除并保存新的数据。
  5. URL 配置:
    • 配置批量校验和保存数据的 URL。

通过这种方式,前端可以批量发送数据进行校验,校验通过后再进行批量保存。保存时,根据 unique_together 字段标记已有数据为删除并保存新的数据,实现批量更新和添加的功能。


是的,如果数据中包含了多余的不属于字段的数据,我们可以在保存之前对这些数据进行过滤,只保留模型定义中的字段,然后再进行保存。我们可以通过动态过滤数据的方法来实现这一点。

以下是具体实现方法:

模型定义

模型保持不变。

自定义校验类和工具函数

保持不变。

序列化器

在序列化器中动态过滤掉不属于模型字段的数据。

# your_app/serializers.py

from rest_framework import serializers
from .models import YourModel, FieldInfo
from .validators import DynamicFieldValidator
from .utils import parse_file_name
import uuid

class YourModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = YourModel
        fields = ['id', 'uuid', 'file_name', 'file_metadata', 'is_deleted']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.dynamic_validators = {}
        validation_rules = FieldInfo.objects.all()
        for rule in validation_rules:
            if rule.field_name not in self.dynamic_validators:
                self.dynamic_validators[rule.field_name] = []
            self.dynamic_validators[rule.field_name].append(DynamicFieldValidator(rule.field_name, 'regex', r'.*'))

        self.unique_together_fields = []
        field_infos = FieldInfo.objects.filter(is_unique_together=True)
        for field_info in field_infos:
            self.unique_together_fields.append(field_info.field_name)

    def validate(self, data):
        parsed_data = parse_file_name(data['file_name'])
        data.update(parsed_data)

        errors = {}
        for field, validators in self.dynamic_validators.items():
            value = data.get(field)
            if value is not None:
                for validator in validators:
                    try:
                        validator(value)
                    except serializers.ValidationError as e:
                        errors[field] = str(e)

        unique_filter = {field: data[field] for field in self.unique_together_fields}
        if YourModel.objects.filter(**unique_filter, is_deleted=False).exists():
            existing_instance = YourModel.objects.get(**unique_filter, is_deleted=False)
            data['uuid'] = existing_instance.uuid
            data['is_deleted'] = False
        else:
            data['uuid'] = uuid.uuid4()

        if errors:
            raise serializers.ValidationError(errors)

        return data

    def create(self, validated_data):
        unique_filter = {field: validated_data[field] for field in self.unique_together_fields}
        YourModel.objects.filter(**unique_filter).update(is_deleted=True)
        return YourModel.objects.create(**validated_data)

class BulkYourModelSerializer(serializers.ListSerializer):
    def create(self, validated_data):
        instances = []
        for data in validated_data:
            serializer = YourModelSerializer(data=data)
            serializer.is_valid(raise_exception=True)
            instances.append(serializer.save())
        return instances

视图定义

在视图中对数据进行过滤,只保留模型字段,然后进行保存。

# your_app/views.py

from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import YourModel
from .serializers import YourModelSerializer, BulkYourModelSerializer

@api_view(['POST'])
def validate_data(request):
    serializer = YourModelSerializer(data=request.data, many=True)
    if serializer.is_valid():
        return Response(serializer.data, status=status.HTTP_200_OK)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['POST'])
def save_data(request):
    data_list = request.data
    valid_data_list = []

    for data in data_list:
        file_metadata = {key: value for key, value in data.items() if key not in ['file_name', 'is_deleted']}
        data['file_metadata'] = file_metadata
        data = {key: value for key, value in data.items() if key in ['file_name', 'is_deleted', 'file_metadata']}
        parsed_data = parse_file_name(data['file_name'])
        data.update(parsed_data)
        valid_data_list.append(data)
    
    serializer = BulkYourModelSerializer(data=valid_data_list, many=True)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

配置 URL

配置批量校验和保存数据的 URL。

# your_app/urls.py

from django.urls import path
from .views import validate_data, save_data

urlpatterns = [
    path('yourmodel/validate/', validate_data, name='validate_data'),
    path('yourmodel/save/', save_data, name='save_data'),
]

代码解释

  1. 模型定义:
    • 保持之前的模型定义。
  2. 校验类和工具函数:
    • 保持之前的自定义校验类和文件名解析函数。
  3. 序列化器:
    • YourModelSerializer: 用于验证和序列化 YourModel 数据,包含动态校验规则和唯一性约束。
    • BulkYourModelSerializer: 用于批量处理的序列化器,调用 YourModelSerializer 来处理每个数据项,并确保校验和保存逻辑。
  4. 视图定义:
    • validate_data: 校验数据的视图函数,支持批量校验。
    • save_data: 保存数据的视图函数,支持批量保存。在保存前,过滤掉多余的数据,只保留模型字段,并将特定字段数据移入 JSON 字段,并根据文件名解析出额外字段。
  5. URL 配置:
    • 配置批量校验和保存数据的 URL。

通过这种方式,在校验数据时可以忽略多余的字段,而在保存时,我们会动态过滤掉这些多余的字段,只保留模型定义的字段并进行保存。同时将特定字段数据移入 JSON 字段,并根据文件名解析出额外字段,从而确保数据的一致性和完整性。


好的,我们可以将校验和保存分开处理。在校验过程中,数据以普通字段的形式传递进行校验;在保存时,将特定字段的数据移入 JSON 字段,并根据文件名解析出额外的字段。以下是具体实现步骤。

1. 模型定义

模型保持不变。

# your_app/models.py

from django.db import models
from django.contrib.postgres.fields import JSONField
import uuid

class YourModel(models.Model):
    uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    file_name = models.CharField(max_length=255)
    file_metadata = JSONField(blank=True, null=True)
    is_deleted = models.BooleanField(default=False)

    def __str__(self):
        return self.file_name

class FieldInfo(models.Model):
    field_name = models.CharField(max_length=255)
    is_unique_together = models.BooleanField(default=False)

    def __str__(self):
        return f"{self.field_name} - {'Unique Together' if self.is_unique_together else 'Not Unique Together'}"

2. 自定义校验类和工具函数

# your_app/validators.py

import re
from rest_framework import serializers

class DynamicFieldValidator:
    def __init__(self, field_name, rule_type, rule_value):
        self.field_name = field_name
        self.rule_type = rule_type
        self.rule_value = rule_value

    def __call__(self, value):
        if self.rule_type == 'regex':
            if not re.match(self.rule_value, value):
                raise serializers.ValidationError(f"{self.field_name} does not match pattern {self.rule_value}")
        elif self.rule_type == 'min_length':
            if len(value) < int(self.rule_value):
                raise serializers.ValidationError(f"{self.field_name} length is less than {self.rule_value}")
        # 可以添加更多规则类型
# your_app/utils.py

import re

def parse_file_name(file_name):
    match = re.match(r'\[(.*?)\]-(.*?)\.s4p', file_name)
    if match:
        parsed_data = match.groups()
        return {
            "type": parsed_data[0],
            "channel": parsed_data[1]
        }
    return {}

3. 序列化器

# your_app/serializers.py

from rest_framework import serializers
from .models import YourModel, FieldInfo
from .validators import DynamicFieldValidator
from .utils import parse_file_name
import uuid

class YourModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = YourModel
        fields = ['id', 'uuid', 'file_name', 'file_metadata', 'is_deleted']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.dynamic_validators = {}
        validation_rules = FieldInfo.objects.all()
        for rule in validation_rules:
            if rule.field_name not in self.dynamic_validators:
                self.dynamic_validators[rule.field_name] = []
            self.dynamic_validators[rule.field_name].append(DynamicFieldValidator(rule.field_name, 'regex', r'.*'))

        self.unique_together_fields = []
        field_infos = FieldInfo.objects.filter(is_unique_together=True)
        for field_info in field_infos:
            self.unique_together_fields.append(field_info.field_name)

    def validate(self, data):
        parsed_data = parse_file_name(data['file_name'])
        data.update(parsed_data)

        errors = {}
        for field, validators in self.dynamic_validators.items():
            value = data.get(field)
            if value is not None:
                for validator in validators:
                    try:
                        validator(value)
                    except serializers.ValidationError as e:
                        errors[field] = str(e)

        unique_filter = {field: data[field] for field in self.unique_together_fields}
        if YourModel.objects.filter(**unique_filter, is_deleted=False).exists():
            existing_instance = YourModel.objects.get(**unique_filter, is_deleted=False)
            data['uuid'] = existing_instance.uuid
            data['is_deleted'] = False
        else:
            data['uuid'] = uuid.uuid4()

        if errors:
            raise serializers.ValidationError(errors)

        return data

    def create(self, validated_data):
        unique_filter = {field: validated_data[field] for field in self.unique_together_fields}
        YourModel.objects.filter(**unique_filter).update(is_deleted=True)
        return YourModel.objects.create(**validated_data)

class BulkYourModelSerializer(serializers.ListSerializer):
    def create(self, validated_data):
        instances = []
        for data in validated_data:
            serializer = YourModelSerializer(data=data)
            serializer.is_valid(raise_exception=True)
            instances.append(serializer.save())
        return instances

4. 视图定义

我们将校验和保存视图分开处理。

# your_app/views.py

from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import YourModel
from .serializers import YourModelSerializer, BulkYourModelSerializer

@api_view(['POST'])
def validate_data(request):
    serializer = YourModelSerializer(data=request.data, many=True)
    if serializer.is_valid():
        return Response(serializer.data, status=status.HTTP_200_OK)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['POST'])
def save_data(request):
    data_list = request.data
    for data in data_list:
        file_metadata = {key: value for key, value in data.items() if key not in ['file_name', 'is_deleted']}
        data['file_metadata'] = file_metadata
        data = {key: value for key, value in data.items() if key in ['file_name', 'is_deleted', 'file_metadata']}
        parsed_data = parse_file_name(data['file_name'])
        data.update(parsed_data)
    
    serializer = BulkYourModelSerializer(data=data_list, many=True)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

5. 配置 URL

更新 URL 配置以支持批量操作。

# your_app/urls.py

from django.urls import path
from .views import validate_data, save_data

urlpatterns = [
    path('yourmodel/validate/', validate_data, name='validate_data'),
    path('yourmodel/save/', save_data, name='save_data'),
]

代码解释

  1. 模型定义:
    • 保持之前的模型定义。
    • FieldInfo 表继续存储字段信息和 unique_together 标志。
  2. 校验类和工具函数:
    • DynamicFieldValidator: 保持之前的自定义校验类,用于动态校验字段。
    • parse_file_name: 保持之前的文件名解析函数。
  3. 序列化器:
    • YourModelSerializer: 用于验证和序列化 YourModel 数据,包含动态校验规则和唯一性约束。
    • BulkYourModelSerializer: 用于批量处理的序列化器,调用 YourModelSerializer 来处理每个数据项,并确保校验和保存逻辑。
  4. 视图定义:
    • validate_data: 校验数据的视图函数,支持批量校验。
    • save_data: 保存数据的视图函数,支持批量保存。在保存前,将特定字段数据移入 JSON 字段,并根据文件名解析出额外字段。
  5. URL 配置:
    • 配置批量校验和保存数据的 URL。

通过这种方式,数据在校验通过后返回给前端进行检查,用户确认无误后再进行保存。在保存时,将特定字段数据移入 JSON 字段,并根据文件名解析出额外字段。这样即使有额外的数据也可以正常保存,并且保持数据的一致性和完整性。


Django 的 ORM 不能直接保存包含非模型字段的数据。如果尝试保存包含多余字段的数据,Django 会抛出 TypeError 异常。因此,需要确保传递给模型实例的数据只包含模型定义的字段。

不过,我们可以在视图或序列化器中处理这些多余字段。在保存数据之前,先过滤掉不属于模型字段的数据,或者动态地创建字典,确保只传递模型字段给 Django ORM。

下面是完整的示例代码,演示如何在不过滤掉多余字段的情况下,确保正常保存数据。

模型定义

# your_app/models.py

from django.db import models
from django.contrib.postgres.fields import JSONField
import uuid

class YourModel(models.Model):
    uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    file_name = models.CharField(max_length=255)
    file_metadata = JSONField(blank=True, null=True)
    is_deleted = models.BooleanField(default=False)

    def __str__(self):
        return self.file_name

class FieldInfo(models.Model):
    field_name = models.CharField(max_length=255)
    is_unique_together = models.BooleanField(default=False)

    def __str__(self):
        return f"{self.field_name} - {'Unique Together' if self.is_unique_together else 'Not Unique Together'}"

自定义校验类和工具函数

# your_app/validators.py

import re
from rest_framework import serializers

class DynamicFieldValidator:
    def __init__(self, field_name, rule_type, rule_value):
        self.field_name = field_name
        self.rule_type = rule_type
        self.rule_value = rule_value

    def __call__(self, value):
        if self.rule_type == 'regex':
            if not re.match(self.rule_value, value):
                raise serializers.ValidationError(f"{self.field_name} does not match pattern {self.rule_value}")
        elif self.rule_type == 'min_length':
            if len(value) < int(self.rule_value):
                raise serializers.ValidationError(f"{self.field_name} length is less than {self.rule_value}")
        # 可以添加更多规则类型
# your_app/utils.py

import re

def parse_file_name(file_name):
    match = re.match(r'\[(.*?)\]-(.*?)\.s4p', file_name)
    if match:
        parsed_data = match.groups()
        return {
            "type": parsed_data[0],
            "channel": parsed_data[1]
        }
    return {}

序列化器

# your_app/serializers.py

from rest_framework import serializers
from .models import YourModel, FieldInfo
from .validators import DynamicFieldValidator
from .utils import parse_file_name
import uuid

class YourModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = YourModel
        fields = ['id', 'uuid', 'file_name', 'file_metadata', 'is_deleted']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.dynamic_validators = {}
        validation_rules = FieldInfo.objects.all()
        for rule in validation_rules:
            if rule.field_name not in self.dynamic_validators:
                self.dynamic_validators[rule.field_name] = []
            self.dynamic_validators[rule.field_name].append(DynamicFieldValidator(rule.field_name, 'regex', r'.*'))

        self.unique_together_fields = []
        field_infos = FieldInfo.objects.filter(is_unique_together=True)
        for field_info in field_infos:
            self.unique_together_fields.append(field_info.field_name)

    def validate(self, data):
        parsed_data = parse_file_name(data['file_name'])
        data.update(parsed_data)

        errors = {}
        for field, validators in self.dynamic_validators.items():
            value = data.get(field)
            if value is not None:
                for validator in validators:
                    try:
                        validator(value)
                    except serializers.ValidationError as e:
                        errors[field] = str(e)

        unique_filter = {field: data[field] for field in self.unique_together_fields}
        if YourModel.objects.filter(**unique_filter, is_deleted=False).exists():
            existing_instance = YourModel.objects.get(**unique_filter, is_deleted=False)
            data['uuid'] = existing_instance.uuid
            data['is_deleted'] = False
        else:
            data['uuid'] = uuid.uuid4()

        if errors:
            raise serializers.ValidationError(errors)

        return data

    def create(self, validated_data):
        unique_filter = {field: validated_data[field] for field in self.unique_together_fields}
        YourModel.objects.filter(**unique_filter).update(is_deleted=True)
        return YourModel.objects.create(**validated_data)

class BulkYourModelSerializer(serializers.ListSerializer):
    def create(self, validated_data):
        instances = []
        for data in validated_data:
            serializer = YourModelSerializer(data=data)
            serializer.is_valid(raise_exception=True)
            instances.append(serializer.save())
        return instances

视图定义

在视图中过滤掉不属于模型字段的数据,然后进行保存。

# your_app/views.py

from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import YourModel
from .serializers import YourModelSerializer, BulkYourModelSerializer

def filter_data_by_model_fields(data, model):
    model_fields = {field.name for field in model._meta.fields}
    return {key: value for key, value in data.items() if key in model_fields}

@api_view(['POST'])
def validate_data(request):
    serializer = YourModelSerializer(data=request.data, many=True)
    if serializer.is_valid():
        return Response(serializer.data, status=status.HTTP_200_OK)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['POST'])
def save_data(request):
    data_list = request.data
    valid_data_list = []

    for data in data_list:
        filtered_data = filter_data_by_model_fields(data, YourModel)
        file_metadata = {key: value for key, value in filtered_data.items() if key not in ['file_name', 'is_deleted']}
        filtered_data['file_metadata'] = file_metadata
        parsed_data = parse_file_name(filtered_data['file_name'])
        filtered_data.update(parsed_data)
        valid_data_list.append(filtered_data)
    
    serializer = BulkYourModelSerializer(data=valid_data_list, many=True)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

配置 URL

配置批量校验和保存数据的 URL。

# your_app/urls.py

from django.urls import path
from .views import validate_data, save_data

urlpatterns = [
    path('yourmodel/validate/', validate_data, name='validate_data'),
    path('yourmodel/save/', save_data, name='save_data'),
]

代码解释

  1. 模型定义:
    • 保持之前的模型定义。
  2. 校验类和工具函数:
    • 保持之前的自定义校验类和文件名解析函数。
  3. 序列化器:
    • YourModelSerializer: 用于验证和序列化 YourModel 数据,包含动态校验规则和唯一性约束。
    • BulkYourModelSerializer: 用于批量处理的序列化器,调用 YourModelSerializer 来处理每个数据项,并确保校验和保存逻辑。
  4. 视图定义:
    • filter_data_by_model_fields: 过滤掉不属于模型字段的数据。
    • validate_data: 校验数据的视图函数,支持批量校验。
    • save_data: 保存数据的视图函数,支持批量保存。在保存前,过滤掉多余的数据,只保留模型字段,并将特定字段数据移入 JSON 字段,并根据文件名解析出额外字段。
  5. URL 配置:
    • 配置批量校验和保存数据的 URL。

通过这种方式,在校验数据时可以忽略多余的字段,而在保存时,我们会动态过滤掉这些多余的字段,只保留模型定义的字段并进行保存。同时将特定字段数据移入 JSON 字段,并根据文件名解析出额外字段,从而确保数据的一致性和完整性。


当然可以将操作放在序列化器中。为了实现这种方法,您可以自定义序列化器的方法来处理创建、更新和删除操作。这样可以保持视图层的简洁,将主要的逻辑处理放在序列化器中。

模型定义

保持之前的模型定义:

# your_app/models.py

from django.db import models
from django.contrib.postgres.fields import JSONField
import uuid

class YourModel(models.Model):
    uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    file_name = models.CharField(max_length=255)
    file_metadata = JSONField(blank=True, null=True)
    is_deleted = models.BooleanField(default=False)

    def __str__(self):
        return self.file_name

class FieldInfo(models.Model):
    field_name = models.CharField(max_length=255)
    is_unique_together = models.BooleanField(default=False)

    def __str__(self):
        return f"{self.field_name} - {'Unique Together' if self.is_unique_together else 'Not Unique Together'}"

自定义校验类和工具函数

保持之前的自定义校验类和文件名解析函数:

# your_app/validators.py

import re
from rest_framework import serializers

class DynamicFieldValidator:
    def __init__(self, field_name, rule_type, rule_value):
        self.field_name = field_name
        self.rule_type = rule_type
        self.rule_value = rule_value

    def __call__(self, value):
        if self.rule_type == 'regex':
            if not re.match(self.rule_value, value):
                raise serializers.ValidationError(f"{self.field_name} does not match pattern {self.rule_value}")
        elif self.rule_type == 'min_length':
            if len(value) < int(self.rule_value):
                raise serializers.ValidationError(f"{self.field_name} length is less than {self.rule_value}")
        # 可以添加更多规则类型
# your_app/utils.py

import re

def parse_file_name(file_name):
    match = re.match(r'\[(.*?)\]-(.*?)\.s4p', file_name)
    if match:
        parsed_data = match.groups()
        return {
            "type": parsed_data[0],
            "channel": parsed_data[1]
        }
    return {}

序列化器

定义序列化器以实现数据的校验、创建、更新和删除操作:

# your_app/serializers.py

from rest_framework import serializers
from .models import YourModel, FieldInfo
from .validators import DynamicFieldValidator
from .utils import parse_file_name
import uuid

class YourModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = YourModel
        fields = ['id', 'uuid', 'file_name', 'file_metadata', 'is_deleted']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.dynamic_validators = {}
        validation_rules = FieldInfo.objects.all()
        for rule in validation_rules:
            if rule.field_name not in self.dynamic_validators:
                self.dynamic_validators[rule.field_name] = []
            self.dynamic_validators[rule.field_name].append(DynamicFieldValidator(rule.field_name, 'regex', r'.*'))

        self.unique_together_fields = []
        field_infos = FieldInfo.objects.filter(is_unique_together=True)
        for field_info in field_infos:
            self.unique_together_fields.append(field_info.field_name)

    def validate(self, data):
        parsed_data = parse_file_name(data['file_name'])
        data.update(parsed_data)

        errors = {}
        for field, validators in self.dynamic_validators.items():
            value = data.get(field)
            if value is not None:
                for validator in validators:
                    try:
                        validator(value)
                    except serializers.ValidationError as e:
                        errors[field] = str(e)

        unique_filter = {field: data[field] for field in self.unique_together_fields}
        if YourModel.objects.filter(**unique_filter, is_deleted=False).exists():
            existing_instance = YourModel.objects.get(**unique_filter, is_deleted=False)
            data['uuid'] = existing_instance.uuid
            data['is_deleted'] = False
        else:
            data['uuid'] = uuid.uuid4()

        if errors:
            raise serializers.ValidationError(errors)

        return data

    def create(self, validated_data):
        return YourModel.objects.create(**validated_data)

    def update(self, instance, validated_data):
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        return instance

    def perform_operation(self, validated_data):
        operation = validated_data.pop('operation', None)

        if operation == 'create':
            return self.create(validated_data)

        elif operation == 'update':
            instance_id = validated_data.get('id')
            if not instance_id:
                raise serializers.ValidationError("ID is required for update operation.")
            try:
                instance = YourModel.objects.get(pk=instance_id)
            except YourModel.DoesNotExist:
                raise serializers.ValidationError("Object not found for update operation.")
            return self.update(instance, validated_data)

        elif operation == 'delete':
            instance_id = validated_data.get('id')
            if not instance_id:
                raise serializers.ValidationError("ID is required for delete operation.")
            try:
                instance = YourModel.objects.get(pk=instance_id)
            except YourModel.DoesNotExist:
                raise serializers.ValidationError("Object not found for delete operation.")
            instance.delete()
            return instance

        else:
            raise serializers.ValidationError("Invalid operation specified.")

视图定义

定义视图以调用序列化器的操作方法:

# your_app/views.py

from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from .serializers import YourModelSerializer

class YourModelAPIView(APIView):

    def post(self, request, *args, **kwargs):
        serializer = YourModelSerializer(data=request.data)
        if serializer.is_valid():
            try:
                result = serializer.perform_operation(serializer.validated_data)
                return Response(YourModelSerializer(result).data, status=status.HTTP_200_OK)
            except serializers.ValidationError as e:
                return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

配置 URL

配置 URL 以实现数据的创建、更新和删除操作:

# your_app/urls.py

from django.urls import path
from .views import YourModelAPIView

urlpatterns = [
    path('yourmodel/', YourModelAPIView.as_view(), name='yourmodel-api'),
]

代码解释

  1. 模型定义:
    • YourModelFieldInfo 模型定义如之前。
  2. 校验类和工具函数:
    • 保持之前的自定义校验类和文件名解析函数。
  3. 序列化器:
    • YourModelSerializer: 用于验证和序列化 YourModel 数据,包含动态校验规则和唯一性约束。实现了 createupdate 方法,以处理创建和更新操作。
    • perform_operation: 一个新方法,根据传递的操作类型(创建、更新或删除)执行相应的操作。
  4. 视图定义:
    • YourModelAPIView: 自定义视图类,处理POST请求,通过调用序列化器的perform_operation方法来执行具体的操作。
  5. URL 配置:
    • 配置一个URL,通过POST请求中的operation字段来处理创建、更新和删除操作。

通过这种方式,所有的业务逻辑都在序列化器中处理,而视图仅负责接受请求和返回响应。这样可以保持视图的简洁,同时使序列化器更具扩展性和可维护性。


以下是调整后的代码,支持直接删除数据以及在原数据上进行修改的操作。

模型定义

保持之前的模型定义:

# your_app/models.py

from django.db import models
from django.contrib.postgres.fields import JSONField
import uuid

class YourModel(models.Model):
    uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    file_name = models.CharField(max_length=255)
    file_metadata = JSONField(blank=True, null=True)
    is_deleted = models.BooleanField(default=False)

    def __str__(self):
        return self.file_name

class FieldInfo(models.Model):
    field_name = models.CharField(max_length=255)
    is_unique_together = models.BooleanField(default=False)

    def __str__(self):
        return f"{self.field_name} - {'Unique Together' if self.is_unique_together else 'Not Unique Together'}"

自定义校验类和工具函数

保持之前的自定义校验类和文件名解析函数:

# your_app/validators.py

import re
from rest_framework import serializers

class DynamicFieldValidator:
    def __init__(self, field_name, rule_type, rule_value):
        self.field_name = field_name
        self.rule_type = rule_type
        self.rule_value = rule_value

    def __call__(self, value):
        if self.rule_type == 'regex':
            if not re.match(self.rule_value, value):
                raise serializers.ValidationError(f"{self.field_name} does not match pattern {self.rule_value}")
        elif self.rule_type == 'min_length':
            if len(value) < int(self.rule_value):
                raise serializers.ValidationError(f"{self.field_name} length is less than {self.rule_value}")
        # 可以添加更多规则类型
# your_app/utils.py

import re

def parse_file_name(file_name):
    match = re.match(r'\[(.*?)\]-(.*?)\.s4p', file_name)
    if match:
        parsed_data = match.groups()
        return {
            "type": parsed_data[0],
            "channel": parsed_data[1]
        }
    return {}

序列化器

定义序列化器以实现数据的校验和保存:

# your_app/serializers.py

from rest_framework import serializers
from .models import YourModel, FieldInfo
from .validators import DynamicFieldValidator
from .utils import parse_file_name
import uuid

class YourModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = YourModel
        fields = ['id', 'uuid', 'file_name', 'file_metadata', 'is_deleted']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.dynamic_validators = {}
        validation_rules = FieldInfo.objects.all()
        for rule in validation_rules:
            if rule.field_name not in self.dynamic_validators:
                self.dynamic_validators[rule.field_name] = []
            self.dynamic_validators[rule.field_name].append(DynamicFieldValidator(rule.field_name, 'regex', r'.*'))

        self.unique_together_fields = []
        field_infos = FieldInfo.objects.filter(is_unique_together=True)
        for field_info in field_infos:
            self.unique_together_fields.append(field_info.field_name)

    def validate(self, data):
        parsed_data = parse_file_name(data['file_name'])
        data.update(parsed_data)

        errors = {}
        for field, validators in self.dynamic_validators.items():
            value = data.get(field)
            if value is not None:
                for validator in validators:
                    try:
                        validator(value)
                    except serializers.ValidationError as e:
                        errors[field] = str(e)

        unique_filter = {field: data[field] for field in self.unique_together_fields}
        if YourModel.objects.filter(**unique_filter, is_deleted=False).exists():
            existing_instance = YourModel.objects.get(**unique_filter, is_deleted=False)
            data['uuid'] = existing_instance.uuid
            data['is_deleted'] = False
        else:
            data['uuid'] = uuid.uuid4()

        if errors:
            raise serializers.ValidationError(errors)

        return data

    def create(self, validated_data):
        return YourModel.objects.create(**validated_data)

    def update(self, instance, validated_data):
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        return instance

视图定义

定义视图以实现数据的创建、更新和删除操作:

# your_app/views.py

from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from .models import YourModel
from .serializers import YourModelSerializer

class YourModelAPIView(APIView):

    def post(self, request, *args, **kwargs):
        operation = request.data.get('operation')
        
        if operation == 'create':
            serializer = YourModelSerializer(data=request.data)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data, status=status.HTTP_201_CREATED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        elif operation == 'update':
            try:
                instance = YourModel.objects.get(pk=request.data.get('id'))
            except YourModel.DoesNotExist:
                return Response({"error": "Object not found."}, status=status.HTTP_404_NOT_FOUND)

            serializer = YourModelSerializer(instance, data=request.data)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data, status=status.HTTP_200_OK)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        elif operation == 'delete':
            try:
                instance = YourModel.objects.get(pk=request.data.get('id'))
            except YourModel.DoesNotExist:
                return Response({"error": "Object not found."}, status=status.HTTP_404_NOT_FOUND)

            instance.delete()
            return Response(status=status.HTTP_204_NO_CONTENT)
        
        else:
            return Response({"error": "Invalid operation."}, status=status.HTTP_400_BAD_REQUEST)

配置 URL

配置 URL 以实现数据的创建、更新和删除操作:

# your_app/urls.py

from django.urls import path
from .views import YourModelAPIView

urlpatterns = [
    path('yourmodel/', YourModelAPIView.as_view(), name='yourmodel-api'),
]

代码解释

  1. 模型定义:
    • YourModelFieldInfo 模型定义如之前。
  2. 校验类和工具函数:
    • 保持之前的自定义校验类和文件名解析函数。
  3. 序列化器:
    • YourModelSerializer: 用于验证和序列化 YourModel 数据,包含动态校验规则和唯一性约束。实现了 createupdate 方法,以处理创建和更新操作。
  4. 视图定义:
    • YourModelAPIView: 自定义视图类,处理POST请求,通过请求体中的operation字段来区分创建、更新和删除操作。
  5. URL 配置:
    • 配置一个URL,通过POST请求中的operation字段来处理创建、更新和删除操作。

通过一个API接口,可以实现数据的创建、更新和删除操作。创建和更新操作会进行数据校验,删除操作则直接删除数据。请求体中的operation字段用来区分具体的操作类型。


为了实现修改和删除操作时将旧数据转移到历史表中,可以创建一个历史表模型来存储旧数据,并在序列化器中处理数据的转移和保存。

模型定义

定义主表模型和历史表模型:

# your_app/models.py

from django.db import models
from django.contrib.postgres.fields import JSONField
import uuid

class YourModel(models.Model):
    uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    file_name = models.CharField(max_length=255)
    file_metadata = JSONField(blank=True, null=True)
    is_deleted = models.BooleanField(default=False)

    def __str__(self):
        return self.file_name

class YourModelHistory(models.Model):
    original_id = models.UUIDField()
    file_name = models.CharField(max_length=255)
    file_metadata = JSONField(blank=True, null=True)
    is_deleted = models.BooleanField()
    timestamp = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"History of {self.original_id} at {self.timestamp}"

class FieldInfo(models.Model):
    field_name = models.CharField(max_length=255)
    is_unique_together = models.BooleanField(default=False)

    def __str__(self):
        return f"{self.field_name} - {'Unique Together' if self.is_unique_together else 'Not Unique Together'}"

序列化器

在序列化器中处理数据的转移和保存:

# your_app/serializers.py

from rest_framework import serializers
from .models import YourModel, YourModelHistory, FieldInfo
from .validators import DynamicFieldValidator
from .utils import parse_file_name
import uuid

class YourModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = YourModel
        fields = ['id', 'uuid', 'file_name', 'file_metadata', 'is_deleted']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.dynamic_validators = {}
        validation_rules = FieldInfo.objects.all()
        for rule in validation_rules:
            if rule.field_name not in self.dynamic_validators:
                self.dynamic_validators[rule.field_name] = []
            self.dynamic_validators[rule.field_name].append(DynamicFieldValidator(rule.field_name, 'regex', r'.*'))

        self.unique_together_fields = []
        field_infos = FieldInfo.objects.filter(is_unique_together=True)
        for field_info in field_infos:
            self.unique_together_fields.append(field_info.field_name)

    def validate(self, data):
        parsed_data = parse_file_name(data['file_name'])
        data.update(parsed_data)

        errors = {}
        for field, validators in self.dynamic_validators.items():
            value = data.get(field)
            if value is not None:
                for validator in validators:
                    try:
                        validator(value)
                    except serializers.ValidationError as e:
                        errors[field] = str(e)

        unique_filter = {field: data[field] for field in self.unique_together_fields}
        if YourModel.objects.filter(**unique_filter, is_deleted=False).exists():
            existing_instance = YourModel.objects.get(**unique_filter, is_deleted=False)
            data['uuid'] = existing_instance.uuid
            data['is_deleted'] = False
        else:
            data['uuid'] = uuid.uuid4()

        if errors:
            raise serializers.ValidationError(errors)

        return data

    def create(self, validated_data):
        return YourModel.objects.create(**validated_data)

    def update(self, instance, validated_data):
        # 将旧数据转移到历史表
        YourModelHistory.objects.create(
            original_id=instance.uuid,
            file_name=instance.file_name,
            file_metadata=instance.file_metadata,
            is_deleted=instance.is_deleted
        )
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        return instance

    def delete(self, instance):
        # 将数据转移到历史表并标记删除
        YourModelHistory.objects.create(
            original_id=instance.uuid,
            file_name=instance.file_name,
            file_metadata=instance.file_metadata,
            is_deleted=instance.is_deleted
        )
        instance.is_deleted = True
        instance.save()
        return instance

    def perform_operation(self, validated_data):
        operation = validated_data.pop('operation', None)

        if operation == 'create':
            return self.create(validated_data)

        elif operation == 'update':
            instance_id = validated_data.get('id')
            if not instance_id:
                raise serializers.ValidationError("ID is required for update operation.")
            try:
                instance = YourModel.objects.get(pk=instance_id)
            except YourModel.DoesNotExist:
                raise serializers.ValidationError("Object not found for update operation.")
            return self.update(instance, validated_data)

        elif operation == 'delete':
            instance_id = validated_data.get('id')
            if not instance_id:
                raise serializers.ValidationError("ID is required for delete operation.")
            try:
                instance = YourModel.objects.get(pk=instance_id)
            except YourModel.DoesNotExist:
                raise serializers.ValidationError("Object not found for delete operation.")
            return self.delete(instance)

        else:
            raise serializers.ValidationError("Invalid operation specified.")

视图定义

定义视图以调用序列化器的操作方法:

# your_app/views.py

from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from .serializers import YourModelSerializer

class YourModelAPIView(APIView):

    def post(self, request, *args, **kwargs):
        serializer = YourModelSerializer(data=request.data)
        if serializer.is_valid():
            try:
                result = serializer.perform_operation(serializer.validated_data)
                return Response(YourModelSerializer(result).data, status=status.HTTP_200_OK)
            except serializers.ValidationError as e:
                return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

配置 URL

配置 URL 以实现数据的创建、更新和删除操作:

# your_app/urls.py

from django.urls import path
from .views import YourModelAPIView

urlpatterns = [
    path('yourmodel/', YourModelAPIView.as_view(), name='yourmodel-api'),
]

代码解释

  1. 模型定义:
    • YourModelFieldInfo 模型定义如之前。
    • YourModelHistory 模型用于存储旧数据,包括 original_id 以关联主表记录和时间戳 timestamp 记录转移时间。
  2. 校验类和工具函数:
    • 保持之前的自定义校验类和文件名解析函数。
  3. 序列化器:
    • YourModelSerializer: 用于验证和序列化 YourModel 数据,包含动态校验规则和唯一性约束。实现了 createupdate 方法,以处理创建和更新操作。
    • update 方法在修改数据前,将旧数据转移到 YourModelHistory 表。
    • delete 方法将数据标记为删除并将旧数据转移到 YourModelHistory 表。
    • perform_operation 方法根据传递的操作类型(创建、更新或删除)执行相应的操作。
  4. 视图定义:
    • YourModelAPIView: 自定义视图类,处理POST请求,通过调用序列化器的perform_operation方法来执行具体的操作。
  5. URL 配置:
    • 配置一个URL,通过POST请求中的operation字段来处理创建、更新和删除操作。

通过这种方式,所有的业务逻辑都在序列化器中处理,而视图仅负责接受请求和返回响应。这样可以保持视图的简洁,同时使序列化器更具扩展性和可维护性。在修改和删除操作时,旧数据被转移到历史表中,以确保数据的完整性和可追溯性。


了解,您遇到的问题可能与如何正确地传递和使用 context 相关。在 Django REST Framework 中,context 是一个字典,用于在序列化器和视图之间传递额外的上下文信息,如请求对象、用户信息等。在使用 ListSerializer 和自定义的列表序列化器时,确保正确地传递和更新 context 是非常重要的。

下面我将给出一个示例,演示如何在 Django REST Framework 中正确传递和使用 context,以便在序列化器中进行数据操作,包括使用列表序列化器处理数据的预处理。

示例代码

# your_app/serializers.py

from rest_framework import serializers
from .models import YourModel, YourModelHistory

class YourModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = YourModel
        fields = ['id', 'field1', 'field2', ...]  # Replace with actual fields

    def create(self, validated_data):
        return YourModel.objects.create(**validated_data)

    def update(self, instance, validated_data):
        YourModelHistory.objects.create(
            original_id=instance.id,
            field1=instance.field1,
            field2=instance.field2,
            ...
        )
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        return instance

    def delete(self, instance):
        YourModelHistory.objects.create(
            original_id=instance.id,
            field1=instance.field1,
            field2=instance.field2,
            ...
        )
        instance.delete()

class YourModelListSerializer(serializers.ListSerializer):
    child = YourModelSerializer()

    def create(self, validated_data):
        instances = [YourModel(**item) for item in validated_data]
        return YourModel.objects.bulk_create(instances)

    def update(self, queryset, validated_data):
        instances_map = {instance.id: instance for instance in queryset}
        instances = []
        for item in validated_data:
            instance_id = item.get('id')
            if instance_id:
                instance = instances_map.get(instance_id)
                if instance:
                    instances.append(self.child.update(instance, item))
            else:
                instances.append(self.child.create(item))
        return instances

    def delete(self, queryset):
        for instance in queryset:
            self.child.delete(instance)

class YourModelBulkSerializer(serializers.ModelSerializer):
    class Meta:
        model = YourModel
        fields = ['id', 'field1', 'field2', ...]  # Replace with actual fields
        list_serializer_class = YourModelListSerializer

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.context.update(kwargs.get('context', {}))  # Update context

视图示例

# your_app/views.py

from rest_framework import generics
from rest_framework.response import Response
from .serializers import YourModelBulkSerializer
from .models import YourModel

class YourModelBulkAPIView(generics.ListCreateAPIView):
    queryset = YourModel.objects.all()
    serializer_class = YourModelBulkSerializer

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data, many=isinstance(request.data, list), context={'request': request})
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        return Response(serializer.data, status=status.HTTP_201_CREATED)

    def perform_create(self, serializer):
        serializer.save()

    def get_queryset(self):
        return self.queryset

解释说明

  1. 序列化器定义:
    • YourModelSerializer: 单个对象的序列化器,定义了如何创建、更新和删除单个对象。
    • YourModelListSerializer: 列表对象的序列化器,继承自 ListSerializer,定义了如何批量创建、更新和删除对象。
    • YourModelBulkSerializer: 如果需要在单个视图中处理批量操作,可以将 list_serializer_class 设置为 YourModelListSerializer。在 __init__ 方法中,确保正确更新 context
  2. 视图定义:
    • YourModelBulkAPIView: 继承自 ListCreateAPIView,处理批量创建和获取操作。在 post 方法中,根据请求数据是否为列表来选择序列化器,并传递正确的 context
  3. 注意事项:
    • 确保在初始化序列化器时,正确地传递和更新 context。在视图中可以根据需要传递请求对象或其他上下文信息。
    • ListSerializerlist_serializer_class 的使用可以帮助处理包含多个对象的批量操作,并通过子序列化器 (YourModelSerializerYourModelListSerializer) 来执行相应的数据操作逻辑。

通过这样的方式,您可以在 Django REST Framework 中有效地处理包含列表操作的数据,并在序列化器中进行必要的数据操作和预处理。


在 Django REST framework 中,传递 context 给序列化器时,若使用 ListSerializer,需要确保 context 在子序列化器中也可用。这通常是通过将 context 传递给每个子序列化器来实现的。

以下是完整的代码示例,展示如何传递 context 值给 ListSerializer 并在子序列化器中使用:

序列化器定义

# your_app/serializers.py

from rest_framework import serializers
from .models import YourModel, YourModelHistory, FieldInfo
from .validators import DynamicFieldValidator
from .utils import parse_file_name
import uuid

class YourModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = YourModel
        fields = ['id', 'uuid', 'file_name', 'file_metadata', 'is_deleted']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.dynamic_validators = {}
        validation_rules = FieldInfo.objects.all()
        for rule in validation_rules:
            if rule.field_name not in self.dynamic_validators:
                self.dynamic_validators[rule.field_name].append(DynamicFieldValidator(rule.field_name, 'regex', r'.*'))

        self.unique_together_fields = []
        field_infos = FieldInfo.objects.filter(is_unique_together=True)
        for field_info in field_infos:
            self.unique_together_fields.append(field_info.field_name)

    def validate(self, data):
        parsed_data = parse_file_name(data['file_name'])
        data.update(parsed_data)

        errors = {}
        for field, validators in self.dynamic_validators.items():
            value = data.get(field)
            if value is not None:
                for validator in validators:
                    try:
                        validator(value)
                    except serializers.ValidationError as e:
                        errors[field] = str(e)

        unique_filter = {field: data[field] for field in self.unique_together_fields}
        if YourModel.objects.filter(**unique_filter, is_deleted=False).exists():
            existing_instance = YourModel.objects.get(**unique_filter, is_deleted=False)
            data['uuid'] = existing_instance.uuid
            data['is_deleted'] = False
        else:
            data['uuid'] = uuid.uuid4()

        if errors:
            raise serializers.ValidationError(errors)

        return data

    def create(self, validated_data):
        return YourModel.objects.create(**validated_data)

    def update(self, instance, validated_data):
        YourModelHistory.objects.create(
            original_id=instance.uuid,
            file_name=instance.file_name,
            file_metadata=instance.file_metadata,
            is_deleted=instance.is_deleted
        )
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        return instance

    def delete(self, instance):
        YourModelHistory.objects.create(
            original_id=instance.uuid,
            file_name=instance.file_name,
            file_metadata=instance.file_metadata,
            is_deleted=instance.is_deleted
        )
        instance.is_deleted = True
        instance.save()
        return instance

    def perform_operation(self, validated_data):
        operation = validated_data.pop('operation', None)

        if operation == 'create':
            return self.create(validated_data)

        elif operation == 'update':
            instance_id = validated_data.get('id')
            if not instance_id:
                raise serializers.ValidationError("ID is required for update operation.")
            try:
                instance = YourModel.objects.get(pk=instance_id)
            except YourModel.DoesNotExist:
                raise serializers.ValidationError("Object not found for update operation.")
            return self.update(instance, validated_data)

        elif operation == 'delete':
            instance_id = validated_data.get('id')
            if not instance_id:
                raise serializers.ValidationError("ID is required for delete operation.")
            try:
                instance = YourModel.objects.get(pk=instance_id)
            except YourModel.DoesNotExist:
                raise serializers.ValidationError("Object not found for delete operation.")
            return self.delete(instance)

        else:
            raise serializers.ValidationError("Invalid operation specified.")

class YourModelListSerializer(serializers.ListSerializer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.child.context.update(self.context)

    def perform_operation(self, validated_data):
        results = []
        for item in validated_data:
            serializer = YourModelSerializer(data=item, context=self.context)
            if serializer.is_valid():
                result = serializer.perform_operation(serializer.validated_data)
                results.append(result)
            else:
                raise serializers.ValidationError(serializer.errors)
        return results

视图定义

# your_app/views.py

from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from .serializers import YourModelSerializer, YourModelListSerializer

class YourModelAPIView(APIView):

    def post(self, request, *args, **kwargs):
        is_bulk = isinstance(request.data, list)
        if is_bulk:
            serializer_class = YourModelListSerializer
        else:
            serializer_class = YourModelSerializer

        serializer = serializer_class(data=request.data, many=is_bulk, context={'request': request})
        if serializer.is_valid():
            try:
                result = serializer.perform_operation(serializer.validated_data)
                return Response(YourModelSerializer(result, many=is_bulk).data, status=status.HTTP_200_OK)
            except serializers.ValidationError as e:
                return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

配置 URL

# your_app/urls.py

from django.urls import path
from .views import YourModelAPIView

urlpatterns = [
    path('yourmodel/', YourModelAPIView.as_view(), name='yourmodel-api'),
]

代码解释

  1. 模型定义:
    • YourModelFieldInfo 模型定义如之前。
    • YourModelHistory 模型用于存储旧数据,包括 original_id 以关联主表记录和时间戳 timestamp 记录转移时间。
  2. 校验类和工具函数:
    • 保持之前的自定义校验类和文件名解析函数。
  3. 序列化器:
    • YourModelSerializer: 用于验证和序列化 YourModel 数据,包含动态校验规则和唯一性约束。实现了 createupdate 方法,以处理创建和更新操作。
    • update 方法在修改数据前,将旧数据转移到 YourModelHistory 表。
    • delete 方法将数据标记为删除并将旧数据转移到 YourModelHistory 表。
    • perform_operation 方法根据传递的操作类型(创建、更新或删除)执行相应的操作。
    • YourModelListSerializer: 自定义的列表序列化器,用于处理多个对象的操作。在 __init__ 方法中,将 context 传递给子序列化器。在 perform_operation 方法中,迭代地调用单个对象的 perform_operation 方法。
  4. 视图定义:
    • YourModelAPIView: 自定义视图类,处理POST请求,通过调用序列化器的perform_operation方法来执行具体的操作。在POST请求中检查是否为批量操作,并选择适当的序列化器。
  5. URL 配置:
    • 配置一个URL,通过POST请求中的operation字段来处理创建、更新和删除操作。

通过这种方式,可以使用一个API接口来处理单个或批量的创建、更新和删除操作。序列化器中的逻辑负责校验、数据转移和保存操作,从而使视图保持简洁。


理解了您的需求,如果您需要在 YourModelSerializer 中实现操作并传递 context 参数,可以按照以下方式进行调整和实现:

序列化器代码

# your_app/serializers.py

from rest_framework import serializers
from .models import YourModel, YourModelHistory

class YourModelHistorySerializer(serializers.ModelSerializer):
    class Meta:
        model = YourModelHistory
        fields = '__all__'

class YourModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = YourModel
        fields = ['id', 'field1', 'field2', ...]  # Replace with actual fields
        list_serializer_class = YourModelListSerializer  # Specify the list serializer class

    def create(self, validated_data):
        context = self.context.copy()  # Copy context for use in create method
        # Access context data if needed, for example:
        user = context.get('user')

        return YourModel.objects.create(**validated_data)

    def update(self, instance, validated_data):
        context = self.context.copy()  # Copy context for use in update method
        # Access context data if needed, for example:
        user = context.get('user')

        # Save the current state to history before updating
        YourModelHistory.objects.create(
            original_id=instance.id,
            field1=instance.field1,
            field2=instance.field2,
            ...
        )

        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        return instance

    def delete(self, instance):
        # Save the current state to history before deleting
        YourModelHistory.objects.create(
            original_id=instance.id,
            field1=instance.field1,
            field2=instance.field2,
            ...
        )
        instance.delete()

    def perform_operation(self, action, data):
        context = self.context.copy()  # Copy context for use in perform_operation method
        # Access context data if needed, for example:
        user = context.get('user')

        if action == 'create':
            serializer = self.create(data)
            return serializer.data
        elif action == 'update':
            instance_id = data.get('id')
            if not instance_id:
                raise serializers.ValidationError({"id": "This field is required for update."})
            try:
                instance = YourModel.objects.get(id=instance_id)
                serializer = self.update(instance, data)
                return serializer.data
            except YourModel.DoesNotExist:
                raise serializers.ValidationError({"id": "Instance with this id does not exist."})
        elif action == 'delete':
            instance_id = data.get('id')
            if not instance_id:
                raise serializers.ValidationError({"id": "This field is required for delete."})
            try:
                instance = YourModel.objects.get(id=instance_id)
                self.delete(instance)
                return None  # Return None or appropriate response
            except YourModel.DoesNotExist:
                raise serializers.ValidationError({"id": "Instance with this id does not exist."})
        else:
            raise serializers.ValidationError({"action": "Invalid action."})

class YourModelListSerializer(serializers.ListSerializer):
    def to_internal_value(self, data):
        # Perform special handling for all items in the list
        # For example, merge some fields from all items
        merged_data = self.merge_fields(data)
        return super().to_internal_value(merged_data)

    def merge_fields(self, data):
        # Implement your logic to merge fields across all items in the data list
        # Example: merging 'field1' across all items
        merged_field1 = [item['field1'] for item in data]
        return {
            'field1': merged_field1,
            # Add more fields to merge here
        }

    def create(self, validated_data):
        instances = [YourModel(**item) for item in validated_data]
        return YourModel.objects.bulk_create(instances)

    def update(self, queryset, validated_data):
        instances_map = {instance.id: instance for instance in queryset}
        instances = []
        for item in validated_data:
            instance_id = item.get('id')
            if instance_id:
                instance = instances_map.get(instance_id)
                if instance:
                    instances.append(self.child.update(instance, item))
            else:
                instances.append(self.child.create(item))
        return instances

    def delete(self, queryset):
        for instance in queryset:
            self.child.delete(instance)

    def save(self, **kwargs):
        if isinstance(self.instance, list):
            self.instance = self.create(self.validated_data)
        else:
            self.instance = self.update(self.instance, self.validated_data)
        return self.instance

解释说明

  1. YourModelSerializer:
    • createupdateperform_operation 方法中,都通过 self.context 访问传递的 context 参数。这里假设 context 中可能包含用户信息等。
    • list_serializer_class 属性指定了 YourModelListSerializer,用于处理批量操作。
  2. YourModelListSerializer:
    • to_internal_value 方法用于在序列化之前对所有数据进行特殊处理,例如合并特定字段的数据。
    • createupdatedelete 方法分别处理批量操作的创建、更新和删除。

通过这样的设置,您可以在序列化器中使用 context 参数,并通过 list_serializer_class 处理批量操作,保持代码的清晰性和可维护性。


为了确保 user 字段能够正确地序列化,你需要在数据还原后显式调用 user 序列化器。以下是修改后的代码,包括 to_representation 方法,用于确保在还原数据后正确序列化 user 字段:

模型定义

# your_app/models.py

from django.contrib.auth.models import User
from django.db import models

class YourModel(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    field1 = models.CharField(max_length=255)
    field2 = models.CharField(max_length=255)
    # 其他字段...

class YourModelHistory(models.Model):
    original_id = models.IntegerField()
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    field1 = models.CharField(max_length=255)
    field2 = models.CharField(max_length=255)
    # 其他字段...

序列化器

# your_app/serializers.py

from rest_framework import serializers
from django.contrib.auth.models import User
from .models import YourModel, YourModelHistory

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'username', 'email']

class YourModelSerializer(serializers.ModelSerializer):
    user = UserSerializer(read_only=True)

    class Meta:
        model = YourModel
        fields = ['id', 'user', 'field1', 'field2']

    def create(self, validated_data):
        user = self.context['request'].user
        validated_data['user'] = user
        return YourModel.objects.create(**validated_data)

    def update(self, instance, validated_data):
        user = self.context['request'].user
        YourModelHistory.objects.create(
            original_id=instance.id,
            user=user,
            field1=instance.field1,
            field2=instance.field2,
            # 其他字段...
        )
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        return instance

    def delete(self, instance):
        user = self.context['request'].user
        YourModelHistory.objects.create(
            original_id=instance.id,
            user=user,
            field1=instance.field1,
            field2=instance.field2,
            # 其他字段...
        )
        instance.delete()

    def to_representation(self, instance):
        ret = super().to_representation(instance)
        if 'user' in ret:
            ret['user'] = UserSerializer(instance.user).data
        return ret

class YourModelListSerializer(serializers.ListSerializer):
    def perform_operation(self, action, data, context):
        if action == 'create':
            created_instances = []
            for item in data:
                serializer = YourModelSerializer(data=item, context=context)
                if serializer.is_valid(raise_exception=True):
                    created_instances.append(serializer.save())
            return created_instances
        elif action == 'update':
            updated_instances = []
            for item in data:
                instance_id = item.get('id')
                if not instance_id:
                    raise serializers.ValidationError({"id": "This field is required for update."})
                try:
                    instance = YourModel.objects.get(id=instance_id)
                    serializer = YourModelSerializer(instance, data=item, context=context)
                    if serializer.is_valid(raise_exception=True):
                        updated_instances.append(serializer.save())
                except YourModel.DoesNotExist:
                    raise serializers.ValidationError({"id": "Instance with this id does not exist."})
            return updated_instances
        elif action == 'delete':
            for item in data:
                instance_id = item.get('id')
                if not instance_id:
                    raise serializers.ValidationError({"id": "This field is required for delete."})
                try:
                    instance = YourModel.objects.get(id=instance_id)
                    YourModelSerializer().delete(instance)
                except YourModel.DoesNotExist:
                    raise serializers.ValidationError({"id": "Instance with this id does not exist."})
            return None
        else:
            raise serializers.ValidationError({"action": "Invalid action."})

class YourModelListModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = YourModel
        fields = ['id', 'user', 'field1', 'field2']
        list_serializer_class = YourModelListSerializer

    def perform_operation(self, action, data):
        if isinstance(data, list):
            list_serializer = self.Meta.list_serializer_class()
            return list_serializer.perform_operation(action, data, self.context)
        else:
            if action == 'create':
                return self.create(data)
            elif action == 'update':
                instance = YourModel.objects.get(id=data.get('id'))
                return self.update(instance, data)
            elif action == 'delete':
                instance = YourModel.objects.get(id=data.get('id'))
                self.delete(instance)
                return None
            else:
                raise serializers.ValidationError({"action": "Invalid action."})

视图

# your_app/views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import YourModel
from .serializers import YourModelListModelSerializer

class YourModelView(APIView):
    def post(self, request, *args, **kwargs):
        action = request.data.get('action')
        data = request.data.get('data')
        if not action or not data:
            return Response({"error": "Action and data are required."}, status=status.HTTP_400_BAD_REQUEST)

        serializer = YourModelListModelSerializer(data=data, context={'request': request}, many=isinstance(data, list))

        if serializer.is_valid():
            result = serializer.perform_operation(action, serializer.validated_data)
            if result:
                return Response(result, status=status.HTTP_200_OK)
            else:
                return Response(status=status.HTTP_204_NO_CONTENT)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

解释说明

  1. to_representation 方法:在 YourModelSerializer 中添加 to_representation 方法,以确保在序列化 YourModel 实例时正确嵌套序列化 user 字段。

  2. YourModelListSerializer:处理列表数据的创建、更新和删除操作。

  3. YourModelListModelSerializer:指定 list_serializer_classYourModelListSerializer,并实现 perform_operation 方法。

  4. YourModelView:处理 POST 请求,使用 YourModelListModelSerializer 处理数据并执行操作。

这样,序列化器将能够正确处理 user 字段,并确保在还原数据后正确序列化用户信息。


如果你需要同时检查传递进来的数据数组内部是否有重复项,以及这些数据与数据库中的数据是否重复,可以在序列化器的 validate 方法中进行更复杂的校验。

假设你有一个 API 接受一个产品列表,并且需要验证这些产品在列表中是否重复,以及它们是否与数据库中的现有数据重复。以下是一个示例来说明如何实现这一点:

1. 模型定义

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=100)
    code = models.CharField(max_length=50)

    class Meta:
        unique_together = ('name', 'code')

2. 序列化器定义

首先定义一个单个产品的序列化器:

from rest_framework import serializers
from .models import Product

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

然后定义一个序列化器来处理产品列表:

from rest_framework import serializers
from .models import Product

class ProductListSerializer(serializers.Serializer):
    products = ProductSerializer(many=True)

    def validate(self, data):
        products = data['products']

        # 检查列表内的重复项
        seen = set()
        duplicates = set()
        for product in products:
            identifier = (product['name'], product['code'])
            if identifier in seen:
                duplicates.add(identifier)
            else:
                seen.add(identifier)

        if duplicates:
            raise serializers.ValidationError(f"列表中存在重复的产品: {duplicates}")

        # 检查列表中的产品是否与数据库中的现有产品重复
        for product in products:
            if Product.objects.filter(name=product['name'], code=product['code']).exists():
                raise serializers.ValidationError(f"产品 {product['name']} 和代码 {product['code']} 已存在于数据库中。")

        return data

3. 视图定义

定义一个视图来处理产品列表的创建请求:

from rest_framework import status, views
from rest_framework.response import Response
from .serializers import ProductListSerializer
from .models import Product

class ProductListCreateView(views.APIView):
    def post(self, request, *args, **kwargs):
        serializer = ProductListSerializer(data=request.data)
        if serializer.is_valid():
            # 保存数据
            for product_data in serializer.validated_data['products']:
                Product.objects.create(**product_data)
            return Response(serializer.validated_data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

4. 路由定义

urls.py 中注册路由:

from django.urls import path
from .views import ProductListCreateView

urlpatterns = [
    path('products/', ProductListCreateView.as_view(), name='product-list-create'),
]

总结

通过上述代码,你可以实现以下功能:

  1. 检查传递进来的产品列表内部是否有重复项。
  2. 检查产品列表中的产品是否与数据库中的现有产品重复。

这样,你就能确保传入的数据在多个层面上都是唯一且有效的。


在 Django 中,虽然没有像 ORM 那样直接操作 JSON 字段的简便方法,但可以使用 Django 的 F 表达式和 Func 表达式来在数据库级别直接操作 JSON 字段。使用 PostgreSQL 数据库时,特别有一些高级的 JSON 函数和操作符,可以帮助实现直接更新 JSON 字段。

以下是一个更直接的方式,通过数据库操作来更新 JSON 字段中的特定键值对。

使用 Django ORM 直接操作 JSON 字段

假设你的模型如下:

from django.db import models
from django.contrib.postgres.fields import JSONField

class MyModel(models.Model):
    data = JSONField()

你可以使用 F 表达式和 Func 表达式来更新 JSON 字段中的特定键值对。

更新视图示例

from django.db.models import F, Func, Value
from django.shortcuts import get_object_or_404
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from .models import MyModel

class JSONBSet(Func):
    function = 'jsonb_set'
    template = "%(function)s(%(expressions)s)"

@api_view(['POST'])
def update_json_field(request, pk):
    type_to_update = request.data.get('type')
    new_data = request.data.get('new_data')

    # 验证输入
    if not type_to_update or not isinstance(new_data, dict):
        return Response({'error': 'Invalid input'}, status=status.HTTP_400_BAD_REQUEST)

    # 获取模型实例
    instance = get_object_or_404(MyModel, pk=pk)

    # 获取现有的 JSON 数据
    json_data = instance.data

    # 查找并更新特定类型的字典
    updated = False
    for index, item in enumerate(json_data):
        if item.get('type') == type_to_update:
            # 更新指定键值对
            for key, value in new_data.items():
                json_path = f'{index},{key}'
                MyModel.objects.filter(pk=pk).update(
                    data=JSONBSet(F('data'), Value(json_path), Value(json.dumps(value)), Value(True))
                )
            updated = True
            break

    if not updated:
        return Response({'error': 'Type not found'}, status=status.HTTP_404_NOT_FOUND)

    # 刷新实例以反映数据库中的更新
    instance.refresh_from_db()

    return Response({'status': 'success', 'data': instance.data}, status=status.HTTP_200_OK)

解释

  1. JSONBSet: 自定义一个函数表达式类,用于在数据库中执行 jsonb_set 操作。
  2. update_json_field 视图:
    • 接收并验证输入。
    • 获取模型实例和现有的 JSON 数据。
    • 查找特定类型的字典,并使用 JSONBSet 函数表达式更新指定键值对。
    • 刷新实例以反映数据库中的更新。

优点

这种方法直接在数据库级别操作 JSON 字段,避免了从数据库中获取数据、在应用层更新数据、再保存回数据库的开销。它使用 PostgreSQL 的强大 JSONB 操作功能,可以高效地更新 JSON 字段中的特定部分。

配置 URL

确保在 urls.py 中配置了对应的 URL 和视图:

from django.urls import path
from .views import update_json_field

urlpatterns = [
    path('update/<int:pk>/', update_json_field, name='update-json-field'),
]

示例请求

向该视图发送一个 POST 请求来更新 JSON 字段中的特定字典:

{
    "type": "desired_type",
    "new_data": {
        "key1": "new_value1",
        "key2": "new_value2"
    }
}

通过这种方法,可以直接在数据库级别高效地更新 JSON 字段中的特定键值对。