Discussion:
Advice on disabling validators for Nested serializers
Arjun Nambiar
2018-08-16 19:02:02 UTC
Permalink
My models.py

from django.db import models
from netfields import InetAddressField, CidrAddressField, NetManager

class IP(models.Model):
"""
Stores IP Objects
"""
# name is the reverse lookup of an IP if it exists else it is IP_<value>
name = models.CharField(max_length=50, unique=True)
value = InetAddressField(unique=True)

objects = NetManager()

def __unicode__(self):
return f'{self.name}'


class CIDR(models.Model):
"""
Stores CIDR Objects
"""
# name of a subnet is NETWORK_<value>
name = models.CharField(max_length=50, unique=True)
value = CidrAddressField(unique=True)

objects = NetManager()

def __unicode__(self):
return f'{self.name}'


class IPGroup(models.Model):
"""
Stores IP Groups
"""
name = models.CharField(max_length=50, unique=True)
ips = models.ManyToManyField(IP, through=IPGroupToIP)
cidrs = models.ManyToManyField(CIDR, through=IPGroupToCIDR)

def __unicode__(self):
return f'{self.name}'


My serializers.py

class IPSerializer(serializers.ModelSerializer):
class Meta:
model = IP
fields = ['id', 'name', 'value']


class CIDRSerializer(serializers.ModelSerializer):
class Meta:
model = CIDR
fields = ['id', 'name', 'value']


class IPGroupSerializer(serializers.ModelSerializer):
"""
This Serializer is responsible for parsing IPGroup POST Request.
It validates the JSON Body and throws error if any of the fields does not meet requirements.
"""
ips = IPSerializer(many=True)
cidrs = CIDRSerializer(many=True)

class Meta:
model = IPGroup
depth = 1
fields = '__all__'

def validate(self, data):
mandatory_list = ['ips', 'cidrs']
if not any(data[item] for item in mandatory_list):
raise serializers.ValidationError("Atleast one of the fields(IP, Subnets) should be present")

if ' ' in data['name']:
raise serializers.ValidationError("IPGroups cannot have spaces in their names")

return data

@transaction.atomic()
def create(self, validated_data):
# Pop out ips, subnet since these are M2M and have to be created separately
ips_data = validated_data.pop('ips', None)
cidrs_data = validated_data.pop('cidrs', None)

# First create an IPGroup with the required name
ip_group = IPGroup.objects.create(name=validated_data['name'])

# Now create the M2M relationships
if ips_data:
for ip in ips_data:
# Get the ip object id if already created or create it on the fly and save it
ip_obj, created = IP.objects.get_or_create(name=ip['name'], value=ip['value'])
IPGroupToIP.objects.create(ip_group_id=ip_group.id, ip_id=ip_obj.id)

if cidrs_data:
for cidr in cidrs_data:
# Get the subnet object id if already created or create it on the fly and save it
cidr_obj, created = CIDR.objects.get_or_create(name=f"NETWORK_{cidr['name']}", value=cidr['value'])
IPGroupToCIDR.objects.create(ip_group_id=ip_group.id, cidr_id=cidr_obj.id)

return ip_group



My views.py

class IPGroupCreateView(generics.CreateAPIView):
queryset = IPGroup.objects.get_queryset()
serializer_class = IPGroupSerializer


This however keeps giving me a validation error saying that IP or CIDR
already exists. However, I am using the get_or_create to create IPs and
CIDRs and hence should not be getting this error. I am assuming that this
happening
because of the nested model serializer's unique field validation kicking
in. How would I disable the unique key validation for IPs and CIDRs only
when i am creating an IPGroup ?
--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Ryan Kilby
2018-08-17 18:31:26 UTC
Permalink
Hi Arjun,

