基于DRF+React实现单篇博客多类别选择与存储的技术问询
实现Django+React博客文章多选类别提交方案
一、前端React修改(多选组件替换+提交数据处理)
将原单选下拉框改为多选组件,调整状态管理和提交逻辑,以下是完整修改示例:
原单选版本参考(用户现有代码)
import { useState, useEffect } from 'react'; const CreatePost = () => { const [title, setTitle] = useState(''); const [content, setContent] = useState(''); const [categories, setCategories] = useState([]); const [selectedCategory, setSelectedCategory] = useState(''); useEffect(() => { fetch('/api/categories/') .then(res => res.json()) .then(data => setCategories(data)); }, []); const handleSubmit = async (e) => { e.preventDefault(); const postData = { title, content, category: selectedCategory }; await fetch('/api/posts/', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': getCookie('csrftoken') }, body: JSON.stringify(postData) }); }; return ( <form onSubmit={handleSubmit}> <input type="text" value={title} onChange={(e) => setTitle(e.target.value)} placeholder="文章标题" /> <textarea value={content} onChange={(e) => setContent(e.target.value)} placeholder="文章内容" /> <select value={selectedCategory} onChange={(e) => setSelectedCategory(e.target.value)} > <option value="">选择类别</option> {categories.map(category => ( <option key={category.id} value={category.id}> {category.name} </option> ))} </select> <button type="submit">发布文章</button> </form> ); }; export default CreatePost;
修改后的多选版本代码
import { useState, useEffect } from 'react'; const CreatePost = () => { const [title, setTitle] = useState(''); const [content, setContent] = useState(''); const [categories, setCategories] = useState([]); const [selectedCategories, setSelectedCategories] = useState([]); // 改为数组存储多选ID // 获取类别逻辑不变 useEffect(() => { fetch('/api/categories/') .then(res => res.json()) .then(data => setCategories(data)); }, []); // 处理多选状态变化 const handleCategoryChange = (e) => { const selectedIds = Array.from(e.target.options) .filter(option => option.selected) .map(option => option.value); setSelectedCategories(selectedIds); }; const handleSubmit = async (e) => { e.preventDefault(); const postData = { title, content, categories: selectedCategories // 提交类别ID数组 }; await fetch('/api/posts/', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': getCookie('csrftoken') }, body: JSON.stringify(postData) }); // 提交后重置表单 setTitle(''); setContent(''); setSelectedCategories([]); }; return ( <form onSubmit={handleSubmit}> <input type="text" value={title} onChange={(e) => setTitle(e.target.value)} placeholder="文章标题" required /> <textarea value={content} onChange={(e) => setContent(e.target.value)} placeholder="文章内容" required /> {/* 添加multiple属性启用多选,调整高度提升体验 */} <select multiple value={selectedCategories} onChange={handleCategoryChange} style={{ height: '120px', width: '100%' }} > {categories.map(category => ( <option key={category.id} value={category.id}> {category.name} </option> ))} </select> <p style={{ fontSize: '14px', color: '#666' }}>提示:按住Ctrl(Windows)/Command(Mac)可选择多个类别</p> <button type="submit" style={{ marginTop: '10px' }}>发布文章</button> </form> ); }; // 辅助函数:获取CSRF Token function getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== '') { const cookies = document.cookie.split(';'); for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].trim(); if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } export default CreatePost;
二、后端Django调整(支持多对多字段接收)
1. 模型配置(确保多对多关系)
# models.py from django.db import models class Category(models.Model): name = models.CharField(max_length=100) def __str__(self): return self.name class Post(models.Model): title = models.CharField(max_length=200) content = models.TextField() categories = models.ManyToManyField(Category, related_name='posts') # 多对多关联字段 created_at = models.DateTimeField(auto_now_add=True)
2. Serializer修改(支持接收ID数组)
# serializers.py from rest_framework import serializers from .models import Post, Category class CategorySerializer(serializers.ModelSerializer): class Meta: model = Category fields = ['id', 'name'] class PostSerializer(serializers.ModelSerializer): categories = serializers.PrimaryKeyRelatedField( many=True, queryset=Category.objects.all() ) class Meta: model = Post fields = ['id', 'title', 'content', 'categories', 'created_at'] def create(self, validated_data): # 先创建文章,再关联多对多类别 categories_data = validated_data.pop('categories') post = Post.objects.create(**validated_data) post.categories.add(*categories_data) return post
3. View配置(无需额外修改,依赖Serializer处理)
# views.py from rest_framework import viewsets from .models import Post from .serializers import PostSerializer class PostViewSet(viewsets.ModelViewSet): queryset = Post.objects.all() serializer_class = PostSerializer
关键注意事项
- 原生多选select需要按住快捷键批量选择,若要更友好的交互,可替换为React-Select等第三方多选组件,核心逻辑保持一致。
- 后端必须先创建Post实例,再通过
add(*categories_data)关联多对多关系,不能直接在创建时传递多对多数据。 - 确保CSRF Token正确传递,避免403权限错误。
内容的提问来源于stack exchange,提问作者Abbasali Rezaei
相关产品推荐
相关产品推荐

