Django HTMX NoReverseMatch Reverse for 'my_url' with arguments '('',)' not found using POST-request in view

问题描述 投票:0回答:1

我在使用 HTMXDjango 框架上遇到了以下问题:django.urls.exceptions.NoReverseMatch: Reverse for 'create_cv_workexp' with arguments '('',)' not found。尝试了 1 种模式:['create\-cv\-workexp/(?P[0-9]+)/\Z']

models.py:

class Position(models.Model):
    position_name = models.CharField(max_length=255, blank=True, verbose_name='Position')
    created_by = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='user by', blank=True, null=True)
    date_create = models.DateTimeField(auto_now_add=True)
    date_update = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['date_create']

    def __str__(self):
        return self.position_name


class WorkExperience(models.Model):
    pos = models.ForeignKey('Position', blank=True, null=True, on_delete=models.SET_NULL, verbose_name='Position Relative')
    company = models.CharField(max_length=50, verbose_name='Company', blank=True)
    position_work = models.CharField(max_length=50, verbose_name='Position', blank=True)
    link_company = models.URLField(verbose_name="Company's link", blank=True)
    date_of_employment = models.DateField(blank=True, verbose_name='Date of employment')
    date_of_dismissal = models.DateField(blank=True, verbose_name='Date of dismissal')

    def __str__(self):
        return self.company

    class Meta:
        ordering = ["-date_of_employment"]

这里有两个模型,两个模型通过外键链接。

forms.py:

class CreateCVWorkExperienceForm(forms.ModelForm):
    ''' form for WorkExperience object '''

    class Meta:
        model = resume.models.WorkExperience
        fields = ('company',
                  'position_work',
                  'link_company',
                  'date_of_employment',
                  'date_of_dismissal')

views.py:

@login_required(login_url='/users/login/')
def create_cv_workexp(request, pk):
    ''' main views for displaying form for to add WorkExperience to CV and redirect to HTMX template '''

    position = Position.objects.get(id=pk)
    workexps = WorkExperience.objects.filter(pos=position)
    form = CreateCVWorkExperienceForm(request.POST or None)

    if request.method == "POST":
        if form.is_valid():
            workexp = form.save(commit=False)
            workexp.pos = position
            workexp.save()
            pk = workexp.pk
            return redirect("resume:detail-workexp", pk=pk)
        else:
            return render(request, "resume/partials/workexp_form.html", context={
                "form": form
            })

    context = {
        "form": form,
        "position": position,
        "workexps": workexps,
        'title': 'Add company'
    }

    return render(request, "resume/create_cv_workexp.html", context)


@login_required(login_url='/users/login/')
def create_workexp_form(request, pk):
    ''' view for rendering form of WorkExperience object for adding instnce during creating CV '''

    form = CreateCVWorkExperienceForm()
    position = {"id": pk}
    context = {
        "form": form,
        "position": position,
    }
    return render(request, "resume/partials/workexp_form.html", context)


@login_required(login_url='/users/login/')
def detail_workexp(request, pk):
    ''' view for rendering added WorkExperience to DB and displaying it on 'resume/create_cv_workexp.html' template '''

    workexp = get_object_or_404(WorkExperience, pk=pk)
    context = {
        "workexp": workexp,
        'pk': pk
    }
    return render(request, "resume/partials/workexp_detail.html", context)


@login_required(login_url='/users/login/')
def update_workexp(request, pk):
    ''' view for updating added WorkExperience '''

    workexp = WorkExperience.objects.get(id=pk)
    form = CreateCVWorkExperienceForm(request.POST or None, instance=workexp)

    if request.method == "POST":
        if form.is_valid():
            form.save()
            return redirect("resume:detail-workexp", pk=workexp.id)

    context = {
        "form": form,
        "workexp": workexp
    }

    return render(request, "resume/partials/workexp_form.html", context)


