toel
toel

Reputation: 155

how to save foreign keys using django-rest-framework

I'm new to Django and I'm trying to save Unit and current_user foreign keys in the database based pk obtained but every time I try doing so but run into two types of error serializers.is_valid() raises an exception error or the serializer returns "Invalid data. Expected a dictionary, but got Unit."

I have tried a very very ugly way of bypassing serializers by getting rid but i get ee8452a4-2a82-4804-a010-cf2f5a41e006 must be an instance of SavedUnit.unit.I have also tried saving the foreign key directly using SavedUnit.objects.create() without luck

model.py

class SavedUnit(models.Model):
    """
    Saving units for later models
    relationship with units and users
    """
    id = models.UUIDField(primary_key=True, default=hex_uuid, editable=False)
    unit = models.ForeignKey(Unit, on_delete=models.CASCADE)
    user = models.ForeignKey('accounts.User', on_delete=models.CASCADE, related_name='user')
    published_at = models.DateTimeField(auto_now_add=True)

serializers.py

class SavedSerializer(serializers.ModelSerializer):
    unit = UnitSerializer()
    class Meta:
        model = SavedUnit
        fields = [
            'id',
            'unit'
        ]

views.py

class SavedUnitView(APIView):
    """
    Query all the unites saved
    """
    @staticmethod
    def get_unit(request, pk):
        try:
            return Unit.objects.get(pk=pk)
        except Unit.DoesNotExist:
            return Response(status=status.HTTP_400_BAD_REQUEST)
    @staticmethod
    def post(request, pk):
        if request.user.is_authenticated:
            unit = get_object_or_404(Unit, id=pk)
            serializers = SavedSerializer(data=unit)
            if serializers.is_valid(raise_exception=True):
                created = SavedUnit.objects.get_or_create(
                    user=request.user,
                    unit=unit)
                return Response(status=status.HTTP_201_CREATED)
        return Response(status=status.HTTP_401_UNAUTHORIZED)
    def get(self, request):
        units = SavedUnit.objects.filter(user=self.request.user.id)
        try:
            serializers = SavedSerializer(units, many=True)
            return Response(serializers.data, status=status.HTTP_200_OK)
        except Unit.DoesNotExist:
            return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)

Upvotes: 3

Views: 8467

Answers (2)

Lucas Weyne
Lucas Weyne

Reputation: 1152

Your code is using serializer only for validation, but it is possible use it to insert or update new objects on database calling serializer.save().

To save foreign keys using django-rest-framework you must put a related field on serializer to deal with it. Use PrimaryKeyRelatedField.

serializers.py

class SavedSerializer(serializers.ModelSerializer):
    unit_id = serializers.PrimaryKeyRelatedField(
        source='unit',
        queryset=Unit.objects.all()
    )
    unit = UnitSerializer(read_only=True)

    class Meta:
        model = SavedUnit
        fields = [
            'id',
            'unit_id',
            'unit'
        ]

views.py

class SavedUnitView(APIView):
    permission_classes = (permissions.IsAuthenticated,) # For not handling authorization mannually

    def post(request):
        serializer = SavedSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)  # Trigger Bad Request if errors exist
        serializer.save(user=request.user)         # Passing the current user
        return Response(serializer.data, status=status.HTTP_201_CREATED)

Now, the id of the unit will be passed in the request body like this

POST /saved-units/
Accept: application/json
Content-Type: application/json
Authorization: Token your-api-token

{ 
    "unit_id": 5  # Id of an existing Unit
}

Upvotes: 6

ruddra
ruddra

Reputation: 51948

Actually the problem is here:

def post(request, pk):
    if request.user.is_authenticated:
        unit = get_object_or_404(Unit, id=pk)
        serializers = SavedSerializer(data=unit)  <-- Here

You are passing a unit instance, but it should be request.data, like this:

serializers = SavedSerializer(data=request.data)

( I am unsure about what you are doing, if you already have PK, then why are you even using serializer? because you don't need it as you already have the unit, and you can access current user from request.user, which you are already doing. And I don't think you should use @staticmethod, you can declare the post method like this: def post(self, request, pk) and remove static method decorator)

Upvotes: 1

Related Questions