post group name with the multiple device selected

2019-09-07 09:06发布

问题:

I am trying to create an api for device group and device. A device group can have multiple devices and i want to have the post api for device group with multiple devices because group creation is shown only when device is selected and user might select multiple devices and then create a new group. That way when group is created, those selected devices should also be shown as device_list

Here is my code, i am not sure on how to do post request

class BaseDevice(PolymorphicModel):
  name = models.CharField(max_length=250, blank=False, null=False)
  group = models.ForeignKey('DeviceGroup', related_name="groups", null=True, blank=True)

class Device(BaseDevice):
  description = models.TextField(blank=True, null=True)

class DeviceGroup(models.Model):
    name = models.CharField(max_length=250, blank=False, null=False)  

class DeviceIdSerializer(serializers.ModelSerializer):
    id = serializers.UUIDField(source='token', format='hex', read_only=True)
    class Meta:
        model = Device
        # id is the token of the device and name is the name of the device
        fields = ('id', 'name')

class DeviceGroupSerializer(serializers.ModelSerializer):
    name = serializers.StringRelatedField()
    device_list = DeviceIdSerializer(read_only=False, many=True, required=False, source="groups")
    class Meta:
        model = DeviceGroup
        # name is the name of group created and device_list is the list of devices with id(token) and device name
        fields = ('id', 'name', 'device_list')

    def create(self, validated_data):
        print ('validated_data', validated_data)
        device_list_data = validated_data.pop('device_list')
        group = DeviceGroup.objects.create(**validated_data)
        for device_list in device_list_data:
            BaseDevice.objects.create(group=group, **device_list)
        return group

class DeviceGroupAPIView(APIView):
    permission_classes = (permissions.IsAuthenticated,)

    def get_object(self, user, token):
        try:
            return BaseDevice.objects.filter(owner=user).get(token=token)
        except ObjectDoesNotExist:
            return error.RequestedResourceNotFound().as_response()


    def post(self, request, token=None, format=None):
        device_group_instance = DeviceGroup.objects.get(token=token)
        for device_token in request.data['devices']:
            device = Device.objects.get(token=device_token, owner=request.user)
            device.group = device_group_instance

Here is my api design

{
   "data":[
      {
         "id":1,
         "name":"Home",
         "device_list":[
            {
               "id":"481cfef5a4884e52a63d135967fbc367",
               "name":"Oxygen Provider"
            },
            {
               "id":"7eb006d6db50479aa47f887da0d4f10e",
               "name":"Fan Speed"
            }
         ]
      },
      {
         "id":2,
         "name":"Business",
         "device_list":[

         ]
      }
   ]
}

UPDATE

url(r'^device_group/(?P<token>[0-9a-f]+)/add$', DeviceGroupAPIView.as_view(), name='device_group'),

回答1:

I've changed your code a bit

class BaseDevice(PolymorphicModel):
    name = models.CharField(max_length=250, blank=False, null=False)
    group = models.ForeignKey('DeviceGroup', related_name="groups", null=True, blank=True)

class Device(BaseDevice):
    description = models.TextField(blank=True, null=True)

class DeviceGroup(models.Model):
    name = models.CharField(max_length=250, blank=False, null=False)

class DeviceIdSerializer(serializers.ModelSerializer):
    id = serializers.UUIDField(source='token', format='hex', read_only=True)
    # token does not exist in your model so this will not be included
    class Meta:
        model = Device
        # id is the token of the device and name is the name of the device
        fields = ('id', 'name')

class DeviceGroupSerializer(serializers.ModelSerializer):
    device_list = DeviceIdSerializer(read_only=False, many=True, required=False, source="groups")
    class Meta:
        model = DeviceGroup
        # name is the name of group created and device_list is the list of devices with id(token) and device name
        fields = ('id', 'name', 'device_list')

    def create(self, validated_data):
        print ('validated_data', validated_data)
        device_list_data = validated_data.pop('groups', [])
        # notice that I pop 'groups' because validation changes the input data 
        # to match the field names
        # Also since it is not required I've added a default value
        group = DeviceGroup.objects.create(**validated_data)
        devices = [BaseDevice(group=group, **device_list) for device_list in device_list_data] 
        BaseDevice.objects.bulk_create(devices)
        # Use bulk create when you have to create multiple objects
        # It hits the db only once instead of multiple times
        return group

class DeviceGroupAPIView(ModelViewSet):
    permission_classes = (permissions.IsAuthenticated,)
    serializer_class = DeviceGroupSerializer
    queryset = DeviceGroup.objects.all()
    # Although I have used ModelViewSet, you could use any other one
    # I used this so that I don't need to write the code for create,
    # update, delete or list


# urls.py
router = routers.DefaultRouter()
router.register(r'device_group', DeviceGroupAPIView, base_name='device_group')
# this gives the following urls
# /device_group/ POST to create, GET to list    
# /device_group/(?<pk>\d+)/  GET to retrieve single DeviceGroup, PATCH/PUT to update it, and DELETE to delete it

This is the structure of the JSON to POST to create a new DeviceGroup with a bunch of Devices

{
   "name":"Group Name",
   "device_list":[
      {
         "name":"Device 1"
      },
      {
         "name":"Device 2"
      },
      {
         "name":"Device 3"
      }
   ]
}

Hope this helps

Also you should read up more about Django-Rest-Framework