0

I can't seem to figure out how to pass in an initial value to a serializer. I have a multitenant django site and I am trying to now get APIs setup. The client field exists but needs to be hidden and read only. I thought this worked like a form and a view in traditional django. I would normally pass in the get_initial in the view. I tried that first but it doesn't work. I think I need to get the value directly in the serializer but I can't seem to get it to work.

model:

class Location(ClientAwareModel):
    name = models.CharField(max_length=64, blank=True)
    address = models.CharField(max_length=64)
    address2 = models.CharField(max_length=64, blank=True)
    city = models.CharField(max_length=64)
    state = USStateField()
    zip_code = USZipCodeField()

class Client(models.Model):
    name = models.CharField(max_length=100)
    subdomain_prefix = models.CharField(max_length=100, unique=True)

    def __str__(self):
        return self.name

class ClientAwareModel(models.Model):
    client = models.ForeignKey(Client, on_delete=models.PROTECT)

    class Meta:
        abstract = True

def hostname_from_request(request):
    # split on `:` to remove port
    return request.get_host().split(':')[0].lower()

def client_from_request(request):
    hostname = hostname_from_request(request)
    subdomain_prefix = hostname.split('.')[0]
    return Client.objects.filter(subdomain_prefix=subdomain_prefix).first()

serializer (you can see all my failed attempts commented out:

class LocationSerializer(serializers.ModelSerializer):
    def get_client(self, obj):
        # return client_from_request(self.request)
        return client_from_request(self.context['request'])

    # client = serializers.SerializerMethodField('get_client')
    # client = serializers.SerializerMethodField()
    # client = serializers.Field(source='get_client', read_only=True)
    # client = serializers.ReadOnlyField(source='get_client')
    # client = serializers.PrimaryKeyRelatedField(read_only=True, default='get_client')
    client = serializers.PrimaryKeyRelatedField(read_only=True, source='get_client')
    # client = serializers.HiddenField(default=get_client(self))

    class Meta:
        model = Location
        fields = ['name', 'address', 'address2', 'city', 'state', 'zip_code', 'client']

viewset:

class LocationViewSet(viewsets.ModelViewSet):
    queryset = Location.objects.all()
    serializer_class = LocationSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        client = client_from_request(self.request)
        return super().get_queryset().filter(client=client)

you can see the different ways I tried to pass in the value but no matter what I do I get

IntegrityError at /locations/
null value in column "client_id" violates not-null constraint
HobbyHack
  • 33
  • 4
  • What is ```client_from_request```? – Baivaras Sep 06 '20 at 21:25
  • Client is a user in your system? could you please attach your `Client` model to your question? – Roham Sep 07 '20 at 04:42
  • what's the point of ClientAwareModel? Why not add a location FK to Client? – gregory Sep 08 '20 at 19:57
  • @gregory This is a multitenant site with a ton of models. When a user goes to the site they are only allowed to see the client records they have access to. I use ClientAware for inheritance and to force some of the security. There is a lot more to it than shown. I just pulled out a bit of the code to try and understand why the API wasn't working. – HobbyHack Sep 09 '20 at 16:08
  • @Baivaras client_from_request this is what determines what client site the user is accessing. i added the code if it helps. – HobbyHack Sep 09 '20 at 16:10
  • @Roham edited with the info – HobbyHack Sep 09 '20 at 16:10

2 Answers2

0

In your viewset you are trying to get Location by filtering Client when your Location model does not have a Client FK.

Right here:

def get_queryset(self):
        client = client_from_request(self.request)
        return super().get_queryset().filter(client=client) <----

As @gregory mentioned in the comment above, adding Foreign Key would solve your problem, then you can just simply add it to your serializer.

Baivaras
  • 438
  • 5
  • 17
  • Python is an object oriented language. Location inherits from ClientAwareModel which has a FK to client. This causes the Location object to have a client FK. The code is displaying all the existing locations just fine. The question was how " how to pass in an initial value to a serializer" – HobbyHack Sep 14 '20 at 15:00
  • @HobbyHack great, sorry did not see the inheritance before :) – Baivaras Sep 14 '20 at 16:31
0

One easy way to pass the client object to serializer is to pass it in perform_create method, something like:

from rest_framework import serializers


class LocationViewSet(viewsets.ModelViewSet):
    queryset = Location.objects.all()
    serializer_class = LocationSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        client = client_from_request(self.request)
        return super().get_queryset().filter(client=client)

    def perform_create(self, serializer):
        client = client_from_request(self.request)
        if not client:
            raise serializers.ValidationError("Client does not exist")
        serializer.save(client=client)

And also remove the client field from your serializer:

class LocationSerializer(serializers.ModelSerializer):

    class Meta:
        model = Location
        fields = ['name', 'address', 'address2', 'city', 'state', 'zip_code']
Roham
  • 1,970
  • 2
  • 6
  • 16