我正在使用Django Rest Framework,我的情况类似于此处文档中使用的Album and Tracks示例:http://www.django-rest-framework.org/api-guide/relations/#api-reference
我想创建一个允许我发布此JSON数据的端点
[{"id":1},{"id":2},{"id":3},{"id":4},{"id":6},{"id":7},{"id":8},{"id":9},{"id":10},{"id":11},{"id":12},{"id":13},{"id":75},{"id":76},{"id":77},{"id":78}]
这样曲目将在其所属的专辑中进行相应的重新排序。
JSON数据中的ID指的是特定相册中曲目的主键。
我用来存储订单的字段为order
,唯一索引为unique_together = ('album', 'order')
我可以编写自己的自定义端点来满足这一要求,但是想知道我是否可以重用DRF已经拥有的任何现有代码来实现这一点。
我看过https://github.com/miki725/django-rest-framework-bulk但是我正在使用Django Rest 3.6.3 Django 1.10,python 3所以它似乎已经过时了。
我想更新一个特定相册中曲目的新顺序,给出特定相册的曲目ID列表,如上面的JSON数据所示。< / p>
答案 0 :(得分:2)
首先,我建议删除这个独特的约束,在我看来,它会让你的生活方式变得更加困难。
说真的,我不认为对订单设置一个独特的约束是一个好主意,删除它,确保验证理智的值,并级联你的订单,继续...
如果你想更新曲目的顺序,你需要在订单周围玩耍,这是一些python伪代码:
# original ordering is {"1":1, "2":2, "3":3}
request_data = {"3":1, "2":3, "1":2}
# I need to get a track to filter on the album
example_track = Track.objects.get(pk=request_data.keys()[0])
the_album = example_track.album
# Get the largest ordering value, so we can update these to avoid collisions which throw duplicate value errors from the database
tmp_order = max(Track.objects.filter(album=album).values_list('order', flat=True)) + 1
# Reorder all your tracks using the tmp_order, tmp_order is big enough to never collide with other orderings...
for track in Track.objects.filter(pk__in=list(request_data.keys()):
track.order = tmp_order
track.save()
tmp_order = tmp_order + 1
# Update the tracks again to set the final ordering
for track_id, order_num in request_data:
track = Track.objects.get(pk=track_id)
track.order = order_num
track.save()
此解决方案的一个问题是,如果request_data不包含相册中的所有曲目,它仍然可能会发生碰撞和错误!
# original ordering is {"1":1, "2":2, "3":3, "4": 4}
request_data = {"3":1, "2":3, "1":4}
由于Track#4未包含在请求数据中,并且没有其他值可用于排序,因此除非您愿意更改Track#4的顺序,否则Track#1永远不会被分配4。
此示例部分原因是我建议您不要在曲目上强制执行唯一排序。您可以在应用程序逻辑中使用验证,而不是在数据库上强制执行完整性,并且在这种情况下,您将避免使用数据库完整性约束的大量pita。如果两个轨道具有相同的顺序会发生什么?他们应该通过其他一些领域(赛道名称)进行排序,这不是一个糟糕的结果并且不会破坏任何东西,相当于使用错误顺序的两个项目!这将使您更容易编写可维护的应用程序。
答案 1 :(得分:1)
而不是
[{"id":1},{"id":2},{"id":3},{"id":4},{"id":6},{"id":7},{"id":8},{"id":9},{"id":10},{"id":11},{"id":12},{"id":13},{"id":75},{"id":76},{"id":77},{"id":78}]
我做了
{"children" :[{"id":1},{"id":2},{"id":3},{"id":4},{"id":6},{"id":7},{"id":8},{"id":9},{"id":10},{"id":11},{"id":12},{"id":13},{"id":75},{"id":76},{"id":77},{"id":78}]}
url(r'^(?i)api/v1.0/album/(?P<pk>[0-9]+)/update_children_order$',
views.TracksList.as_view(),
name='view-tracks'),
在views.py中
from django_bulk_update.helper import bulk_update
from rest_framework.response import Response
import json
from rest_framework.views import APIView
class TracksReorder(APIView):
def patch(self, request, pk=None):
"""
Update the order of the tracks by the parent album id
request.data is in the expected structure:
track pk is in data.children.{n}.id
"""
# because the data is expected to be nested dictionary
decoded = request.body.decode('utf8')
data = json.loads(decoded)
sorted_dict = {}
for key, value in enumerate(data['children']):
sorted_dict[value['id']] = key
# sorted dict uses track pk as key and new order as value
tracks = Track.objects.filter(album_id=pk)
for track in tracks:
track.order = sorted_dict[track.id]
affected_rows = bulk_update(tracks, update_fields=['order'])
if affected_rows:
return Response({'affected_rows': affected_rows})
return Response({}, status.HTTP_204_NO_CONTENT)