@login_required(login_url='/users/login/')
def delete_workexp(request, pk):
    ''' view for delete instance of WorkExperience object '''

    workexp = get_object_or_404(WorkExperience, id=pk)

    if request.method == "POST":
        workexp.delete()
        return HttpResponse("")

    return HttpResponseNotAllowed(
        [
            "POST",
        ]
    )

POST-request之后查看create_cv_workexp我确实重定向到url'resume:detail-workexp'并将参数pk传递给url,它指的是id刚刚保存了对象WorkExperience的实例。然后视图 create_cv_workexp 必须是 rendres 'resume/create_cv_workexp.html' 模板,因为 'resume:detail-workexp' 使用动态模板(关于这个是下一个)。

urls.py:

app_name = 'resume'
urlpatterns = [
    
    ...

    # paths for working with WorWorkExperience objects during creating CV
    path('create-cv-workexp/<int:pk>/', views.create_cv_workexp, name='create_cv_workexp'), # here
    path('htmx/workexp/<int:pk>/', views.detail_workexp, name="detail-workexp"), # and here
    path('htmx/workexp/<int:pk>/update/', views.update_workexp, name="update-workexp"),
    path('htmx/workexp/<int:pk>/delete/', views.delete_workexp, name="delete-workexp"),
    path('htmx/create-workexp-form/<int:pk>/', views.create_workexp_form, name='create-workexp-form'),
]

detail-workexp url在views.py中调用视图detail_workexp

@login_required(login_url='/users/login/')
def detail_workexp(request, pk):
    ''' view for rendering added WorkExperience to DB and displaying it on 'resume/create_cv_workexp.html' template '''

    workexp = get_object_or_404(WorkExperience, pk=pk)
    context = {
        "workexp": workexp,
        'pk': pk
    }
    return render(request, "resume/partials/workexp_detail.html", context)

This view rendres template workexp_detail.html:

<div hx-target="this" class="mt-3 py-3 px-3 bg-white shadow border border-gray-100">
    <h3 class="text-lg leading-6 font-medium text-gray-900">
        Company: {{ workexp.company }}
    </h3>
    <p class="text-gray-600">Position: {{ workexp.position_work }}</p>
    <div class="mt-2">
        <button hx-get="{% url 'resume:update-workexp' workexp.id %}" hx-swap="outerHTML"
            class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-indigo-700 bg-indigo-100 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
            Update
        </button>
        <button hx-post="{% url 'resume:delete-workexp' workexp.id %}" hx-swap="outerHTML swap:1s"
            class="ml-2 inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-red-700 bg-red-100 hover:bg-red-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500">
            Delete
        </button>
    </div>
</div>

这里是模板create_cv_workexp.html:

{% extends 'resume/base.html' %}

{% block content %}

    <h2>Add work experience for {{ position.position_name }}</h2>
    <hr>

    <div class="container">
        <button type="button" hx-get="{% url 'resume:create-workexp-form' position.id %}" hx-target="#workexpform" hx-swap="beforeend">
            Add work experience
        </button>
    </div>

    <div id="workexpform" class="container"></div>

    <div class="container">
        {% for workexp in workexps %}
            {% include 'resume/partials/workexp_detail.html' %}
        {% endfor %}
    </div>

{% endblock content %}

这是模板workexp_form.html:

{% load tailwind_filters %}

<div hx-target="this" hx-swap="outerHTML" class="mt-3 py-3 px-3 bg-white shadow border border-gray-100">
    <form method="POST">
        {% csrf_token %}
        {{ form|crispy }}
        {% if workexp %}
        <button type="submit" hx-post="{% url 'resume:update-workexp' workexp.id %}"
            class="inline-flex items-center px-3 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
            Submit
        </button>
        <button hx-get="{% url 'resume:detail-workexp' workexp.id %}" type="button"
            class="ml-2 inline-flex items-center px-3 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
            Cancel
        </button>
        {% else %}
            <button type="submit" hx-post="{% url 'resume:create_cv_workexp' position.id %}"
            class="inline-flex items-center px-3 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
            Submit
        </button>
        {% endif %}
    </form>
