Django с использованием общих представлений, ошибка http 405

Я пытаюсь использовать общий вид django для CRUD, но DeleteView приводит к ошибке 405, следуя официальному руководству Django https://docs.djangoproject.com/en/2.1/ref/class-based-views/generic-editing/, я не понимаю, где это моя ошибка, так что мой код:

views.py

from django.urls import reverse
from django.views.generic import (
    CreateView,
    DetailView,
    ListView,
    UpdateView,
    ListView,
    DeleteView
)

from .forms import ContatoModelForm
from .models import Contato
class ContatoCreateView(CreateView):
    template_name = 'contato/contato_create.html'
    form_class = ContatoModelForm
    model = Contato

class ContatoListView(ListView):
    template_name = 'contato/contato_list.html'
    model = Contato

class ContatoDetailView(DetailView):
    template_name = 'contato/contato_detail.html'
    model = Contato

class ContatoUpdateView(UpdateView):
    template_name = 'contato/contato_create.html'
    form_class = ContatoModelForm
    model = Contato

class ContatoDeleteView(DeleteView):
    template_name = 'contato/contato_delete.html'
    model = Contato
    def get_success_url(self):
        return reverse('contato:contato-list')

urls.py

 from django.urls import path
 from .views import (
     ContatoCreateView,
     ContatoDeleteView,
     ContatoDetailView,
     ContatoListView,
     ContatoUpdateView,
)
app_name = 'contato'
urlpatterns = [
    path('', ContatoListView.as_view(), name='contato-list'),
    path('create/', ContatoCreateView.as_view(), name='contato-create'),
    path('<int:pk>/', ContatoDetailView.as_view(), name='contato-detail'),
    path('<int:pk>/update/', ContatoUpdateView.as_view(), name='contato-update'),
    path('<int:pk>/delete/', ContatoDeleteView.as_view(), name='contato-delete'),
]

contato_delete.html

<form action='.' method='post'>{% csrf_token %}
<dialog class="mdl-dialog">
    <h6>Deseja excluir {{ object.nome }}?</h6>
    <div class="mdl-dialog__actions">
        <input type="submit" class="mdl-button mdl-js-button mdl-button--accent" value="Excluir">
        <button type="button" class="mdl-button close">Cancelar</button>
    </div>
</dialog>
</form>

contato_detail.html

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

{% block content %}
<div class="mdl-card__title">
    <h2 class="mdl-card__title-text">{{ object.nome }}</h2>
</div>
<div class="mdl-layout-spacer"></div>
<a id="show-dialog"  class="mdl-button mdl-js-button mdl-button--icon">
  <i class="material-icons">delete</i>
</a>

{% include "contato/contato_delete.html" %}

<script type="text/javascript" src="{% static 'js/dialog.js' %}"></script>

<div class="mdl-card__actions mdl-card--border">
    <ul class="demo-list-icon mdl-list">

        <li class="mdl-list__item">
            <span class="mdl-list__item-primary-content">
    <i class="material-icons mdl-list__item-icon">call</i>
    {{ object.celular }}
  </span>
        </li>

        <li class="mdl-list__item">
            <span class="mdl-list__item-primary-content">
    <i class="material-icons mdl-list__item-icon">email</i>
    {{ object.email }}
  </span>
        </li>
        <li class="mdl-list__item">
            <span class="mdl-list__item-primary-content">
    <i class="material-icons mdl-list__item-icon">business</i>
    {{ object.cargo }} na {{ object.empresa}}
  </span>
        </li>
    </ul>

</div>

<a href="{% url 'contato:contato-update' object.id %}" id="fab" class="mdl-button mdl-js-button mdl-button--fab mdl-button--primary">
     <i class="material-icons">create</i>
    </a>

{% endblock %}

contato_list.html

 {% extends "base.html" %}
 {% block content %}
 <style>
  .demo-list-three {
    width: 800px;
    margin-left: 30px;
  }
  #fab {
    position: fixed;
    display: block;
    right: 0;
    bottom: 0;
    margin-right: 40px;
    margin-bottom: 40px;
    z-index: 900;
  }
</style>

<form method='GET' action=''>
</form>
{% for obj in object_list %}
<ul class="demo-list-three mdl-list">
  <li class="mdl-list__item mdl-list__item--three-line">
    <span class="mdl-list__item-primary-content">
        <i class="material-icons mdl-list__item-avatar">person</i>
        <a href='{{ obj.get_absolute_url }}'><span>{{ obj.nome }}</span> 
 </a>
    <span class="mdl-list__item-text-body">
          {{ obj.celular }} <br>
          {{ obj.email }}
         </span>
    </span>

    <a href="{{ obj.get_absolute_url }}" class="mdl-button mdl-js-button mdl-button--primary">
  DETALHES
