Jak przesłać klucz obcy do tabeli w DRF?

0

Sorry, pewnie ktoś powie że banalne pytanie, ale dopiero wchodzę w DRF.
Dziwna sprawa. Jak wybieram w formie od Frontu (react) "PHP" to dodaje mi do bazy dane i nawet pięknie
uploaduje obrazek na backu. Jednak gdy wybieram w formie Python, to po kliku "submit" wyświetla
mi na backu następujący error:
"error {'category': [ErrorDetail(string='Incorrect type. Expected pk value, received str.', code='incorrect_type')]}
Bad Request: /api2/course/"

To jest kod:
model:

class CourseCategory(models.Model):
    title = models.CharField(max_length=150)
    description = models.TextField()

    class Meta:
        verbose_name_plural = "2. Course Categories"

    def __str__(self):
        return self.title


class Course(models.Model):
    category = models.ForeignKey(
        CourseCategory, on_delete=models.CASCADE, related_name='tracks', null=True)
    instructor = models.ForeignKey(Instructor, on_delete=models.CASCADE)
    title = models.CharField(max_length=150)
    description = models.TextField()
    image = models.ImageField(upload_to='course_imgs/', null=True)
    techs = models.TextField(null=True)

    class Meta:
        verbose_name_plural = "3. Courses"

serializer:

class CategorySerializer(serializers.ModelSerializer):
    tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
    

    class Meta:
        model = models.CourseCategory
        fields = ['id', 'title', 'description', 'tracks']


class CourseSerializer(serializers.ModelSerializer):
    

    class Meta:
        model = models.Course
        fields = '__all__'

Jakieś pomysły? Sprawdzałem w bazie na xampie ... Categorie PHP, Python są normalnie pięknie dodane, więc
to nie jest kwestia tego że brakuje id Pytona. I właśnie PHP dodaje mi bez problemu, a jak wybieram katetorie Python to
taki właśnie error jest.
Już patrzyłem też tutaj:
https://www.django-rest-framework.org/api-guide/relations/#slugrelatedfield
... ale jednak mi ta kategoria python nie działa.
Ktoś coś?

Edit:
Aha, i dodam że na formie od frontu nie ma nawet problemu z Corsem. Tzn. jak klikam
w selecta od strony reacta to normalnie wyświetla mi do wyboru PHP oraz Python.

1

za mało troche pokazałeś, pokaż urls.py i views.py, szczegolnie dla /api2/course/

0
fespue napisał(a):

za mało troche pokazałeś, pokaż urls.py i views.py, szczegolnie dla /api2/course/

Ok, już zapodaję bo właśnie nie wiedziałem na ile dokładnie wszystko pokazać.
urls:

from django.urls import path
from . import views

urlpatterns = [
    # instructor
    path('instructor/', views.InstructorList.as_view()),
    path('instructor/<int:pk>/', views.InstructorDetail.as_view()),
    path('instructor-login', views.instructor_login),
    # category
    path('category/', views.CategoryList.as_view()),
    # course url
    path('course/', views.CourseList.as_view())
]

views:

from ast import main
import imp
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import generics
from rest_framework import permissions
from rest_framework import status
from .serializers import InstructorSerializer, CategorySerializer, CourseSerializer
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse, HttpResponse
from . import models
from rest_framework.parsers import MultiPartParser, FormParser


class InstructorList(generics.ListCreateAPIView):
    queryset = models.Instructor.objects.all()
    serializer_class = InstructorSerializer
    


class InstructorDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = models.Instructor.objects.all()
    serializer_class = InstructorSerializer
    


@csrf_exempt
def instructor_login(request):
    email = request.POST['email']
    password = request.POST['password']
    instructorData = models.Instructor.objects.get(
        email=email, password=password)
    if instructorData:
        return JsonResponse({'bool': True})
    else:
        return JsonResponse({'bool', False})


class CategoryList(generics.ListCreateAPIView):
    queryset = models.CourseCategory.objects.all()
    serializer_class = CategorySerializer