It would be helpful to see the actual stack trace associated with the
error.
Post by Arjun Nambiar
My models.py
from django.db import models
from netfields import InetAddressField, CidrAddressField, NetManager
"""
Stores IP Objects
"""
# name is the reverse lookup of an IP if it exists else it is IP_<value>
name = models.CharField(max_length=50, unique=True)
value = InetAddressField(unique=True)
objects = NetManager()
return f'{self.name}'
"""
Stores CIDR Objects
"""
# name of a subnet is NETWORK_<value>
name = models.CharField(max_length=50, unique=True)
value = CidrAddressField(unique=True)
objects = NetManager()
return f'{self.name}'
"""
Stores IP Groups
"""
name = models.CharField(max_length=50, unique=True)
ips = models.ManyToManyField(IP, through=IPGroupToIP)
cidrs = models.ManyToManyField(CIDR, through=IPGroupToCIDR)
return f'{self.name}'
My serializers.py
model = IP
fields = ['id', 'name', 'value']
model = CIDR
fields = ['id', 'name', 'value']
"""
This Serializer is responsible for parsing IPGroup POST Request.
It validates the JSON Body and throws error if any of the fields does not meet requirements.
"""
ips = IPSerializer(many=True)
cidrs = CIDRSerializer(many=True)
model = IPGroup
depth = 1
fields = '__all__'
mandatory_list = ['ips', 'cidrs']
raise serializers.ValidationError("Atleast one of the fields(IP, Subnets) should be present")
raise serializers.ValidationError("IPGroups cannot have spaces in their names")
return data
@transaction.atomic()
# Pop out ips, subnet since these are M2M and have to be created separately
ips_data = validated_data.pop('ips', None)
cidrs_data = validated_data.pop('cidrs', None)
# First create an IPGroup with the required name
ip_group = IPGroup.objects.create(name=validated_data['name'])
# Now create the M2M relationships
# Get the ip object id if already created or create it on the fly and save it
ip_obj, created = IP.objects.get_or_create(name=ip['name'], value=ip['value'])
IPGroupToIP.objects.create(ip_group_id=ip_group.id, ip_id=ip_obj.id)
# Get the subnet object id if already created or create it on the fly and save it
cidr_obj, created = CIDR.objects.get_or_create(name=f"NETWORK_{cidr['name']}", value=cidr['value'])
IPGroupToCIDR.objects.create(ip_group_id=ip_group.id, cidr_id=cidr_obj.id)
return ip_group
My views.py
queryset = IPGroup.objects.get_queryset()
serializer_class = IPGroupSerializer
This however keeps giving me a validation error saying that IP or CIDR
already exists. However, I am using the get_or_create to create IPs and
CIDRs and hence should not be getting this error. I am assuming that this
happening
because of the nested model serializer's unique field validation kicking
in. How would I disable the unique key validation for IPs and CIDRs only
when i am creating an IPGroup ?
--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Arjun Nambiar
2018-08-17 21:41:02 UTC
Permalink
Hi Ryan,

