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

Django中create_submission_form路由反向解析失败求助

Django NoReverseMatch 错误排查与解决

错误详情

NoReverseMatch at /dashboard/
Reverse for 'create_submission_form' with keyword arguments '{'docstash_key': ''}' not found. 1 pattern(s) tried: ['core/submission/new/(?P<docstash_key>[^/]+)/\\Z']

触发错误的HTML代码

<a class="dropdown-item" href="{% url 'core:create_submission_form' docstash_key=docstash.key %}">

URL配置

path('new/<str:docstash_key>/', views.create_submission_form, name='create_submission_form'),

对应视图函数

@with_docstash()
def create_submission_form(request):
    if request.method == 'POST' and 'initial' in request.docstash:
        del request.docstash['initial']
        # XXX: docstash will be saved further down

    form = SubmissionFormForm(request.POST or request.docstash.POST,
        initial=request.docstash.get('initial', {}).get('submission_form'))
    formsets = get_submission_formsets(request.POST or request.docstash.POST,
        initial=request.docstash.get('initial'))

    documents = Document.objects.filter(pk__in=request.docstash.get('document_pks', []))
    protocol_uploaded = documents.filter(doctype__identifier='protocol').exists()

    if request.method == 'GET' and not request.docstash.POST and \
        not 'initial' in request.docstash:

        protocol_uploaded = True    # don't show error on completely new
                                    # submission

        # neither docstash nor POST data: this is a completely new submission
        # => prepopulate submitter_* fields with the presenters data
        profile = request.user.profile
        form.initial.update({
            'submitter_contact_first_name': request.user.first_name,
            'submitter_contact_last_name': request.user.last_name,
            'submitter_email': request.user.email,
            'submitter_contact_gender': profile.gender,
            'submitter_contact_title': profile.title,
            'submitter_organisation': profile.organisation,
            'submitter_jobtitle': profile.jobtitle,
        })

    if 'notification_type_id' in request.docstash:
        notification_type = NotificationType.objects.get(
            id=request.docstash['notification_type_id'])
    else:
        notification_type = None

    if 'submission_id' in request.docstash:
        submission = Submission.objects.get(
            id=request.docstash['submission_id'])
    else:
        submission = None

    valid = False

    if request.method == 'POST':
        submit = request.POST.get('submit', False)
        save = request.POST.get('save', False)
        autosave = request.POST.get('autosave', False)

        request.docstash.name = (
            request.POST.get('german_project_title') or
            request.POST.get('project_title')
        )
        request.docstash.POST = request.POST
        request.docstash.save()

        if save or autosave:
            return HttpResponse('save successfull')

        for name, formset in formsets.items():
            formset.full_clean()        # work around django bug: full_clean does not get called in is_valid if total_form_count==0

        formsets_valid = all([formset.is_valid() for formset in formsets.values()]) # non-lazy validation of formsets
        valid = form.is_valid() and formsets_valid and protocol_uploaded and not 'upload' in request.POST
        if valid and submission and not notification_type and \
            not submission.current_submission_form.allows_resubmission(request.user):

            messages.error(request,
                _("This form can't be submitted at the moment."))
            valid = False

        if submit and valid:
            if not submission:
                submission = Submission.objects.create()
            submission_form = form.save(commit=False)
            submission_form.submission = submission
            submission_form.is_notification_update = bool(notification_type)
            submission_form.is_transient = bool(notification_type)
            submission_form.save()
            form.save_m2m()

            submission_form.documents = documents
            for doc in documents:
                doc.parent_object = submission_form
                doc.save()
        
            investigators = formsets.pop('investigator').save(commit=False)
            employees = formsets.pop('investigatoremployee').save(commit=False)
            for investigator in investigators:
                investigator.submission_form = submission_form
                investigator.save()
            for employee in employees:
                if employee.investigator_index >= len(investigators):
                    continue
                employee.investigator = investigators[employee.investigator_index]
                employee.save()

            for formset in formsets.values():
                for instance in formset.save(commit=False):
                    instance.submission_form = submission_form
                    instance.save()

            request.docstash.delete()
            
            on_study_submit.send(Submission, submission=submission, form=submission_form, user=request.user)

            if notification_type:
                return redirect('notifications:create_diff_notification',
                    submission_form_pk=submission_form.pk,
                    notification_type_pk=notification_type.pk)

            return redirect('readonly_submission_form',
                submission_form_pk=submission_form.submission.current_submission_form.pk)

    context = {
        'form': form,
        'tabs': SUBMISSION_FORM_TABS,
        'valid': valid,
        'submission': submission,
        'notification_type': notification_type,
        'protocol_uploaded': protocol_uploaded,
    }
    for prefix, formset in formsets.items():
        context['%s_formset' % prefix] = formset
    return render(request, 'submissions/form.html', context)

