I’m having no fun with a simple CreateView issue, which has been so frustrating that I’m seriously considering ditching class-based views entirely (so that at least I would understand what I don’t know and could ask a better question).
What I think I want to do is to pass the primary key of the Task model through the steps below, so that each instance of the Note model is linked to the correct instance of the Task model.
I have a Task model and a Note model. The Note model has a field, task, which is a primary key to the Task model:
class Note(models.Model):
task = models.ForeignKey(
Task, on_delete=models.CASCADE, related_name="notes", null=False, blank=False
) # Required, links to tasks.Task
text = models.TextField(
null=False, blank=False,
) # Required
The TaskDetailView renders a template (task_detail.html) which includes a link to add new note:
<a href="{% url 'tasks:note_new' task.id %}" class="btn btn-primary">Add Note</a>
The application urls.py file directs this request (and the argument) as follows:
path("note/<int:task_id>/", NoteCreateView.as_view(), name="note_new"),
The NoteCreateView in views.py is:
class NoteCreateView(CreateView):
model = Note
form_class = NoteForm
template_name = "tasks/note_detail.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["page_title"] = "New Note"
return context
def form_valid(self, form):
form.instance.author = self.request.user
form.instance.task_id = self.kwargs.get("task_id")
print("\nin form_valid: ", self.kwargs.get("task_id"))
return super().form_valid(form)
def get_success_url(self, **kwargs):
return reverse_lazy('tasks:edit', kwargs={'pk': self.kwargs.get("task_id")})
This renders a new, small ModelForm:
forms.py:
class NoteForm(ModelForm):
class Meta:
model = Taskmodel = Note
fields = ["text"]
widgets = {
"text": forms.TextInput(attrs={
"class": "input input-primary"
}
)
}
note_detail.html:
<form action="{% url 'tasks:note_new' task_id=task.id %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save" class="btn btn-accent" />
</form>
I think the current error is:
NoReverseMatch at /tasks/note/85/
Reverse for ‘note_new’ with keyword arguments ‘{‘task_id’: ‘’}’ not found. 1 pattern(s) tried: [‘tasks/note/(?P<task_id>[0-9]+)/\Z’]
85 is the correct pk for the task. And the traceback points to the form definition and url in note_detail.html as the cause of the error.
My suspicion is that the task pk is not being passed from the first page (which renders task_detail.html and provides the link to CreateView), so the template for the CreateView can’t reverse the URL back to the originating task. I can’t seem to dig through the layers of abstraction in the CBV to work out why or how and where to override it, though.