当然,以下是每一行代码的详细解释,帮助你理解如何实现多表联合查询并进行过滤。
1. 定义模型
在 your_app/models.py
中定义 Author
和 Book
模型:
# 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 请求,上传包含 title
、author
和 published_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 |
代码详细解释
- 模型定义:
Book
模型定义了书籍的基本信息:书名、作者和出版日期。
- 过滤类:
CharInFilter
是一个自定义过滤器,用于处理字符类型的列表。BookFilter
定义了过滤Book
模型的字段:书名、作者和出版日期。
- 视图定义:
UploadXLSXView
是一个 API 视图,用于处理 XLSX 文件上传。post
方法处理上传的文件,读取 XLSX 文件内容,验证列名,并使用过滤器集检查数据是否存在于数据库中。filterset
用于创建一个过滤器集实例,使用上传的数据进行查询。
- URL 配置:
your_app/urls.py
配置了UploadXLSXView
视图的路由。your_project/urls.py
包含应用的 URL 配置,确保路由正确。
通过这个示例,你可以了解如何使用 BaseInFilter
和 django-filter
来处理从 XLSX 文件中获取的数据,并检查这些数据是否存在于数据库中。如果你有任何问题或需要进一步的解释,请随时告诉我。
为了实现从 BookColumn
表中获取校验规则并应用于前端传递的数据或 XLSX 文件中的数据,首先我们需要在 BookColumn
表中存储这些校验规则。然后,我们需要在视图中读取这些规则,并在处理数据时应用这些规则进行校验。下面是一个完整的示例代码,包括定义模型、视图和校验逻辑。
1. 定义模型
首先,定义 Book
和 BookColumn
模型:
# 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 请求,上传包含 title
、author
和 published_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 |
代码详细解释
- 模型定义:
Book
模型定义了书籍的基本信息:书名、作者和出版日期。BookColumn
模型定义了字段的校验规则,包括字段名、字段类型、正则表达式和是否必填。
- 视图定义:
UploadXLSXView
是一个 API 视图,用于处理 XLSX 文件上传。post
方法处理上传的文件,读取 XLSX 文件内容,获取校验规则,并应用这些规则对数据进行校验。validation_rules
获取所有的校验规则。- 对每一行数据,根据校验规则检查字段值是否符合要求,如果有错误,记录错误信息。
- 如果有错误,返回400错误响应。
- 如果没有错误,处理并保存数据。
通过这个示例,你可以了解如何使用从 BookColumn
表中获取的校验规则,对前端传递的数据或 XLSX 文件中的数据进行校验。如果你有任何问题或需要进一步的解释,请随时告诉我。
为了实现这个功能,我们需要以下步骤:
- 创建
BookColumn
模型 用于存储字段的校验信息。 - 创建一个函数,从
BookColumn
表中获取校验信息,并对传递的数据或从 XLSX 文件中读取的数据进行校验。 - 在视图中使用这个校验函数,对数据进行校验并返回错误信息。
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 请求,上传包含 title
、author
和 published_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. 定义模型
首先定义两个模型: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
示例说明
- 当你通过 API 创建或更新
YourModel
实例时,序列化器会先解析文件名。 - 解析结果会与原始数据合并,进行动态校验。
- 根据校验表中的规则和自定义校验类进行校验。
- 如果校验失败,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
示例说明
- 当你通过 API 创建或更新
YourModel
实例时,序列化器会先解析文件名。 - 解析结果会与原始数据合并,进行动态校验。
- 根据校验表中的规则和自定义校验类进行校验。
- 如果校验失败,API 会返回具体的错误信息。
- 只有在所有校验通过后,数据才会被写入数据库。
这样,你可以确保数据在通过所有校验后才会被写入数据库,并在校验失败时返回详细的错误信息。
好的,我们可以将数据校验和数据保存分成两个视图分别处理。以下是实现完整代码的步骤:
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。
通过这种方式,数据在校验通过后会返回给前端,由用户检查后再提交进行保存,确保数据的完整性和正确性。
了解您的需求后,下面的代码将会对您的问题进行实现。具体内容包括:
- 批量更新和添加数据。
- 更新数据时,新增一条数据,并将旧数据标记为删除。
- 删除数据时,新添加的数据也标记为删除。
- 动态地根据不同模型的
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
规则,在create
和update
方法中对数据进行标记和更新。
视图
validate_data
: 用于校验数据的视图函数,返回校验结果。save_data
: 用于批量保存数据的视图函数,在校验成功后将数据保存到数据库,并根据unique_together
规则进行数据更新和标记。
URL 配置
- 配置校验和保存数据的 URL。
通过这种方式,数据在校验通过后会返回给前端,由用户检查后再提交进行保存,确保数据的完整性和正确性。批量处理数据时,符合 unique_together
规则的数据会被标记和更新。
要实现批量更新和添加,同时确保删除和更新操作符合业务需求,并动态地根据不同模型的 unique_together 字段来判断数据是否是同一条数据,可以进行以下修改:
模型定义
我们保留之前的模型定义,确保 FieldInfo
表中有每个字段的信息。
自定义校验类和工具函数
保留之前的 DynamicFieldValidator
和 parse_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'),
]
代码解释
- 模型定义:
- 保持之前的模型定义。
FieldInfo
表继续存储字段信息和 unique_together 标志。
- 校验类和工具函数:
DynamicFieldValidator
: 保持之前的自定义校验类,用于动态校验字段。parse_file_name
: 保持之前的文件名解析函数。
- 序列化器:
YourModelSerializer
: 用于验证和序列化YourModel
数据,包含动态校验规则和唯一性约束。BulkYourModelSerializer
: 用于批量处理的序列化器,调用YourModelSerializer
来处理每个数据项,并确保校验和保存逻辑。
- 视图定义:
validate_data
: 校验数据的视图函数,支持批量校验。save_data
: 保存数据的视图函数,支持批量保存。在保存前,检查 unique_together 字段,标记已有数据为删除并保存新的数据。
- 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'),
]
代码解释
- 模型定义:
- 保持之前的模型定义。
- 校验类和工具函数:
- 保持之前的自定义校验类和文件名解析函数。
- 序列化器:
YourModelSerializer
: 用于验证和序列化YourModel
数据,包含动态校验规则和唯一性约束。BulkYourModelSerializer
: 用于批量处理的序列化器,调用YourModelSerializer
来处理每个数据项,并确保校验和保存逻辑。
- 视图定义:
validate_data
: 校验数据的视图函数,支持批量校验。save_data
: 保存数据的视图函数,支持批量保存。在保存前,过滤掉多余的数据,只保留模型字段,并将特定字段数据移入 JSON 字段,并根据文件名解析出额外字段。
- 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'),
]
代码解释
- 模型定义:
- 保持之前的模型定义。
FieldInfo
表继续存储字段信息和 unique_together 标志。
- 校验类和工具函数:
DynamicFieldValidator
: 保持之前的自定义校验类,用于动态校验字段。parse_file_name
: 保持之前的文件名解析函数。
- 序列化器:
YourModelSerializer
: 用于验证和序列化YourModel
数据,包含动态校验规则和唯一性约束。BulkYourModelSerializer
: 用于批量处理的序列化器,调用YourModelSerializer
来处理每个数据项,并确保校验和保存逻辑。
- 视图定义:
validate_data
: 校验数据的视图函数,支持批量校验。save_data
: 保存数据的视图函数,支持批量保存。在保存前,将特定字段数据移入 JSON 字段,并根据文件名解析出额外字段。
- 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'),
]
代码解释
- 模型定义:
- 保持之前的模型定义。
- 校验类和工具函数:
- 保持之前的自定义校验类和文件名解析函数。
- 序列化器:
YourModelSerializer
: 用于验证和序列化YourModel
数据,包含动态校验规则和唯一性约束。BulkYourModelSerializer
: 用于批量处理的序列化器,调用YourModelSerializer
来处理每个数据项,并确保校验和保存逻辑。
- 视图定义:
filter_data_by_model_fields
: 过滤掉不属于模型字段的数据。validate_data
: 校验数据的视图函数,支持批量校验。save_data
: 保存数据的视图函数,支持批量保存。在保存前,过滤掉多余的数据,只保留模型字段,并将特定字段数据移入 JSON 字段,并根据文件名解析出额外字段。
- 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'),
]
代码解释
- 模型定义:
YourModel
和FieldInfo
模型定义如之前。
- 校验类和工具函数:
- 保持之前的自定义校验类和文件名解析函数。
- 序列化器:
YourModelSerializer
: 用于验证和序列化YourModel
数据,包含动态校验规则和唯一性约束。实现了create
和update
方法,以处理创建和更新操作。perform_operation
: 一个新方法,根据传递的操作类型(创建、更新或删除)执行相应的操作。
- 视图定义:
YourModelAPIView
: 自定义视图类,处理POST请求,通过调用序列化器的perform_operation
方法来执行具体的操作。
- URL 配置:
- 配置一个URL,通过POST请求中的
operation
字段来处理创建、更新和删除操作。
- 配置一个URL,通过POST请求中的
通过这种方式,所有的业务逻辑都在序列化器中处理,而视图仅负责接受请求和返回响应。这样可以保持视图的简洁,同时使序列化器更具扩展性和可维护性。
以下是调整后的代码,支持直接删除数据以及在原数据上进行修改的操作。
模型定义
保持之前的模型定义:
# 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'),
]
代码解释
- 模型定义:
YourModel
和FieldInfo
模型定义如之前。
- 校验类和工具函数:
- 保持之前的自定义校验类和文件名解析函数。
- 序列化器:
YourModelSerializer
: 用于验证和序列化YourModel
数据,包含动态校验规则和唯一性约束。实现了create
和update
方法,以处理创建和更新操作。
- 视图定义:
YourModelAPIView
: 自定义视图类,处理POST请求,通过请求体中的operation
字段来区分创建、更新和删除操作。
- URL 配置:
- 配置一个URL,通过POST请求中的
operation
字段来处理创建、更新和删除操作。
- 配置一个URL,通过POST请求中的
通过一个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'),
]
代码解释
- 模型定义:
YourModel
和FieldInfo
模型定义如之前。YourModelHistory
模型用于存储旧数据,包括original_id
以关联主表记录和时间戳timestamp
记录转移时间。
- 校验类和工具函数:
- 保持之前的自定义校验类和文件名解析函数。
- 序列化器:
YourModelSerializer
: 用于验证和序列化YourModel
数据,包含动态校验规则和唯一性约束。实现了create
和update
方法,以处理创建和更新操作。update
方法在修改数据前,将旧数据转移到YourModelHistory
表。delete
方法将数据标记为删除并将旧数据转移到YourModelHistory
表。perform_operation
方法根据传递的操作类型(创建、更新或删除)执行相应的操作。
- 视图定义:
YourModelAPIView
: 自定义视图类,处理POST请求,通过调用序列化器的perform_operation
方法来执行具体的操作。
- URL 配置:
- 配置一个URL,通过POST请求中的
operation
字段来处理创建、更新和删除操作。
- 配置一个URL,通过POST请求中的
通过这种方式,所有的业务逻辑都在序列化器中处理,而视图仅负责接受请求和返回响应。这样可以保持视图的简洁,同时使序列化器更具扩展性和可维护性。在修改和删除操作时,旧数据被转移到历史表中,以确保数据的完整性和可追溯性。
了解,您遇到的问题可能与如何正确地传递和使用 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
解释说明
- 序列化器定义:
YourModelSerializer
: 单个对象的序列化器,定义了如何创建、更新和删除单个对象。YourModelListSerializer
: 列表对象的序列化器,继承自ListSerializer
,定义了如何批量创建、更新和删除对象。YourModelBulkSerializer
: 如果需要在单个视图中处理批量操作,可以将list_serializer_class
设置为YourModelListSerializer
。在__init__
方法中,确保正确更新context
。
- 视图定义:
YourModelBulkAPIView
: 继承自ListCreateAPIView
,处理批量创建和获取操作。在post
方法中,根据请求数据是否为列表来选择序列化器,并传递正确的context
。
- 注意事项:
- 确保在初始化序列化器时,正确地传递和更新
context
。在视图中可以根据需要传递请求对象或其他上下文信息。 ListSerializer
和list_serializer_class
的使用可以帮助处理包含多个对象的批量操作,并通过子序列化器 (YourModelSerializer
或YourModelListSerializer
) 来执行相应的数据操作逻辑。
- 确保在初始化序列化器时,正确地传递和更新
通过这样的方式,您可以在 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'),
]
代码解释
- 模型定义:
YourModel
和FieldInfo
模型定义如之前。YourModelHistory
模型用于存储旧数据,包括original_id
以关联主表记录和时间戳timestamp
记录转移时间。
- 校验类和工具函数:
- 保持之前的自定义校验类和文件名解析函数。
- 序列化器:
YourModelSerializer
: 用于验证和序列化YourModel
数据,包含动态校验规则和唯一性约束。实现了create
和update
方法,以处理创建和更新操作。update
方法在修改数据前,将旧数据转移到YourModelHistory
表。delete
方法将数据标记为删除并将旧数据转移到YourModelHistory
表。perform_operation
方法根据传递的操作类型(创建、更新或删除)执行相应的操作。YourModelListSerializer
: 自定义的列表序列化器,用于处理多个对象的操作。在__init__
方法中,将context
传递给子序列化器。在perform_operation
方法中,迭代地调用单个对象的perform_operation
方法。
- 视图定义:
YourModelAPIView
: 自定义视图类,处理POST请求,通过调用序列化器的perform_operation
方法来执行具体的操作。在POST请求中检查是否为批量操作,并选择适当的序列化器。
- URL 配置:
- 配置一个URL,通过POST请求中的
operation
字段来处理创建、更新和删除操作。
- 配置一个URL,通过POST请求中的
通过这种方式,可以使用一个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
解释说明
YourModelSerializer
:- 在
create
、update
和perform_operation
方法中,都通过self.context
访问传递的context
参数。这里假设context
中可能包含用户信息等。 list_serializer_class
属性指定了YourModelListSerializer
,用于处理批量操作。
- 在
YourModelListSerializer
:to_internal_value
方法用于在序列化之前对所有数据进行特殊处理,例如合并特定字段的数据。create
、update
和delete
方法分别处理批量操作的创建、更新和删除。
通过这样的设置,您可以在序列化器中使用 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)
解释说明
-
to_representation
方法:在YourModelSerializer
中添加to_representation
方法,以确保在序列化YourModel
实例时正确嵌套序列化user
字段。 -
YourModelListSerializer
:处理列表数据的创建、更新和删除操作。 -
YourModelListModelSerializer
:指定list_serializer_class
为YourModelListSerializer
,并实现perform_operation
方法。 -
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'),
]
总结
通过上述代码,你可以实现以下功能:
- 检查传递进来的产品列表内部是否有重复项。
- 检查产品列表中的产品是否与数据库中的现有产品重复。
这样,你就能确保传入的数据在多个层面上都是唯一且有效的。
在 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)
解释
- JSONBSet: 自定义一个函数表达式类,用于在数据库中执行
jsonb_set
操作。 - 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 字段中的特定键值对。