asojidaiod
asojidaiod

Reputation: 93

How to create new model with foreignkey in django

class Schedule(models.Model):
    name = models.CharField(max_length=50)


class ScheduleDefinition(models.Model):
    schedule = models.ForeignKey(Schedule, on_delete=models.DO_NOTHING)
    config = JSONField(default=dict, blank=True)

These are my models. I am trying to create a new ScheduleDefinition(The Schedule already exists and I know the ID I want to use for my foreign_key). I have a predefined Schedule id that I want to use, but it is not working..

Posting this body:

{
    "schedule_id": 1,
    "config": {
        "CCC": "ccc"
    }
}

Error I get: null value in column "schedule_id" violates not-null constraint

What am I doing wrong? When I create new ScheduleDefinition models, the Schedule model will already be created previously. I am never going to be creating new Schedule's when I create new ScheduleDefinition's.

Serializer:

class ScheduleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Schedule
        fields = ['id', 'name']


class ScheduleDefinitionSerializer(serializers.ModelSerializer):
    schedule = ScheduleSerializer(read_only=True, many=False)

    class Meta:
        model = ScheduleDefinition
        fields = ['schedule', 'config']

View:

from rest_framework import generics
from .models import Schedule, ScheduleDefinition
from .serializers import ScheduleSerializer, ScheduleDefinitionSerializer


class ScheduleList(generics.ListAPIView):
    queryset = Schedule.objects.all()
    serializer_class = ScheduleSerializer


class ScheduleDefinitionList(generics.ListCreateAPIView):
    queryset = ScheduleDefinition.objects.all()
    serializer_class = ScheduleDefinitionSerializer


class ScheduleDefinitionDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = ScheduleDefinition.objects.all()
    serializer_class = ScheduleDefinitionSerializer

View error:

File "/app/server/schedules/serializers.py", line 13, in ScheduleDefinitionSerializer
   schedule_id = serializers.PrimaryKeyRelatedField(source="schedule")
File "/usr/local/lib/python3.7/dist-packages/rest_framework/relations.py", line 247, in __init__
  super().__init__(**kwargs)
File "/usr/local/lib/python3.7/dist-packages/rest_framework/relations.py", line 108, in __init__
    'Relational field must provide a `queryset` argument, '
AssertionError: Relational field must provide a `queryset` argument, override `get_queryset`, or set read_only=`True`.

Upvotes: 1

Views: 266

Answers (1)

Cosine Nomine
Cosine Nomine

Reputation: 71

You've specified Schedule as required (not null), but you aren't actually posting any information to it. Currently your serializer is expecting information in the form:

{
    "schedule": {
        "name": "Foo" 
    },
    "config": {...}
}

schedule_id is being discarded when you post. Furthermore, you've specified that schedule is a read only field, meaning even if you posted a schedule, it would still be discarded.

If you'd like to post to foreign keys, you'll either need to specially handle it by manually writing the create/update logic for a writable nested serializer (which can be a bit of a hassle), or use a different (writable) foreignkey field serializer and serialize your other read-only data another way.

For example, the following setup should work (untested) with the data you're currently trying to POST:

class ScheduleDefinitionSerializer(serializers.ModelSerializer):
    schedule = serializers.PrimaryKeyRelatedField(
        queryset=Schedule.objects.all()
    )
    schedule_name = serializers.CharField(read_only=True, source="schedule.name")

    class Meta:
        model = ScheduleDefinition
        fields = ['schedule', 'schedule_name', 'config']

With this your post should work, and you'll still have read-only access to the corresponding schedule's name via the schedule_name field in your list/detail views.


EDIT

My earlier version of the code would not have been compatible with the original desired POST data. The following should work without altering the POST

class ScheduleDefinitionSerializer(serializers.ModelSerializer):
    schedule = ScheduleSerializer(many=False, read_only=True)
    schedule_id = serializers.PrimaryKeyRelatedField(
        source="schedule",
        queryset=Schedule.objects.all()
    )

    class Meta:
        model = ScheduleDefinition
        fields = ['schedule', 'schedule_id', 'config']

Upvotes: 1

Related Questions