</a>
  </li>
</ul>
 {% endfor %}

 <a href="{% url 'contato:contato-create' %}" id="fab" class="mdl-button mdl-js-button mdl-button--fab mdl-button--primary">
     <i class="material-icons">person_add</i>
    </a>


 {% endblock content %}

person Patrick Amaral    schedule 23.09.2018    source источник
comment
405, правда? 404 кажется более правдоподобным   -  person gogaz    schedule 24.09.2018
comment
Да, метод 405 запрещен   -  person Patrick Amaral    schedule 24.09.2018
comment
Почему вы определяете get_object в каждом CBV? Вам просто нужно определить model = MyModel и позволить CBV справиться с этим. Это то, что написано в документации.   -  person schrodigerscatcuriosity    schedule 24.09.2018
comment
Затем в CreateView вы используете это: queryset = Contato.objects.all() Почему? Вы пытаетесь создать новый объект, а не получить его список.   -  person schrodigerscatcuriosity    schedule 24.09.2018


Ответы (2)


Упростите свой код:

class ContatoCreateView(CreateView):
    template_name = 'contato/contato_create.html'
    form_class = ContatoModelForm
    model = Contato # Tell the CBV what object you will create

    #** This doesn't make any sense, you are creating a new Contato not retrieving a queryset.
    # queryset = Contato.objects.all() 

    #** Let the ModelForm handle the validation
    # def form_valid(self, form):
    #    print(form.cleaned_data)
    #    return super().form_valid(form)


 class ContatoListView(ListView):
     template_name = 'contato/contato_list.html'
     model = Contato # This does the work for you

     #** You don't need to do this, unless you want a _specific_ queryset filterd
     # queryset = Contato.objects.all() 



 class ContatoDetailView(DetailView):
    template_name = 'contato/contato_detail.html'
    model = Contato # again, just define the model

    #** let CBV handle this logic
    # def get_object(self):
    #    id_ = self.kwargs.get("id")
    #    return get_object_or_404(Contato, id=id_)

class ContatoUpdateView(UpdateView):
    template_name = 'contato/contato_create.html'
    form_class = ContatoModelForm
    model = Contato # again, just define the model

    #** let CBV handle this logic
    # def get_object(self):
    #    id_ = self.kwargs.get("id")
    #    return get_object_or_404(Contato, id=id_)

    #** Let the ModelForm handle the validation
    #def form_valid(self, form):
    #    print(form.cleaned_data)
    #    return super().form_valid(form)


class ContatoDeleteView(DeleteView):
    template_name = 'contato/contato_delete.html'
    model = Contato # the class object you want to delete

    #** let CBV handle this logic
    # def get_object(self):
    #   id_ = self.kwargs.get("id")
    #    return get_object_or_404(Contato, id=id_)

    def get_success_url(self):
        return reverse('contato:contato-list')
person schrodigerscatcuriosity    schedule 24.09.2018
comment
Итак, как получить id в классе ContatoDetailView? @guillermochamorro - person Patrick Amaral; 24.09.2018
comment
хорошо, это здорово, понимаю, я читал документы о CBV, но в моем urls.py я изменил <int:id ›на <int:pk>, чем работает DetailView, но когда <int:id> работает ListView, а Detail - нет. @guillermochamorro. - person Patrick Amaral; 24.09.2018
comment
@PatrickAmaral Поскольку ListView извлекает список объектов, которые вы не вызываете с помощью pk, который предназначен для одного объекта. - person schrodigerscatcuriosity; 24.09.2018
comment
Я не понял, потому что я не вызвал ListView с помощью pk, я изменил URL-адрес DeitailView, и ListView перестает работать. @guillermochamorro - person Patrick Amaral; 24.09.2018
comment
Ммм, странно, что при изменении одного URL-адреса другой не работает. Но когда вы говорите, перестаньте работать, что именно вы имеете в виду? Есть ли ошибка, в каком случае? - person schrodigerscatcuriosity; 26.09.2018
comment
Да, это ошибка, когда <int:id> вернуть эту ошибку Generic detail view ContatoDetailView must be called with either an object pk or a slug in the URLconf. в детали страницы, а когда <int:pk> вернуть это Reverse for 'contato-detail' with keyword arguments '{'id': 6}' not found. 1 pattern(s) tried: ['(?P<pk>[0-9]+)\\/$'] на главной странице. @guillermochamorro - person Patrick Amaral; 26.09.2018
comment
Опубликуйте html-адрес вызова подробного URL-адреса. - person schrodigerscatcuriosity; 26.09.2018
comment
отправил @guillermochamorro - person Patrick Amaral; 26.09.2018
comment
В base.html нет ссылки на подробности, это может быть другой шаблон. - person schrodigerscatcuriosity; 26.09.2018
comment
Извините, опубликовал неправильно, опубликовал снова, это contato_list.html, @guillermochamorro - person Patrick Amaral; 26.09.2018
comment
Его там нет (contato-detail, который является URL-адресом, который вызывает ошибку), если он не obj.get_absolute_url, определенный, как я полагаю, в вашей модели, и в этом случае вы не передаете object.id. - person schrodigerscatcuriosity; 26.09.2018
comment
да, спасибо, все в порядке, теперь мы можем вернуться к вопросу, ошибка 405 в deleteview. @guillermochamorro - person Patrick Amaral; 26.09.2018
comment
Вы добавили model = Contato в свой DeleteView? - person schrodigerscatcuriosity; 27.09.2018
comment
Да, я обновлю код вопроса. @guillermochamorro - person Patrick Amaral; 28.09.2018

