我有一个Django应用程序,允许通过Django REST Framework插入记录。
记录将由询问电子表格和其他数据库的客户端应用程序逐行批量插入。 REST API允许从Django中抽象出处理数据转换等的其他应用程序。
我想将实际的记录插入与API分离,以提高容错能力和可扩展性。
我正在考虑用Celery做这个,但我之前没用过它。我正在考虑在我现有的DRF ModelViewSets中覆盖perform_create()
(DRF 3.0中添加了perform_create()
)来创建工作人员在后台抓取并处理的Celery任务。
DRF文档说perform_create()
应“通过调用serializer.save()来保存对象实例”。我想知道,在我的情况下,我是否可以忽略此建议,而是让我的Celery任务调用适当的序列化程序来执行对象保存。
例如,如果我有几个模型:
class Book(models.Model):
name = models.CharField(max_length=32)
class Author(models.Model):
surname = models.CharField(max_length=32)
我已经为这些模型提供了DRF视图和序列化器:
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = Book
class AuthorViewSet(viewsets.ModelViewSet):
queryset = Author.objects.all()
serializer_class = Author
在例如中覆盖perform_create()是否是个好主意? BookViewSet
:
def perform_create(self, serializer):
create_book_task(serializer.data)
其中create_book_task
分别是:
@shared_task
def create_book_task(data):
serializer = BookSerializer(data=data)
serializer.save()
我真的找不到其他开发人员做类似事情或试图解决同样问题的例子。我是不是太复杂了?在物理插入方面,我的数据库仍然是限制因素,但至少它不会阻止API客户端排队数据。如果不合适,我不会对Celery承诺。这是最好的解决方案,它有明显的问题,还是有更好的选择?
答案 0 :(得分:2)
我发现你的方法很合理,Celery很棒,除了一些边境案例,根据我的经验可能会有点讨厌(但我不希望在你在问题中提出的用例中遇到这种情况)。 / p>
但是,使用Redis考虑如下简化方法。它有一些优点和缺点。
在BookViewSet中:
from redis import StrictRedis
from rest_framework import viewsets, renderers
redis_client = StrictRedis()
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = Book
def perform_create(self, serializer):
json = renderers.JSONRenderer().render(serializer.data)
redis_client.lpush('create_book_task', json)
在单独的工作人员脚本中:
from django.utils.six import BytesIO
from redis import StrictRedis
from rest_framework.parsers import JSONParser
from myproject import BookSerializer, Book
MAX_BATCH_SIZE = 1000
def create_book_task():
bookset = []
for json in redis_client.brpop(('create_book_task',)):
stream = BytesIO(json)
data = JSONParser().parse(stream)
serializer = BookSerializer(data=data)
assert serializer.is_valid()
bookset.append(serializer.instance)
if len(bookset) >= MAX_BATCH_SIZE:
break
if len(bookset) > 0:
Book.objects.bulk_create(bookset)
while True:
create_book_task()
<强>赞成强>
<强>缺点强>
当然以上是第一种方法,你可能想让它更通用,可以重复使用其他模型,将MAX_BATCH_SIZE移动到你的设置,使用酸洗而不是JSON或其他各种调整,改进或设计决策满足您的特定需求。
最后,我可能会接受我的回答中概述的方法,除非你预计会有其他几个任务被卸载到异步处理,其中使用Celery的情况会变得更强。
PS:由于实际插入将以异步方式完成,请考虑使用202 Accepted
response code代替201 Created
进行回复(除非这会搞砸您的客户)。