It is basically a serializer validation error stating that "IP already
exists" so there is no stack trace. I need to somehow disable unique key
validation check for IPs when being used as a nested serializer in IPGroups.
Post by Ryan Kilby
Hi Arjun,
It would be helpful to see the actual stack trace associated with the
error.
Post by Arjun Nambiar
My models.py
from django.db import models
from netfields import InetAddressField, CidrAddressField, NetManager
"""
Stores IP Objects
"""
# name is the reverse lookup of an IP if it exists else it is IP_<value>
name = models.CharField(max_length=50, unique=True)
value = InetAddressField(unique=True)
objects = NetManager()
return f'{self.name}'
"""
Stores CIDR Objects
"""
# name of a subnet is NETWORK_<value>
name = models.CharField(max_length=50, unique=True)
value = CidrAddressField(unique=True)
objects = NetManager()
return f'{self.name}'
"""
Stores IP Groups
"""
name = models.CharField(max_length=50, unique=True)
ips = models.ManyToManyField(IP, through=IPGroupToIP)
cidrs = models.ManyToManyField(CIDR, through=IPGroupToCIDR)
return f'{self.name}'
My serializers.py
model = IP
fields = ['id', 'name', 'value']
model = CIDR
fields = ['id', 'name', 'value']
"""
This Serializer is responsible for parsing IPGroup POST Request.
It validates the JSON Body and throws error if any of the fields does not meet requirements.
"""
ips = IPSerializer(many=True)
cidrs = CIDRSerializer(many=True)
model = IPGroup
depth = 1
fields = '__all__'
mandatory_list = ['ips', 'cidrs']
raise serializers.ValidationError("Atleast one of the fields(IP, Subnets) should be present")
raise serializers.ValidationError("IPGroups cannot have spaces in their names")
return data
@transaction.atomic()
# Pop out ips, subnet since these are M2M and have to be created separately
ips_data = validated_data.pop('ips', None)
cidrs_data = validated_data.pop('cidrs', None)
# First create an IPGroup with the required name
ip_group = IPGroup.objects.create(name=validated_data['name'])
# Now create the M2M relationships
# Get the ip object id if already created or create it on the fly and save it
ip_obj, created = IP.objects.get_or_create(name=ip['name'], value=ip['value'])
IPGroupToIP.objects.create(ip_group_id=ip_group.id, ip_id=ip_obj.id)
# Get the subnet object id if already created or create it on the fly and save it
cidr_obj, created = CIDR.objects.get_or_create(name=f"NETWORK_{cidr['name']}", value=cidr['value'])
IPGroupToCIDR.objects.create(ip_group_id=ip_group.id, cidr_id=cidr_obj.id)
return ip_group
My views.py
queryset = IPGroup.objects.get_queryset()
serializer_class = IPGroupSerializer
This however keeps giving me a validation error saying that IP or CIDR
already exists. However, I am using the get_or_create to create IPs and
CIDRs and hence should not be getting this error. I am assuming that this
happening
because of the nested model serializer's unique field validation kicking
in. How would I disable the unique key validation for IPs and CIDRs only
when i am creating an IPGroup ?
--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Ryan Kilby
2018-08-17 22:37:07 UTC
Permalink
Gotcha, I was assuming that the code was failing at the get_or_create
statement, not during validation.

Taking a step back, what are you trying to accomplish? Are you trying to
create both the IP groups and IP/CIDR instances at the same time, or are
you just trying to link/relate the instances to the new IP group?
Post by Arjun Nambiar
Hi Ryan,
It is basically a serializer validation error stating that "IP already
exists" so there is no stack trace. I need to somehow disable unique key
validation check for IPs when being used as a nested serializer in IPGroups.
Post by Ryan Kilby
Hi Arjun,
It would be helpful to see the actual stack trace associated with the
error.
Post by Arjun Nambiar
My models.py
from django.db import models
from netfields import InetAddressField, CidrAddressField, NetManager
"""
Stores IP Objects
"""
# name is the reverse lookup of an IP if it exists else it is IP_<value>
name = models.CharField(max_length=50, unique=True)
value = InetAddressField(unique=True)
objects = NetManager()
return f'{self.name}'
"""
Stores CIDR Objects
"""
# name of a subnet is NETWORK_<value>
name = models.CharField(max_length=50, unique=True)
value = CidrAddressField(unique=True)
objects = NetManager()
return f'{self.name}'
"""
Stores IP Groups
"""
name = models.CharField(max_length=50, unique=True)
ips = models.ManyToManyField(IP, through=IPGroupToIP)
cidrs = models.ManyToManyField(CIDR, through=IPGroupToCIDR)
return f'{self.name}'
My serializers.py
model = IP
fields = ['id', 'name', 'value']
model = CIDR
fields = ['id', 'name', 'value']
"""
This Serializer is responsible for parsing IPGroup POST Request.
It validates the JSON Body and throws error if any of the fields does not meet requirements.
"""
ips = IPSerializer(many=True)
cidrs = CIDRSerializer(many=True)
model = IPGroup
depth = 1
fields = '__all__'
mandatory_list = ['ips', 'cidrs']
raise serializers.ValidationError("Atleast one of the fields(IP, Subnets) should be present")
raise serializers.ValidationError("IPGroups cannot have spaces in their names")
return data
@transaction.atomic()
# Pop out ips, subnet since these are M2M and have to be created separately
ips_data = validated_data.pop('ips', None)
cidrs_data = validated_data.pop('cidrs', None)
# First create an IPGroup with the required name
ip_group = IPGroup.objects.create(name=validated_data['name'])
# Now create the M2M relationships
# Get the ip object id if already created or create it on the fly and save it
ip_obj, created = IP.objects.get_or_create(name=ip['name'], value=ip['value'])
IPGroupToIP.objects.create(ip_group_id=ip_group.id, ip_id=ip_obj.id)
# Get the subnet object id if already created or create it on the fly and save it
cidr_obj, created = CIDR.objects.get_or_create(name=f"NETWORK_{cidr['name']}", value=cidr['value'])
IPGroupToCIDR.objects.create(ip_group_id=ip_group.id, cidr_id=cidr_obj.id)
return ip_group
My views.py
queryset = IPGroup.objects.get_queryset()
serializer_class = IPGroupSerializer
This however keeps giving me a validation error saying that IP or CIDR
already exists. However, I am using the get_or_create to create IPs and
CIDRs and hence should not be getting this error. I am assuming that this
happening
because of the nested model serializer's unique field validation kicking
in. How would I disable the unique key validation for IPs and CIDRs only
when i am creating an IPGroup ?
--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Xavier Ordoquy
2018-08-18 07:19:10 UTC
Permalink
Hi,