DocStash模型定义

class DocStash(models.Model):
    class ConcurrentModification(Exception):
        pass

    key = models.UUIDField(default=uuid.uuid4, primary_key=True)
    group = models.CharField(max_length=120, db_index=True, null=True)
    current_version = models.IntegerField(default=-1)
    owner = models.ForeignKey(User, on_delete=models.CASCADE)
    modtime = models.DateTimeField(auto_now=True)
    name = models.TextField(blank=True, null=True)
    value = JSONField(null=False)
    
    content_type = models.ForeignKey(ContentType, null=True, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField(null=True)
    parent_object = GenericForeignKey('content_type', 'object_id')

    class Meta:
        app_label = 'docstash'
        unique_together = ('group', 'owner', 'content_type', 'object_id')
        #abstract = True
    def __getitem__(self, key):
        return self.value[key]

    def __setitem__(self, key, value):
        self.value[key] = value

    def __delitem__(self, key):
        del self.value[key]

    def __contains__(self, key):
        return key in self.value

    def get(self, name, default=None):
        return self.value.get(name, default)

    @property
    def POST(self):
        if 'POST' in self.value:
            return QueryDict(self.value['POST'])
        return None

    @POST.setter
    def POST(self, data):
        self.value['POST'] = data.urlencode()

    def save(self, **kwargs):
        if DocStash.objects.filter(key=self.key).exists():
            updated = DocStash.objects.filter(
                key=self.key, current_version=self.current_version
            ).update(current_version=models.F('current_version') + 1)
            if not updated:
                raise self.ConcurrentModification()

        self.current_version += 1
        return super().save(**kwargs)

已尝试的无效操作

  • HTML代码无法读取docstash.pk
  • 手动以字符串形式传入docstash.pk可绕过错误,但无法找到对应页面

解决思路

1. 确保模板上下文存在有效的DocStash实例

错误提示docstash_key为空字符串,说明模板中的docstash对象要么未传入上下文,要么其key字段未正确生成。需检查dashboard视图(即渲染该HTML的视图)代码:

  • 必须先创建并保存DocStash实例(调用save()后UUID才会生成)
  • 将实例传入模板上下文:
    # dashboard视图示例
    def dashboard(request):
        docstash = DocStash.objects.create(owner=request.user)
        # 可预先设置初始数据到docstash.value
        return render(request, 'dashboard.html', {'docstash': docstash})
    

2. 匹配URL参数的约束

URL规则new/<str:docstash_key>/对应的正则是[^/]+,要求参数为非空字符串。空字符串无法匹配该规则,因此必须保证docstash.key是有效的UUID字符串(DocStash实例创建并保存后自动生成)。

3. 检查@with_docstash装饰器逻辑

该装饰器的作用应该是从URL参数docstash_key中获取DocStash实例。如果手动传入无效的pk字符串,装饰器会因找不到对应实例而返回404。需确认装饰器是否正确通过key字段查询实例:

# @with_docstash装饰器核心逻辑示例
def with_docstash():
    def decorator(view_func):
        def _wrapped_view(request, docstash_key, *args, **kwargs):
            try:
                request.docstash = DocStash.objects.get(key=docstash_key, owner=request.user)
            except DocStash.DoesNotExist:
                raise Http404("DocStash not found")
            return view_func(request, *args, **kwargs)
        return _wrapped_view
    return decorator

4. 模板中增加空值防护

防止docstash为空或key无效时触发错误,在模板中添加判断:

{% if docstash and docstash.key %}
<a class="dropdown-item" href="{% url 'core:create_submission_form' docstash_key=docstash.key %}">提交新表单</a>
{% else %}
<!-- 显示错误提示或禁用按钮 -->
<span class="dropdown-item text-muted">无法创建提交表单</span>
{% endif %}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 22:24:57