Вам необходимо установить action в форме, чтобы он соответствовал маршруту (URL) для удаления объекта contato.

В основном измените открытый тег <form> на:

<form action='{{ id }}/delete' method='post'>

и передайте id виду через context, например:

class ContatoDetailView(DetailView):
    model = Contato
    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        context = self.get_context_data(object=self.object)
        context['id'] = self.object.id
        return self.render_to_response(context)

В качестве дополнительной рекомендации я бы посмотрел, как сделать ваши маршруты более RESTful. См. Эту суть для информации: https://gist.github.com/alexpchin/09939db6f81d654af06b. Следует отметить, что формы HTML5 не поддерживают запросы на удаление (или размещение). Чтобы сделать запрос на удаление с помощью формы, вам нужно будет отправить запрос вручную при отправке с помощью javascript.

person Henry Woody    schedule 23.09.2018
comment
как я могу передать контекст, используя общие представления в моих методах? @henrywoody - person Patrick Amaral; 24.09.2018
comment
теперь отсутствует QuerySet, вы хотите, чтобы мой views.py был завершен? @henrywoody - person Patrick Amaral; 24.09.2018
comment
@PatrickAmaral вы определили model для ContatoDetailView? Если нет, попробуйте добавить строку model = Contato прямо под class ContatoDetailView(DetailView): - person Henry Woody; 24.09.2018
comment
ладно, это возврат Generic detail view ContatoDetailView must be called with either an object pk or a slug in the URLconf.. нужно использовать эти методы вне класса? @henrywoody - person Patrick Amaral; 24.09.2018
comment
@PatrickAmaral попробуйте изменить <int:id> в объявлении маршрута в urls.py на <int:pk> - person Henry Woody; 24.09.2018
comment
= / Не работает, нет идентификатора получения, верните это Invalid block tag on line 1: 'id'. Did you forget to register or load this tag?. @henrywoody - person Patrick Amaral; 24.09.2018
comment
{% id %} для этого {{ id }}, но остальное не работает. - person Patrick Amaral; 24.09.2018
comment
@PatrickAmaral это сработало? если нет, то какую ошибку вы сейчас получаете? - person Henry Woody; 24.09.2018
comment
да, конечно, но все же есть ошибка, мы меняем <int:id> на <int:pk> и возвращаем Reverse for 'contato-detail' with keyword arguments '{'id': 6}' not found. 1 pattern(s) tried: ['(?P<pk>[0-9]+)\\/$'] на моей главной странице, поэтому я попытался изменить views.py в методах get_object, чем работает, но на странице подробностей больше не работает с этой ошибкой Generic detail view ContatoDetailView must be called with either an object pk or a slug in the URLconf. @henrywoody - person Patrick Amaral; 24.09.2018
comment
@PatrickAmaral Я думаю, что ваши собственные get_object методы можно удалить, чтобы использовать метод по умолчанию. Вам нужно будет установить model = Contacto для всех этих представлений. Сообщите мне, изменится ли это в отношении ошибки - person Henry Woody; 24.09.2018
comment
Не работает, может быть, лучше построить представления снова без общих представлений. @henrywoody - person Patrick Amaral; 24.09.2018
comment
@PatrickAmaral не отказывается от CBV, они очень удобны и менее подвержены ошибкам, чем сами пишут все функции. - person schrodigerscatcuriosity; 24.09.2018