I wrote about this a while ago.
Everything should be explained here: https://medium.com/django-rest-framework/dealing-with-unique-constraints-in-nested-serializers-dade33b831d9

Regards,
Xavier O.
Linovia.
Post by Arjun Nambiar
Hi Ryan,
It is basically a serializer validation error stating that "IP already exists" so there is no stack trace. I need to somehow disable unique key validation check for IPs when being used as a nested serializer in IPGroups.
Hi Arjun,
It would be helpful to see the actual stack trace associated with the error.
My models.py
from django.db import models
from netfields import InetAddressField, CidrAddressField, NetManager
"""
Stores IP Objects
"""
# name is the reverse lookup of an IP if it exists else it is IP_<value>
name = models.CharField(max_length=50, unique=True)
value = InetAddressField(unique=True)
objects = NetManager()
return f'{self.name <http://self.name/>}'
"""
Stores CIDR Objects
"""
# name of a subnet is NETWORK_<value>
name = models.CharField(max_length=50, unique=True)
value = CidrAddressField(unique=True)
objects = NetManager()
return f'{self.name <http://self.name/>}'
"""
Stores IP Groups
"""
name = models.CharField(max_length=50, unique=True)
ips = models.ManyToManyField(IP, through=IPGroupToIP)
cidrs = models.ManyToManyField(CIDR, through=IPGroupToCIDR)
return f'{self.name <http://self.name/>}'
My serializers.py
model = IP
fields = ['id', 'name', 'value']
model = CIDR
fields = ['id', 'name', 'value']
"""
This Serializer is responsible for parsing IPGroup POST Request.
It validates the JSON Body and throws error if any of the fields does not meet requirements.
"""
ips = IPSerializer(many=True)
cidrs = CIDRSerializer(many=True)
model = IPGroup
depth = 1
fields = '__all__'
mandatory_list = ['ips', 'cidrs']
raise serializers.ValidationError("Atleast one of the fields(IP, Subnets) should be present")
raise serializers.ValidationError("IPGroups cannot have spaces in their names")
return data
@transaction.atomic()
# Pop out ips, subnet since these are M2M and have to be created separately
ips_data = validated_data.pop('ips', None)
cidrs_data = validated_data.pop('cidrs', None)
# First create an IPGroup with the required name
ip_group = IPGroup.objects.create(name=validated_data['name'])
# Now create the M2M relationships
# Get the ip object id if already created or create it on the fly and save it
ip_obj, created = IP.objects.get_or_create(name=ip['name'], value=ip['value'])
IPGroupToIP.objects.create(ip_group_id=ip_group.id <http://ip_group.id/>, ip_id=ip_obj.id <http://ip_obj.id/>)
# Get the subnet object id if already created or create it on the fly and save it
cidr_obj, created = CIDR.objects.get_or_create(name=f"NETWORK_{cidr['name']}", value=cidr['value'])
IPGroupToCIDR.objects.create(ip_group_id=ip_group.id <http://ip_group.id/>, cidr_id=cidr_obj.id <http://cidr_obj.id/>)
return ip_group
My views.py
queryset = IPGroup.objects.get_queryset()
serializer_class = IPGroupSerializer
This however keeps giving me a validation error saying that IP or CIDR already exists. However, I am using the get_or_create to create IPs and CIDRs and hence should not be getting this error. I am assuming that this happening
because of the nested model serializer's unique field validation kicking in. How would I disable the unique key validation for IPs and CIDRs only when i am creating an IPGroup ?
--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
For more options, visit https://groups.google.com/d/optout <https://groups.google.com/d/optout>.
--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Arjun Nambiar
2018-08-17 21:38:07 UTC
Permalink
Hi Ryan,

It is actually just a validation error so there is no stack trace. It says
that the "name already exists". I basically just need to disable unique key
validation check for IPs when being used as a nested Model serializer.

Regards,
Arjun
Post by Arjun Nambiar
My models.py
from django.db import models
from netfields import InetAddressField, CidrAddressField, NetManager
"""
Stores IP Objects
"""
# name is the reverse lookup of an IP if it exists else it is IP_<value>
name = models.CharField(max_length=50, unique=True)
value = InetAddressField(unique=True)
objects = NetManager()
return f'{self.name}'
"""
Stores CIDR Objects
"""
# name of a subnet is NETWORK_<value>
name = models.CharField(max_length=50, unique=True)
value = CidrAddressField(unique=True)
objects = NetManager()
return f'{self.name}'
"""
Stores IP Groups
"""
name = models.CharField(max_length=50, unique=True)
ips = models.ManyToManyField(IP, through=IPGroupToIP)
cidrs = models.ManyToManyField(CIDR, through=IPGroupToCIDR)
return f'{self.name}'
My serializers.py
model = IP
fields = ['id', 'name', 'value']
model = CIDR
fields = ['id', 'name', 'value']
"""
This Serializer is responsible for parsing IPGroup POST Request.
It validates the JSON Body and throws error if any of the fields does not meet requirements.
"""
ips = IPSerializer(many=True)
cidrs = CIDRSerializer(many=True)
model = IPGroup
depth = 1
fields = '__all__'
mandatory_list = ['ips', 'cidrs']
raise serializers.ValidationError("Atleast one of the fields(IP, Subnets) should be present")
raise serializers.ValidationError("IPGroups cannot have spaces in their names")
return data
@transaction.atomic()
# Pop out ips, subnet since these are M2M and have to be created separately
ips_data = validated_data.pop('ips', None)
cidrs_data = validated_data.pop('cidrs', None)
# First create an IPGroup with the required name
ip_group = IPGroup.objects.create(name=validated_data['name'])
# Now create the M2M relationships
# Get the ip object id if already created or create it on the fly and save it
ip_obj, created = IP.objects.get_or_create(name=ip['name'], value=ip['value'])
IPGroupToIP.objects.create(ip_group_id=ip_group.id, ip_id=ip_obj.id)
# Get the subnet object id if already created or create it on the fly and save it
cidr_obj, created = CIDR.objects.get_or_create(name=f"NETWORK_{cidr['name']}", value=cidr['value'])
IPGroupToCIDR.objects.create(ip_group_id=ip_group.id, cidr_id=cidr_obj.id)
return ip_group
My views.py
queryset = IPGroup.objects.get_queryset()
serializer_class = IPGroupSerializer
This however keeps giving me a validation error saying that IP or CIDR
already exists. However, I am using the get_or_create to create IPs and
CIDRs and hence should not be getting this error. I am assuming that this
happening
because of the nested model serializer's unique field validation kicking
in. How would I disable the unique key validation for IPs and CIDRs only
when i am creating an IPGroup ?
--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Loading...