class CourseList(generics.ListCreateAPIView):
    parser_classes = (MultiPartParser, FormParser)

    def get(self, request):
        queryset = models.Course.objects.all()
        serializer_class = CourseSerializer(queryset, many=True)
        return Response(serializer_class.data)

    def post(self, request):
        posts_serializer = CourseSerializer(data=request.data)
        if posts_serializer.is_valid():
            posts_serializer.save()
            return Response(posts_serializer.data, status=status.HTTP_201_CREATED)
        else:
            print('error', posts_serializer.errors)
            return Response(posts_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    #

i ewentualnie front reacta:

import {Link} from 'react-router-dom';
import InstructorSidebar from './InstructorSidebar';
import React, { useState,useEffect } from 'react';
import axios from 'axios';
const baseUrl='http://127.0.0.1:8000/api2';
function AddCourse(){
    
    const [categories,setCategories] = useState([]);
    const [courseData,setCourseData] = useState({
        category:'',
        title:'',
        description:'',
        image:'',
        techs:''
    });
    useEffect(()=>{
        try {
            axios.get(baseUrl+'/category')
            .then((res)=>{
                setCategories(res.data);
            });
        }catch(error){
            console.log(error);
        }
    },[]);
    const handleChange=(event)=>{
        setCourseData({
            ...courseData,
            [event.target.name]:event.target.value
        });
    };
    const handleFileChange=(event)=>{
        setCourseData({
            ...courseData,
            [event.target.name]:event.target.files[0]
        });
    }
    const formSubmit=()=>{
        const _formData = new FormData();
        _formData.append('category',courseData.category);
        _formData.append('instructor',1);
        _formData.append('title',courseData.title);
        _formData.append('description',courseData.description);
        _formData.append('image',courseData.image,courseData.image.name);
        _formData.append('techs',courseData.techs);
        try {
            
            axios.post(baseUrl+'/course/',_formData,{
                
                headers: {
                    'content-type':'multipart/form-data'
                }
            })
            .then((res)=>{
                console.log(res.data);
        });
        }catch(error){
            console.log(error.response);
        }
    }
    
    return (
        
        <div className="container mt-4">
            <div className="row">
                <aside className="col-md-3">
                    <InstructorSidebar/>
                    
                </aside>
                <section className="col-md-9">
                    {/*Dashboard */}
                    <div className="card">
                        <h5 className="card-header">Dodawanie kursu</h5>
                        <div className="card-body">
                            <form>
                                <div className='mb-3'>
                                    <label htmlFor='title' className='form-label'>Kategoria</label>
                                    
                                    <select name='category' onChange={handleChange} className='form-control'>
                                        {categories.map((category,index)=>{return <option key={index} defaultValue={category.id}>{category.title}</option>})}
                                    </select>
                                </div>
                                {/**zmiana */}
                                <div className='mb-3'>
                                    <label htmlFor='title' className='form-label'>Tytuł</label>
                                    <input type='text' name='title' onChange={handleChange} defaultValue={courseData.title} id='title' className='form-control'/>
                                </div>
                                <div className='mb-3'>
                                    <label htmlFor='description' className='form-label'>Opis</label>
                                    <textarea name='description' onChange={handleChange} defaultValue={courseData.description} className='form-control' id='description'></textarea>
                                </div>
                                <div className='mb-3'>
                                    <label htmlFor='image' className='form-label'>Obrazek</label>
                                    <input type='file' name='image' accept='image/jpeg,image/png,image/gif' onChange={handleFileChange} id='image' className='form-control'/>
                                </div>
                                <div className='mb-3'>
                                    <label htmlFor='techs' className='form-label'>Technologie</label>
                                    <textarea name='techs' onChange={handleChange} defaultValue={courseData.techs} className='form-control' placeholder='JavaScript, PHP, C#' id='techs'></textarea>
                                </div>
                                <button type='submit' onClick={formSubmit}  className='btn btn-primary'>Submit</button>
                            </form>
                            
                        </div>
                    </div>
                    
                </section>
            </div>
        </div>
    )
}
export default AddCourse;
0

@fespue: Wiesz co? Teraz to zauważyłem. Jakaś śmieszna rzecz. Zauważyłem przy dodawaniu z kategorią PHP.
W bazie danych na xammpie w miejscu category_id jest null. Dlaczego? Znaczy ja niby zezwoliłem w modelu na null, bo
mi error też wyskakiwał, ale może tutaj właśnie należy szukać problemu. Nie wiem właśnie.
image
Ale tym bardziej że przecież w formularzu od strony reacta cały czas PHP jest wybrany. Dziwne.

1

szukałbym błędu na frontendzie, np tutaj:

<select name='category' onChange={handleChange} className='form-control'>
    {categories.map((category,index)=>{return <option key={index} defaultValue={category.id}>{category.title}</option>})}
</select>

możliwe, że brakuje ci value

Wracając do DRF, dobrym podejściem byłoby napisanie teraz testu tego endpointu, żeby być pewnym gdzie i po której stronie leży problem. Wtedy możesz sobie porównać z tym co wysyła frontend oraz łatwo debugować ;)

