0

How can i update the same group if the name of group user wants to create matches with already created group? If i want to update instead of showing error where should i work on? Is it on validate function or create function?

Here is my serializer

class DeviceGroupSerializer(serializers.ModelSerializer):
    id = serializers.UUIDField(source='token', format='hex', read_only=True)
    devices = DeviceIdSerializer(many=True)

    class Meta:
        model = DeviceGroup
        fields = ['id','name', 'devices',]

    def validate(self, data):
        errors = {}
        try:
            name = data['name']
            if not bool(name):
                #empty or null
                errors['name'] = 'Name cannot be empty'
        except KeyError:
            if not (self.instance and bool(self.instance.name)):
                errors['name'] = 'Name is required'

        if len(data.get('devices', [])) == 0:
            errors['devices'] = 'Device(s) should be specified.'

        if bool(errors):
            raise serializers.ValidationError(errors)
        return data

    def create(self, validated_data):
        # for create - there is always name; we have already checked that in validation
        # TODO Further check for group-name clash - if yes, update the same group
        owner = validated_data['owner']
        name = validated_data['name']
        group = DeviceGroup.objects.create(owner=owner, name=name)

        tokens = [d['token'] for d in validated_data['devices'] ]
        BaseDevice.objects.filter(token__in=tokens, owner=owner).update(group=group)
        return group

def update(self, instance, validated_data):
        # for update - there may or may not be name
        # if it does, it refers rename

        owner = validated_data['owner']
        name = validated_data.get('name', None)
        if not name is None:
            instance.update(name=name)

        tokens = [d['token'] for d in validated_data['devices'] ]
        BaseDevice.objects.filter(token__in=tokens, owner=owner).update(group=instance)
        return instance
pythonBeginner
  • 781
  • 2
  • 12
  • 27

1 Answers1

1

You want update_or_create():

A convenience method for updating an object with the given kwargs, creating a new one if necessary. The defaults is a dictionary of (field, value) pairs used to update the object.

Based on what you've shared, this would look something like the following, assuming you want to update the owner on DeviceGroup, if a DeviceGroup with the given name already exists:

    def create(self, validated_data):
        # for create - there is always name; we have already checked that in validation
        # TODO Further check for group-name clash - if yes, update the same group
        owner = validated_data['owner']
        name = validated_data['name']

        # created is a boolean telling us if a new DeviceGroup was created
        group, created = DeviceGroup.objects.update_or_create(name=name, defaults={'owner': owner})

        tokens = [d['token'] for d in validated_data['devices'] ]
        BaseDevice.objects.filter(token__in=tokens, owner=owner).update(group=group)
        return group
Jens Astrup
  • 2,415
  • 2
  • 14
  • 20
  • If i have update function, is it better to handle the name clash in create function or update ? – pythonBeginner Mar 11 '17 at 03:17
  • I have device called device1, device2 where device 1 is in Important group and when i clicked device2 and create a new group Important, the device2 should move to already created Important group instead of creating another group with same name – pythonBeginner Mar 11 '17 at 03:32
  • I had tried update_or_create but got an error of TypeError: int() argument must be a string, a bytes-like object or a number, not 'tuple' – pythonBeginner Mar 11 '17 at 03:32
  • 1
    Sorry it was fault in mine code. It is working now. One question, though, if i now create a group Important then the device will be moved to already created group called Important instead of creating another Important group. But if i create a group important, it creates a new group. How to handle uppercase lowercase issue? – pythonBeginner Mar 11 '17 at 03:48
  • 1
    The quickest way to solve that would be to do something like `group_exists = DeviceGroup.objects.filter(name__iexact=name).exists()` or use a `try:catch` block, where you try to get the `DeviceGroup` (with `name__iexact`), if `DeviceGroup.DoesNotExist` is raised, just create it. You can also look at some [more options here](http://stackoverflow.com/questions/7773341/case-insensitive-unique-model-fields-in-django), but they're a bit more complex than just modifying your query. – Jens Astrup Mar 11 '17 at 13:41
  • 1
    I understand the point and also thanks for the informative solution. – pythonBeginner Mar 11 '17 at 14:58