Jin Nii Sama
Jin Nii Sama

Reputation: 747

Django api call in view unable to save for foreign key userId

What i am doing is django 2 project call api to django 1 project to book an appointment in Appointment table and save the details into BookAppt table.

I am trying to save the data from api call to an api using view. Everything else works but however, the foreign key to user model userId give me this error : ValueError: Cannot assign "4": "BookAppt.patientId" must be a "MyUser" instance.

I don't know what is the problem as i did say same post with the same value to that BookAppt table API and it works normally. But when saving using the API call, it gave me this error.

Updated error

After updating my code based on the answer given, i got this error now. {"patientId":["This field is required."]} Still have no idea why even though i specify it already.

Please help as i have been stuck at this part for 2 days now.

Here is my code:

Updated code, do note that it is django 2 calling to django 1

model.py

django 1

class Appointments (models.Model):
    patientId = models.IntegerField()
    clinicId = models.CharField(max_length=10)
    date = models.DateField()
    time = models.TimeField()
    created = models.DateTimeField(auto_now_add=True)
    ticketNo = models.IntegerField()

    STATUS_CHOICES = (
        ("Booked", "Booked"),
        ("Done", "Done"),
        ("Cancelled", "Cancelled"),
    )
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="Booked")

django 2

class MyUser(AbstractUser):
    userId = models.AutoField(primary_key=True)
    gender = models.CharField(max_length=6, blank=True, null=True)
    nric = models.CharField(max_length=9, blank=True, null=True)
    birthday = models.DateField(blank=True, null=True)
    birthTime = models.TimeField(blank=True, null=True)

class BookAppt(models.Model):
    clinicId = models.CharField(max_length=20)
    patientId = models.ForeignKey(MyUser, on_delete=models.CASCADE)
    scheduleTime = models.DateTimeField()
    ticketNo = models.CharField(max_length=5)
    status = models.CharField(max_length=20)

serializer

django 1

class AppointmentsSerializer(serializers.ModelSerializer):

    class Meta:
        model = Appointments
        fields = ('id', 'patientId', 'clinicId', 'date', 'time', 'created', 'ticketNo', 'status')

django 2

class MyUserSerializer(serializers.ModelSerializer):

    class Meta:
        model = MyUser
        fields = ('userId', 'username', 'email', 'first_name', 'last_name', 'gender', 'nric', 'birthday', 'birthTime')
        read_only_fields = ('userId',)


class BookApptSerializer(serializers.ModelSerializer):
    patientId = MyUserSerializer(many=False)

    class Meta:
        model = BookAppt
        fields = ('id', 'patientId', 'clinicId', 'scheduleTime', 'ticketNo', 'status')

view.py

django 1

class AppointmentsViewSet(viewsets.ModelViewSet):
    permission_classes = [AllowAny]
    queryset = Appointments.objects.all()
    serializer_class = AppointmentsSerializer

django 2

@csrf_exempt
def my_django_view(request):

    if request.method == 'POST':
        r = requests.post('http://127.0.0.1:8000/api/makeapp/', data=request.POST)
    else:
        r = requests.get('http://127.0.0.1:8000/api/makeapp/', data=request.GET)

    if r.status_code == 201 and request.method == 'POST':
        data = r.json()
        patient = request.data['patientId']
        patientId = MyUser.objects.get(id=patient)

        saveget_attrs = {
            "patientId": patientId,
            "clinicId": data["clinicId"],
            "scheduleTime": data["created"],
            "ticketNo": data["ticketNo"],
            "status": data["status"],
        }
        saving = BookAppt.objects.create(**saveget_attrs)

        return HttpResponse(r.text)
    elif r.status_code == 200:  # GET response
        return HttpResponse(r.json())
    else:
        return HttpResponse(r.text)


class BookApptViewSet(viewsets.ModelViewSet):
    permission_classes = [AllowAny]
    queryset = BookAppt.objects.all()
    serializer_class = BookApptSerializer

Upvotes: 1

Views: 463

Answers (1)

dmitryro
dmitryro

