Django foreign keys queryset отображается в react-redux
Я знаю, что этот вопрос задавался много раз, но я не могу найти ответ на свою конкретную проблему. Я пытался добраться до решения как на бэкенде (Django), так и на фронтенде (React.js и Redux), но не могу его найти.
Я пытаюсь сделать простой зависимый выпадающий список для марок автомобилей и моделей автомобилей, но я могу получить только полный список моделей, а не только те, которые связаны с их маркой. Я новичок в Django и не знаю, является ли моей ошибкой то, что я получаю все данные в сериализаторе, или же я неправильно устанавливаю отношения внешних ключей.
Если вам нужна дополнительная информация или объяснение, пожалуйста, дайте мне знать. Спасибо за помощь!
models.py:
class Brand(models.Model):
name = models.CharField(max_length=120, unique=True)
def __str__(self):
return self.name
class Model(models.Model):
brand = models.ForeignKey(Brand, on_delete=models.SET_NULL, related_name='brand_id')
name = models.CharField(max_length=120, unique=True)
def __str__(self):
return self.name
serializers.py:
class BrandSerializer(serializers.ModelSerializer):
class Meta:
model = Brand
fields = '__all__'
class ModelSerializer(serializers.ModelSerializer):
class Meta:
model = Model
fields = '__all__'
views.py:
@api_view(['GET'])
@permission_classes([AllowAny])
def getBrands(request):
brands = Brand.objects.all()
serializer = BrandSerializer(brands, many=True)
return Response(serializer.data)
@api_view(['GET'])
@permission_classes([AllowAny])
def getModels(request):
#This is wrong, but I don't know how to get the id from the car models match the id of the car brands
brand_id = Brand.objects.filter('id').all()
models = Model.objects.filter(brand_id=brand_id).order_by('name')
serializer = ModelSerializer(models, many=True)
return Response(serializer.data)
urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('users/', views.UserProfile.as_view()),
path('user/<int:pk>/', views.UserProfile.as_view()),
path('cars/brand/', views.getBrands, name='carbrands'),
path('cars/model/', views.getModels, name='carmodels'),
# path('cars/price/', views.getPrices, name='carprices'),
]
Если это поможет, вот код React:
reducers.js:
export const brandsList = (state = { brands: [] }, action) => {
switch (action.type) {
case BRANDS_LIST_REQUEST:
return { loading: true, brands: [] }
case BRANDS_LIST_SUCCESS:
return {
loading: false,
brands: action.payload,
}
case BRANDS_LIST_FAIL:
return { loading: false, error: action.payload }
default:
return state
}
};
export const modelsList = (state = { models: [] }, action) => {
switch (action.type) {
case MODELS_LIST_REQUEST:
return { loading: true, models: [] }
case MODELS_LIST_SUCCESS:
return {
loading: false,
models: action.payload,
}
case MODELS_LIST_FAIL:
return { loading: false, error: action.payload }
default:
return state
}
};
actions.js:
export const listModels = () => async (dispatch) => {
try {
dispatch({ type: MODELS_LIST_REQUEST })
const { data } = await API.get('/api/cars/model/')
dispatch({
type: MODELS_LIST_SUCCESS,
payload: data
})
} catch (error) {
dispatch({
type: MODELS_LIST_FAIL,
payload: error.response && error.response.data.detail
? error.response.data.detail
: error.message,
})
}
}
export const listCars = () => async (dispatch) => {
try {
dispatch({ type: CARS_LIST_REQUEST })
const { data } = await API.get('/api/cars/')
dispatch({
type: CARS_LIST_SUCCESS,
payload: data
})
} catch (error) {
dispatch({
type: CARS_LIST_FAIL,
payload: error.response && error.response.data.detail
? error.response.data.detail
: error.message,
})
}
}
Dropdown.js:
import React, { useState, useEffect } from 'react'
import { useDispatch } from 'react-redux'
import { Row, Col, ListGroup, Button, Form } from 'react-bootstrap'
import options from "../../utils/Options"
// Redux action
import { listBrands, listModels, listCars } from "../../store/actions";
function Dropdown() {
const [brandId, setBrandId] = useState(null);
const [modelId, setModelId] = useState(null);
const dispatch = useDispatch();
const brandsList = useSelector(state => state.brandsList);
const { error, loading, brands } = brandsList;
const modelsList = useSelector(state => state.modelsList);
const { models } = modelsList;
useEffect(() => {
dispatch(listBrands());
if ( brandId !== null ) {
dispatch(listModels())
};
}, [dispatch])
return (
<div>
<Form.Control
as="select"
value={brandId ? brandId : ''}
onChange={(e) => setBrandId(e.target.value)}
>
{
brands.map((brand) =>
<option value={brand.id}>
{brand.name}
</option>
)
}
</Form.Control>
<Form.Control
as="select"
value={modelId ? modelId : ''}
onChange={(e) => setModelId(e.target.value)}
>
{
models.map((model) =>
<option value={model.id}>
{model.name}
</option>
)
}
</Form.Control>
</div>
)
}
export default Dropdown
Все изменения дадут вам подробную информацию о модели и марке,
Изменение в сериализаторе:
class ModelSerializer(serializers.ModelSerializer):
class Meta:
model = Model
fields = '__all__'
depth = 1 # new
Изменение в представлении api:
@api_view(['GET'])
@permission_classes([AllowAny])
def getModels(request):
models = Model.objects.all().order_by('name')
serializer = ModelSerializer(models, many=True)
return Response(serializer.data)
Соответствующий бренд с каждой моделью будет автоматически добавлен в ответ.