</div>

我想我将所有参数从 views 传递到模板和 url,但是当我从 create_cv_view 视图中获取 POST-request 时,我得到异常:

Reverse for 'create_cv_workexp' with arguments '('',)' not found. 1 pattern(s) tried: ['create\\-cv\\-workexp/(?P<pk>[0-9]+)/\\Z']

我不知道为什么 create_cv_workexp 视图的反向不起作用,因为我将 Position 对象的 id 传递给 url

接下来是关于这个异常的一些屏幕:

请求头和响应头:

这里是异常的完整回溯:

Internal Server Error: /create-cv-workexp/3/
Traceback (most recent call last):
  File "D:\virtualenv\mysite\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "D:\virtualenv\mysite\Lib\site-packages\django\core\handlers\base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\virtualenv\mysite\Lib\site-packages\django\contrib\auth\decorators.py", line 23, in _wrapper_view
    return view_func(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\python_projects\my_site\mysite\resume\views.py", line 286, in create_cv_workexp
    return render(request, "resume/partials/workexp_form.html", context={
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\virtualenv\mysite\Lib\site-packages\django\shortcuts.py", line 24, in render
    content = loader.render_to_string(template_name, context, request, using=using)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\virtualenv\mysite\Lib\site-packages\django\template\loader.py", line 62, in render_to_string
    return template.render(context, request)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\virtualenv\mysite\Lib\site-packages\django\template\backends\django.py", line 61, in render
    return self.template.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\virtualenv\mysite\Lib\site-packages\django\template\base.py", line 175, in render
    return self._render(context)
           ^^^^^^^^^^^^^^^^^^^^^
  File "D:\virtualenv\mysite\Lib\site-packages\django\template\base.py", line 167, in _render
    return self.nodelist.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\virtualenv\mysite\Lib\site-packages\django\template\base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\virtualenv\mysite\Lib\site-packages\django\template\base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\virtualenv\mysite\Lib\site-packages\django\template\base.py", line 966, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "D:\virtualenv\mysite\Lib\site-packages\django\template\defaulttags.py", line 321, in render
    return nodelist.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\virtualenv\mysite\Lib\site-packages\django\template\base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\virtualenv\mysite\Lib\site-packages\django\template\base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\virtualenv\mysite\Lib\site-packages\django\template\base.py", line 966, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "D:\virtualenv\mysite\Lib\site-packages\django\template\defaulttags.py", line 471, in render
    url = reverse(view_name, args=args, kwargs=kwargs, current_app=current_app)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\virtualenv\mysite\Lib\site-packages\django\urls\base.py", line 88, in reverse
    return resolver._reverse_with_prefix(view, prefix, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\virtualenv\mysite\Lib\site-packages\django\urls\resolvers.py", line 828, in _reverse_with_prefix
    raise NoReverseMatch(msg)
django.urls.exceptions.NoReverseMatch: Reverse for 'create_cv_workexp' with arguments '('',)' not found. 1 pattern(s) tried: ['create\\-cv\\-workexp/(?P<pk>[0-9]+)/\\Z']  
[17/May/2023 21:00:03] "POST /create-cv-workexp/3/ HTTP/1.1" 500 150333

所以我真的不知道出了什么问题...我没有在 Stack Overflow 和其他资源中找到解决方案...请帮忙!!!

django url redirect django-views htmx
1个回答
0
投票

在 workexp_form.html 你也使用

position
但它不在上下文中:

        {% else %}
            <button type="submit" hx-post="{% url 'resume:create_cv_workexp' position.id %}"
            class="inline-flex items-center px-3 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
            Submit
        </button>
        {% endif %}
else:
            return render(request, "resume/partials/workexp_form.html", context={
                "form": form
            })
© www.soinside.com 2019 - 2024. All rights reserved.