Reputation: 3506

Your Appointments model - you work with MyUser model rather than its id, so it makes sense to give names of your foreign key-referenced models as patient rather than patient_id, so

class Appointments (models.Model):
    patient = models.ForeignKey(MyUser, on_delete=models.CASCADE)
    clinicId = models.CharField(max_length=10)
    date = models.DateField()
    time = models.TimeField()
    created = models.DateTimeField(auto_now_add=True)
    ticketNo = models.IntegerField()

    STATUS_CHOICES = (
        ("Booked", "Booked"),
        ("Done", "Done"),
        ("Cancelled", "Cancelled"),
    )
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="Booked")

Than, in your serializers.py:

class MyUserSerializer(serializer.ModelSerializer):
    # Whatever you like to serialize
    class Meta:
        model = MyUser

Then, the Appintment serializer needs to be adjusted as well:

class AppointmentsSerializer(serializers.ModelSerializer):
    # Notice this - the patient is serailized using MyUserSerializer
    patient = MyUserSerializer(many=False, read_only=False)
    valid_time_formats = ['%H:%M', '%I:%M%p', '%I:%M %p']
    time = serializers.TimeField(format='%I:%M %p', input_formats=valid_time_formats)
    created = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", read_only=True)

    class Meta:
        model = Appointments
        # Now, you work with patient object, not with patientId
        # clinicId can still be an Id as long as it's not a foreign key
        fields = ('id', 'patient', 'clinicId', 'date', 'time', 'created', 'ticketNo', 'status')



    class BookApptSerializer(serializers.ModelSerializer):
        # Same here - the patient is serailized using MyUserSerializer
        patient = MyUserSerializer(many=False, read_only=False)
        valid_time_formats = ['%Y-%m-%d %H:%M', '%Y-%m-%d %I:%M%p', '%Y-%m-%d %I:%M %p']
        scheduleTime = serializers.DateTimeField(format="%Y-%m-%d %I:%M %p", input_formats=valid_time_formats)

        class Meta:
            model = BookAppt
            fields = ('id', 'patient', 'clinicId', 'scheduleTime', 'ticketNo', 'status')

Now, in your view you'll have to explicitly read the patient:

patient = MyUser.objects.get(id=patientId) 

provided pateintId is an integer value of the id you're sure it exists, and then

saveget_attrs = {
    # This has to be object, not the id, that's why it was breaking
    "patient": patient, 
    "clinicId": data["clinicId"],
    "scheduleTime": data["created"],
    "ticketNo": data["ticketNo"],
    "status": data["status"],
}
savingIntoBookApptTable = BookAppt.objects.create(**saveget_attrs)
return HttpResponse(r.text)

Regarding the datetime field you can use in your Appointments model:

time_scheduled = models.DateTimeField(default=datetime.now, blank=True)

Just as your created = models.DateTimeField(auto_now_add=True) does. You can replace default with something like

default=lambda self.date, self.time: scheduled(self.date, self.time)

So it will be like:

time_scheduled = models.DateTimeField(default=lambda self.date, self.time: scheduled(self.date, self.time), blank=True)

where scheduled calculates and formats your datetime out of date and time provided - your helper function. This will assign time automatically at the time of object creation, but you can specify it later, if you wish. With the field like this you can exclude one of the fields representing date and time.

A sample view format - just to demonstrate how to read user:

from rest_framework.renderers import JSONRenderer
from rest_framework.permissions import AllowAny
from rest_framework.decorators import api_view, renderer_classes, permission_classes

# If you need GET as well, this would be @api_view(['POST', 'GET'])
@api_view(['POST'])
@renderer_classes((JSONRenderer,))
@permission_classes([AllowAny,])
def read_user_view(request):
    try:
        user_id = request.data['user_id']
        user = User.objects.get(id=int(user_id))
    except ObjectDoesNotExist as e:
        user = User.objects.create(id=user_id)
        # Any other processing if user created

    serializer = MyUserSerializer(user, many=False)
    return Response(serializer.data)

Upvotes: 1

Related Questions