I feel like I'm chasing my tail here and so I've come to your fine folks to help me understand where I've screwed up and why my thinking on this must be flawed in some way.
I'm writing an API in DRF and while it doesn't have a lot of tables in the DB, there are many to many relationships in the DB which makes it feel complicated, or at least it makes it difficult to just view the db tables and intuitively understand what's related.
First my model. I'm getting this when attempting to POST a new object to the jobs
model. I'm needing to validate that the requesting worker has the permission to create a job
with the related target
and workergroup
Models:
class Workers(models.Model):
class Meta:
ordering = ['id']
workerid = models.CharField(max_length=16, verbose_name="Worker ID", unique=True, default="Empty")
customer = models.ForeignKey(Customers, on_delete=models.DO_NOTHING, default=1)
workername = models.CharField(max_length=64, verbose_name="Worker Friendly Name", default="")
datecreated = models.DateTimeField(auto_now_add=True)
awsarn = models.CharField(max_length=60, verbose_name="ARN Name of Worker", blank=True, null=True)
customerrights = models.ManyToManyField(Customers, related_name="access_rights", default="")
class Targets(models.Model):
class Meta:
ordering = ['id']
customer = models.ForeignKey(Customers, on_delete=models.SET_NULL, default=1, null=True)
friendly_name = models.CharField(max_length=70, verbose_name="Target Friendly Name", unique=False)
hostname = models.CharField(max_length=120, verbose_name="Target Hostname", default="")
ipaddr = models.GenericIPAddressField(protocol='both', unpack_ipv4=True, default="", null=True)
class WorkerGroups(models.Model):
class Meta:
ordering = ['id']
name = models.CharField(max_length=60, default="Default Group")
workers = models.ManyToManyField(Workers)
class Jobs(models.Model):
target = models.ForeignKey(Targets, on_delete=models.DO_NOTHING)
datecreated = models.DateTimeField(auto_now_add=True)
startdate = models.DateTimeField()
enddate = models.DateTimeField(null=True)
frequency = models.TimeField(default='00:05')
workergroup = models.ForeignKey(WorkerGroups, on_delete=models.DO_NOTHING)
jobdefinition = models.ForeignKey(JobDefinitions, on_delete=models.DO_NOTHING)
In my serializers I have a JobSerializer
which is referencing PrimaryKeyRelatedField classes which I think should have the effect of limiting the queryset which validates those related models. BTW, the customerrightsids
are being built in the view and that seems to work fine for all other models.
Serializers:
def get_queryset(self):
print("In Custom TargetPK get_queryset")
queryset = Targets.objects.filter(customer_id__in=self.context['auth'].customerrightsids)
if isinstance(queryset, (QuerySet, Manager)):
queryset = queryset.all()
return queryset
class WorkerGroupPKSerializer(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
print("In Custom WorkerGroupPK get_queryset")
queryset = WorkerGroups.objects.filter(workers__customer_id__in=self.context['auth'].customerrightsids)
if isinstance(queryset, (QuerySet, Manager)):
queryset = queryset.all()
return queryset
class JobSerializer(serializers.ModelSerializer):
workergroup = WorkerGroupPKSerializer(many=False) # Commenting this out removes error
target = TargetPKSerializer(many=False) # This seems to work fine even though it's similar to the line above
class Meta:
model = Jobs
fields = '__all__'
def create(self, validated_data):
print(self.context['auth'])
return super().create(validated_data)
There's nothing special in the create method of the viewset object. It's taking the request and passing a couple into the ViewSet and updating its context. I can share that if needed, but that doesn't seem to be where the issue is.
So finally, for the error. When I perform a POST
to /jobs/
I'm getting the following:
Error:
MultipleObjectsReturned at /jobs/
get() returned more than one WorkerGroups -- it returned 2!
The error clearly states that I'm getting multiple WorkerGroups returned in a get() but I don't know where or how to resolve that in this case.
It is clearly a problem with the WorkerGroupPKSerializer. If I comment out the reference to it from the JobSerializer the error goes away. That stops the validation of that field though, so that's not a workable solution!