0

możliwe, że brakuje ci value

Wracając do DRF, dobrym podejściem byłoby napisanie teraz testu tego endpointu, żeby być pewnym gdzie i po której stronie leży problem. Wtedy możesz sobie porównać z tym co wysyła frontend oraz łatwo debugować ;)

1.Gdzie dokładnie brakuje "value"? Bo tam jest to reactowe "defaultValue".
2. Napisałbyś przykład takiego testu? Jakiś drobny?

0

Okryłem coś (Amerykę pewnie ktoś powie), wszedłem sobie w przeglądarce do panelu superusera i dodałem ten kurs z fotką
backendem. I tutaj jest wszystko ok. Normalnie nie ma żadnych bad requestów ani nulli. Czyli w po stronie backendu wszystko musi grać.
To faktycznie jest jakiś problem z frontem. Pytanie tylko jaki.

0

Panel admina nie korzysta z widoków które masz w views.py

możesz zbadać sobie stronę, zebys widział requesty, wykonaj akcje z frontu i zobacz co front wysyła

0

@fespue: Ok ale jedno mnie zastanawia. To musi być coś na froncie, bo jak daję na froncie o tak:

const formSubmit=()=>{
        const _formData = new FormData();
        _formData.append('category',2);
        //lub
        _formData.append('category',1)
        _formData.append('instructor',1);
        _formData.append('title',courseData.title);
        _formData.append('description',courseData.description);
        _formData.append('image',courseData.image,courseData.image.name);
        _formData.append('techs',courseData.techs);
        try {
           
            axios.post(baseUrl+'/course/',_formData,{
                
                headers: {
                    'content-type':'multipart/form-data'
                }
            })
            .then((res)=>{
                console.log(res.data);
        });
        }catch(error){
            console.log(error.response);
        }
    }

to spokojnie mi uploaduje fotki, dodaje waszystko do bazy i jest okej. Zarówno python jak i php.
Tzn. jak daję z buta php = 1 przy category oraz python = 2 to wszystko gra.
Tylko właśnie jak daję courseData.category to wtedy w bazie w category_id dostaję nulla (choć w opcji PHP błędu nie ma), a
jak daję Python to mam bad request. Powieszę się zaraz;-)
edit:
No wiem, zaczynam walić głową w mur bez sensu;-)

0

O o o ;-) Coś znalazłem;-) Wyświetla mi faktycznie jakiś error odnośnie serializacji gdy wchodzę nie przez admina
tylko localhost:8000/api2/course. Faktycznie...

0

No ok, poprawiłem na backendzie. I w moim widoku na backendzie normalnie wszystko działa. Wybieram PHP i działa i wybieram Python i też działa.
Tylko jak z frontu daję courseData.category to przy PHP robi mi NULL choć dane się dodają, a jak wybieram Python to dostaję "bad request /api2/course" 400 65.
No i siedzę i kombinuję;-))) No na backu teraz przed chwilą sprawdziłem przez mój własny widok i wszystko pięknie dodaje.
Nie no, to musi być jakaś pierdoła tylko ja ślepy jestem.

0

@fespue: Ok doszedłem;-) Faktycznie w select brakowało defaultValue a w option powinno być samo value. Teraz już "uffff" działa;-)

1 użytkowników online, w tym zalogowanych: 0, gości: 1