1

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
Jin Nii Sama
  • 707
  • 1
  • 16
  • 33

1 Answers1

1

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)
dmitryro
  • 3,463
  • 2
  • 20
  • 28
  • I have a question, won't this `patient = MyUserSerializer(many=False, read_only=True)` prevent from posting the patient(id) ? since it is read only – Jin Nii Sama Jan 29 '18 at 02:27
  • If you want to be able to post a new user, you set it to False, you're right. – dmitryro Jan 29 '18 at 02:28
  • Because to book a appointment, u need to post in the userId(patientId). Not really post a new user. – Jin Nii Sama Jan 29 '18 at 02:34
  • Sorry for all these trouble. So i put this line`patient = MyUser.objects.get(id=patientId)` before the `if request.method == 'POST':` statement ? My `(id=patientId)` got error with unresolve reference. – Jin Nii Sama Jan 29 '18 at 02:39
  • DRF should allow posting, you can also post user_id as integer, read the object with `MyUser.objects.get(id=user_id)` and then save it in the view, if needed. – dmitryro Jan 29 '18 at 02:39
  • Added a simple view as an example - `patient = MyUser.objects.get(id=patientId) ` is within `if request.method == 'POST':` statement, if you follow the format you used. – dmitryro Jan 29 '18 at 02:49
  • ok, i added `patient = request.data['patientId']` `patientId = MyUser.objects.get(id=int(patient))` `saveget_attrs = { "patientId": patientId,` and also in serializer changes. But i got 400 when doing the post – Jin Nii Sama Jan 29 '18 at 03:22
  • So within your view something causes error - try catching it with try... except block and output to the console or using `logging` module or print() - there's an exception in your view, which does not get intercepted properly. Also, make sure your POST ajax call has proper format. You need to verify 1. patientId is indeed sent and is integer 2. patient is read and does not generate an error 3. You can actually access view and have proper permissions 4. The route or path for the view is properly configured and formatted 5. The serializer has no errors. Use logging or print() to debug. – dmitryro Jan 29 '18 at 03:29
  • i found the error `{"patientId":["This field is required."]}` – Jin Nii Sama Jan 30 '18 at 02:48
  • Great. Thanks for letting know. – dmitryro Jan 30 '18 at 02:51
  • Sorry again for troubling you , i still don't know why im getting that as i did give the `patientId`. i updated my code so it is more clearer to see what i have done now. – Jin Nii Sama Jan 30 '18 at 03:16
  • If you want to make a field nullable, it has to have `blank=True, null=True` - see more here on it. https://stackoverflow.com/questions/8609192/differentiate-null-true-blank-true-in-django – dmitryro Jan 30 '18 at 03:23
  • no, its not suppose to be null or blank. i did enter this too `saveget_attrs = { "patientId": patientId,` but its not saving even though patientId has been given – Jin Nii Sama Jan 30 '18 at 03:32