我在前端使用 React Native Expo Web 应用程序,在后端使用 Django。
我有一个运行良好的搜索功能。但我面临的问题是,在搜索项之后,特定动物的属性名称已更改为属性 id(数字)。
我的意思是,在手风琴中显示类别名称。但搜索后会显示类别 ID。
在前端我有一个具有一些属性的手风琴。类别的属性看起来:
export const AccordionItemsProvider = ({ children }) => {
const [categoryExpanded, setCategory] = useState(false);
const accordionItems = (item) => {
return (
<>
<List.Accordion
title="Familie"
expanded={categoryExpanded}
onPress={() => setCategory(!categoryExpanded)}>
<Text>{item.category}</Text>
</List.Accordion>
</>
);
};
return (
<AccordionItemsContext.Provider
value={{
accordionItems,
}}>
{children}
</AccordionItemsContext.Provider>
);
};
因此将显示类别的名称。但搜索后会显示类别的 id。而不是名称。
动物的搜索上下文看起来:
/* eslint-disable prettier/prettier */
import React, { createContext, useEffect, useState } from "react";
import { fetchAnimalData } from "./animal/animal.service";
import useDebounce from "../hooks/use-debounce";
export const SearchAnimalContext = createContext();
export const SearchAnimalContextProvider = ({ children }) => {
const [searchAnimal, setSearchAnimal] = useState([]);
const [results, setResults] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [input, setInput] = useState("");
const debounce = useDebounce(input, 500);
useEffect(() => {
if (debounce === "") {
setResults([]);
return;
}
fetchAnimalData(debounce)
.then((response) => {
setResults(response);
})
.catch((err) => {
setError(err);
});
fetchAnimalData();
}, [debounce]);
const performSearch = async (text) => {
if (text.trim() === "") {
setResults([]);
}
setLoading(true);
setError(null);
setTimeout(() => {
fetchAnimalData(text)
.then((response2) => {
setResults(response2);
console.log(response2);
setLoading(false);
})
.catch((err) => {
setLoading(false);
setError(err);
});
}, 100);
};
return (
<SearchAnimalContext.Provider
value={{
results,
setResults,
searchAnimal,
setSearchAnimal,
input,
setInput,
performSearch,
loading,
error,
}}>
{children}
</SearchAnimalContext.Provider>
);
};
console.log 的搜索请求之后的输出看起来:
category
:
30
category_name
:
"Katachtigen - Felidae"
所以在搜索之前它只是类别。但在搜索请求之后。 api 列表中添加了一个category_name。如果我记录handleSearchChange函数:
const handleSearchChange = (text) => {
setInput(text);
if (text.length === 0) {
performSearch(""); // Clear results when the input is empty
navigation.navigate("dieren");
} else {
performSearch(text);
console.log(results);
}
};
category
:
18
category_name
:
"Eekhoorns - Sciuridae"
那么在正确显示搜索类别之前:“Katachtigen - Felidae”。但是在搜索请求类别显示为 30 - Category_id: 后,这是不正确的。
组件中的 search 函数看起来:
import React, { useContext, useEffect, useState } from "react";
import { SafeArea } from "../../../components/utility/safe-area.component";
import { SearchAnimalContext } from "../../../services/search-animal.context";
import { fetchSubCategoryData } from "../../../services/category/category.service";
export const SubCategoryScreen = ({ route, navigation }) => {
const [isLoading, setLoading] = useState(true);
const { performSearch, results, setInput, input } = useContext(SearchAnimalContext);
const [isSearchbarFocused, setSearchbarFocused] = useState(false);
useEffect(() => {
fetchSubCategoryData(route.params.subcategories).then((data) => {
setSubCategoryList(data.animals.length > 0 ? data.animals : data.subcategories);
setLoading(false);
});
}, [route]);
const handleSearchChange = (text) => {
setInput(text);
if (text.length === 0) {
performSearch("");
navigation.navigate("dieren");
} else {
performSearch(text);
}
};
return (
<SafeArea>
{isLoading && (
<LoadingContainer>
<ActivityIndicator animating={true} color={MD2Colors.green200} />
</LoadingContainer>
)}
<Searchbar
placeholder="Search animals"
value={input}
onFocus={() => setSearchbarFocused(true)}
onChangeText={handleSearchChange}
onBlur={() => setSearchbarFocused(false)}
style={styles.searchbar}
/>
</SafeArea>
);
};
这是搜索请求的 api 调用:
/* eslint-disable prettier/prettier */
import { API_URL } from "@env";
import { retrieveToken } from "../../services/authentication/token";
export const fetchAnimalData = async (text) => {
const token = await retrieveToken();
try {
if (token) {
const response = await fetch(`${API_URL}/api/animals/?search=${text}`, {
method: "GET",
headers: {
Authorization: `Token ${token}`,
Accept: "application/json",
"Content-Type": "application/json",
},
});
return await response.json();
} else {
throw new Error(token);
}
} catch (error) {
console.error("There was a problem with the fetch operation:", error);
throw error;
}
};
以及模型Animal 从后端看起来:
import sys
from io import BytesIO
from django.db import models
class Animal(models.Model):
name = models.CharField(max_length=100, verbose_name="Naam")
sort = models.CharField(max_length=100, default='',
category = models.ForeignKey(
Category, related_name='animals', on_delete=models.CASCADE, verbose_name="Familie")
def category_name(self):
return self.category.name
class Meta:
verbose_name = "Dier"
verbose_name_plural = "Dieren"
# permissions = [("set_display_flag", "Check name is display or not",)]
super(Animal, self).save()
def __str__(self):
return self.name
如何在搜索请求后显示正确的类别名称而不是类别 id?
问题在于搜索请求返回的数据结构与初始数据结构不同。在搜索请求后,API 响应包含
category
作为 ID,以及一个名为
category_name
的新字段,其中包含实际的类别名称。
需要更新前端代码以处理这种差异。可以通过以下两种方式之一实现:
1. 更新
AccordionItems
组件以使用
category_name
:
const accordionItems = (item) => {
return (
<>
<List.Accordion
title="Familie"
expanded={categoryExpanded}
onPress={() => setCategory(!categoryExpanded)}
>
<Text>{item.category_name || item.category}</Text>
</List.Accordion>
</>
);
};
此代码首先检查
category_name
属性是否存在。如果存在,则使用它。否则,它将回退到
category
属性。
2. 更新 API 响应以始终包含类别名称:
还可以更新 Django API,以始终在响应中包含类别名称,即使在搜索请求中也是如此。这将使的前端代码更简单,因为它不需要处理不同的数据结构。
可以通过修改 Django 模型的序列化器来实现此目的。以下是一个示例:
from rest_framework import serializers
class AnimalSerializer(serializers.ModelSerializer):
category_name = serializers.CharField(source='category.name', read_only=True)
class Meta:
model = Animal
fields = '__all__'
此序列化器将始终包含一个名为
category_name
的字段,该字段从
category
关系中获取其值。
选择哪种方法取决于的具体需求和偏好。如果可以控制 API,则更新 API 响应以始终包含类别名称可能会更简洁。但是,如果无法更改 API,则更新
AccordionItems
组件以处理不同的数据结构是一种有效的解决方法。