You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django表单纯文本字段报错及删除确认页实现咨询

Fixing Your Django Form Widget Errors + Simplified Delete Confirmation Page

Let's break down your issues one by one and walk through the fixes, plus a much easier way to build that Django Admin-style delete confirmation page.


1. Fixing AttributeError: 'CharField' object has no attribute 'is_hidden'

Here's why this happens: In your DeleteSiteForm's Meta.widgets dictionary, you incorrectly passed forms.CharField(widget=PlainTextWidget) instead of a Widget instance.

The widgets dict expects direct references to Widget classes/instances, not form fields. When you pass CharField, Django tries to treat it like a Widget—but CharField doesn't have the is_hidden attribute that Widgets require, hence the error.

Fixed Form Code

class DeleteSiteForm(forms.ModelForm):
    class Meta:
        model = SiteData
        fields = ['location']
        widgets = {
            # Pass the Widget instance directly, not a CharField wrapper
            'location': PlainTextWidget(),
        }

2. Fixing TypeError: render() got an unexpected keyword argument 'name'

This error stems from a mismatch between your widget's render method signature and what Django (especially crispy forms) expects.

Django's standard Widget.render method uses named parameters like name and attrs, but you used _name and _attrs (underscore-prefixed aliases). When crispy forms calls render(name="location", ...), your function doesn't recognize the name keyword argument.

Also, directly using mark_safe(value) carries a small XSS risk if your value contains untrusted HTML. Use format_html instead to auto-escape content safely.

Fixed Widget Code

from django.utils.html import format_html
from django.forms import Widget

class PlainTextWidget(Widget):
    # Match Django's standard render method signature exactly
    def render(self, name, value, attrs=None):
        # Use format_html for safe auto-escaping, handle empty values
        return format_html('{}', value) if value is not None else '-'
    
    # Optional: Explicitly declare this isn't a hidden widget (defaults to False, but clear is better)
    is_hidden = False

3. Simplified Delete Confirmation Page (Like Django Admin)

You don't need to build a custom form for this! Django's built-in DeleteView generic class view handles the entire flow: displaying a confirmation page on GET, executing the delete on POST, and handling redirects. It's exactly how Django Admin implements delete confirmations.

Step 1: Define the DeleteView

from django.views.generic import DeleteView
from django.urls import reverse_lazy
from .models import SiteData

class SiteDeleteView(DeleteView):
    model = SiteData
    # Default template path: <your_app>/<model_name>_confirm_delete.html
    template_name = 'your_app/site_confirm_delete.html'
    # Where to redirect after successful deletion (use reverse_lazy for class-based views)
    success_url = reverse_lazy('site_list')

    # Optional: Add custom permission checks (e.g., only allow owners to delete)
    def get_object(self, queryset=None):
        obj = super().get_object(queryset)
        # Example: if obj.created_by != self.request.user:
        #     raise PermissionDenied
        return obj

Step 2: Create the Confirmation Template

Make a template at your_app/templates/your_app/site_confirm_delete.html—this will look and behave just like Django Admin's delete page:

{% extends 'base.html' %}
{% load crispy_forms_tags %}

{% block content %}
<h1>Confirm Site Deletion</h1>
<p>Are you sure you want to delete the site located at <strong>{{ object.location }}</strong>?</p>

<form method="post">
    {% csrf_token %}
    {{ form|crispy }} <!-- Django auto-generates a hidden confirmation form -->
    <button type="submit" class="btn btn-danger">Yes, Delete This Site</button>
    <a href="{% url 'site_list' %}" class="btn btn-secondary">Cancel</a>
</form>
{% endblock %}

Step 3: Add the URL Route

Update your app's urls.py to point to the view:

from django.urls import path
from .views import SiteDeleteView

urlpatterns = [
    # ... your other routes
    path('sites/<int:pk>/delete/', SiteDeleteView.as_view(), name='site_delete'),
]

This approach is cleaner, leverages Django's built-in tools, and avoids writing custom form logic from scratch.

内容的提问来源于stack exchange,提问作者AlexW

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:01:38