Django自定义预填充表单跳转出现404错误求助
开发自定义宠物领养项目时遇到以下问题:
- 页面展示待领养宠物列表,每个宠物设有详情按钮;
- 已登录用户在详情页可见“Adopt”按钮,点击该按钮需跳转到带有该宠物模型预填充字段的表单页面;
- 用户补充少量新字段后提交,应提示领养请求已发送。
目前第一个待领养宠物的pk为1,但访问对应URL时出现404错误,提示未找到匹配的AnimalAdoptSend对象。
错误信息
Page not found (404)
No animal adopt send found matching the query
Request Method: GET
Request URL: http://127.0.0.1:8000/animals/adoption/details/1/adopt/
Raised by: animals.views.AnimalAdoptFormSendView
相关代码
子应用 urls.py
urlpatterns = [ path('adoption/create/', AnimalAdoptCreateView.as_view(), name='animal_adopt_create'), path('adoption/', AnimalAdoptListView.as_view(), name='adoption_animals'), path('adoption/details/<int:pk>', AnimalAdoptDetails.as_view(), name='adoption_animals_details'), path('adoption/details/<int:pk>/adopt/', AnimalAdoptFormSendView.as_view(), name='adopt_form'), ]
views.py
class AnimalAdoptDetails(LoginRequiredMixin, DetailView): model = AnimalAdoptReadyCreate template_name = 'animal_adopt_detail.html' class AnimalAdoptFormSendView(LoginRequiredMixin, UpdateView): model = AnimalAdoptSend form_class = AnimalAdoptSendForm template_name = 'animal_adopt_form.html'
models.py
class AnimalAdoptReadyCreate(models.Model): animal_choice = { ('dog', 'Dog'), ('cat', 'Cat'), ('other', 'Other') } animal_name = models.CharField(max_length=20) animal_type = models.CharField(max_length=30, choices=animal_choice) location = models.CharField(max_length=60) details = models.TextField() other = models.TextField(blank=True) user = models.ForeignKey(CustomUser, on_delete=models.CASCADE) picture = models.ImageField(upload_to='pets_for_adoption/') def __str__(self): return self.animal_name class AnimalAdoptSend(AnimalAdoptReadyCreate): place_to_live = models.CharField(max_length=50) picture_of_place = models.ImageField(upload_to='owner_homes/%Y/%m/%d/') other_pets = models.TextField() other_details = models.TextField(blank=True, null=True)
forms.py
class AnimalAdoptSendForm(forms.ModelForm): class Meta: model = AnimalAdoptSend fields = [ 'animal_name', 'picture', 'location', 'animal_type', 'place_to_live', 'other_pets', 'other_details', 'picture_of_place' ]
宠物详情页模板
{% extends 'base.html' %} {% load static %} {% block content %} <main> <div> <h2>Pet name: {{ object.animal_name }}</h2> <p><img src="{{ object.picture.url }}" alt="None"></p> <p>Location: {{ object.location }}</p> <p>Type: {{ object.animal_type }}</p> <p>Contact with the current owner: {{ object.user.phone }}</p> {% if object.other %} <p>Other Details: {{ object.other }}</p> {% endif %} <button><a href="{% url 'adopt_form' object.pk %}">Adopt</a></button> </div> </main> {% endblock %}
预填充表单页模板
{% extends 'base.html' %} {% load static %} {% block content %} <main> {% if request.user.is_authenticated %} <form method="post" action=""> {% csrf_token %} {{ form.as_p }} <button type="submit">Save</button> </form> {# <h2>Pet name: {{ object.animal_name }}</h2>#} {# <p><img src="{{ object.picture.url }}" alt="None"></p>#} {# <p>Location: {{ object.location }}</p>#} {# <p>Type: {{ object.animal_type }}</p>#} {# <p>Place to Live: {{ object.place_to_live }}</p>#} {# <p>Other Pets: {{ object.other_pets }}</p>#} {# {% if object.other_details %} <p>Other Details: {{ object.other_details }}</p>#} {# {% endif %} {# <p><img src="{{ object.picture_of_place.url }}" alt="None"></p>#} {% endif %} </main> {% endblock %}
主 urls.py
urlpatterns = [ path('admin/', admin.site.urls), path('', include('common.urls')), path("accounts/", include("django.contrib.auth.urls")), path("accounts/", include("accounts.urls")), path('petshop/', include('shop.urls')), path('animals/', include('animals.urls')), ]
解决方案
出现404的核心原因:你用了UpdateView处理领养请求,但AnimalAdoptSend是继承AnimalAdoptReadyCreate的多表继承模型,两张表在数据库中独立存储。访问pk=1的领养URL时,UpdateView会去AnimalAdoptSend表查找pk=1的记录,但该记录尚未创建(还没人提交过该宠物的领养请求),因此抛出404。
正确逻辑是新建领养请求记录,而非更新,需修改视图并预填充原宠物字段:
1. 修改 views.py
from django.shortcuts import get_object_or_404 from django.contrib import messages class AnimalAdoptFormSendView(LoginRequiredMixin, CreateView): model = AnimalAdoptSend form_class = AnimalAdoptSendForm template_name = 'animal_adopt_form.html' success_url = '/animals/adoption/' # 可根据需求调整跳转地址 def get_initial(self): # 获取原宠物对象,预填充表单字段 animal = get_object_or_404(AnimalAdoptReadyCreate, pk=self.kwargs['pk']) return { 'animal_name': animal.animal_name, 'picture': animal.picture, 'location': animal.location, 'animal_type': animal.animal_type, } def form_valid(self, form): # 提交成功后添加提示信息 messages.success(self.request, '领养请求已发送,请等待审核') return super().form_valid(form)
2. 调整表单字段(可选)
若希望原宠物的基础字段不允许用户修改,可在表单中设置禁用:
class AnimalAdoptSendForm(forms.ModelForm): class Meta: model = AnimalAdoptSend fields = [ 'animal_name', 'picture', 'location', 'animal_type', 'place_to_live', 'other_pets', 'other_details', 'picture_of_place' ] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 禁用基础字段,防止用户修改 for field_name in ['animal_name', 'picture', 'location', 'animal_type']: self.fields[field_name].widget.attrs['disabled'] = True self.fields[field_name].required = False
3. 模板调整(可选)
添加文件上传编码类型,并优化预填充内容展示:
{% extends 'base.html' %} {% load static %} {% block content %} <main> {% if request.user.is_authenticated %} <h3>领养申请:{{ form.animal_name.value }}</h3> <p><img src="{{ form.picture.value.url }}" alt="宠物照片" style="width:200px;"></p> <form method="post" enctype="multipart/form-data"> {% csrf_token %} {{ form.as_p }} <button type="submit">提交领养请求</button> </form> {% endif %} </main> {% endblock %}
额外说明
多表继承会在AnimalAdoptSend表生成指向AnimalAdoptReadyCreate的外键(默认名为animaladoptreadycreate_ptr_id),若需关联原宠物而非复制信息,可在form_valid中手动设置:
def form_valid(self, form): animal = get_object_or_404(AnimalAdoptReadyCreate, pk=self.kwargs['pk']) form.instance.animaladoptreadycreate_ptr = animal messages.success(self.request, '领养请求已发送,请等待审核') return super().form_valid(form)
内容的提问来源于stack exchange,提问作者Danail Lesev

