I'm getting a NoReverseMatch error resulting from my template url tag. But I'm using class based views, and the error is related to using the get_context_data function. If I comment out the get_context_data function, I don't get the error.
Here's the error I'm getting:
NoReverseMatch at /task-manager/update-project/e75eac16-711b-4fb7-9b08-7516cae8433f/
Reverse for 'update-project' with arguments '('',)' and keyword arguments '{}' not found. 1 pattern(s) tried: ['task-manager/update-project/(?P[a-zA-Z0-9_-]+)/$']
And then:
Error during template rendering
In template [projectdir]/[appdir]/templates/tasks
/update-project.html, error at line 7
Reverse for 'update-project' with arguments '('',)' and keyword arguments '{}' not found. 1 pattern(s) tried: ['task-manager/update-project/(?P<pk>[a-zA-Z0-9_-]+)/$']
Here is the update-project.html:
{% extends "tasks/base.html" %}
{% block content %}
<h2>Update a project</h2>
<div class="form">
<form action="{% url 'task_manager:update-project' project.id %}" method="POST">
{% csrf_token %}
{{ form }}
<BR><BR>
<input type="submit" value="Submit" />
</form>
</div>
....
My application's urlconf is tasks/urls.py:
from django.conf.urls import url
from . import views
urlpatterns = [
# Index
url(r'^$', views.TaskView.as_view(), name='index'),
# TASKS
# E.g., /task/3j243o-Ofjdsof-3123
url(r'^tasks/(?P<pk>[0-9a-zA-Z_-]+)/$', views.TaskDetailView.as_view(), name='task_detail'),
# Adding tasks
url(r'^add-task/$', views.TaskCreate.as_view(), name='add_task'),
# Update/view tasks
url(r'^view-task/(?P<pk>[0-9a-zA-Z_-]+)/$', views.TaskUpdate.as_view(), name='view_task'),
# PROJECTS
# E.g., /project/234jf0we-324skl-34j
url(r'^projects/(?P<pk>[a-zA-Z0-9_-]+)/$', views.ProjectDetailView.as_view(), name='project_detail'),
# Adding projects
url(r'^add-project/$', views.ProjectCreate.as_view(), name='add_project'),
# Updating/viewing Projects
url(r'^update-project/(?P<pk>[a-zA-Z0-9_-]+)/$', views.ProjectUpdate.as_view(), name='update-project')
]
The relevant part of views.py is:
class ProjectUpdate(UpdateView):
"""
This will be used to view and update projects
"""
template_name = 'tasks/update-project.html'
model = Project
context_object_name = 'project'
fields = ['name','status', 'purpose', 'vision', 'big_steps', 'context', 'priority', 'due_date', 'related_project']
def get_context_data(self, **kwargs):
"""
This pulls in related tasks to display them as links.
"""
# Call base implementation first to get a context
context = super(ProjectUpdate, self).get_context_data(**kwargs)
# TO DO: Add in querysets of related tasks
context['related_tasks'] = Task.objects.all()
And relevant part of model.py:
class Project(models.Model):
"""
Project is for any multi-step thing that needs to be done. Tasks
will be associated with it.
"""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=200)
purpose = models.TextField(blank=True)
"""What is my purpose with this task?"""
vision = models.TextField(blank=True)
"""Vision refers to what the task will look like when successfully
completed"""
big_steps = models.TextField(blank=True)
"""What are the big steps (not task-level steps) that need to be
completed? Maybe split this out into a new class, similar to tasks"""
status = models.CharField(max_length=30, choices=STATUSES, default='pending')
created_date = models.DateTimeField(auto_now_add=True)
updated_date = models.DateTimeField(auto_now=True)
context = models.CharField(max_length=50, choices=CONTEXTS)
priority = models.CharField(max_length=50, choices=PRIORITIES)
due_date = models.DateTimeField(blank=True, null=True)
related_project = models.ForeignKey("self", blank=True, null=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('task_manager:project_detail', kwargs={'pk': self.pk})
What is weird is that if I comment out the get_context_data function in my view, it works just fine.
It looks like you have forgotten to return the context. Without the return statement, it implicitly returns None. This causes the reverse match, because the url tag gets None instead of the required project id.
def get_context_data(self, **kwargs):
"""
This pulls in related tasks to display them as links.
"""
# Call base implementation first to get a context
context = super(ProjectUpdate, self).get_context_data(**kwargs)
# TO DO: Add in querysets of related tasks
context['related_tasks'] = Task.objects.all()
return context
Related
I am having trouble figuring out where my issue is. I am parsing a slug to the url, via my view pulling from the SlugField in my model. For an object instance that exists in my database, the slug is being parsed successfully into the url. However, I am receiving the above error and cannot work out why.
The corresponding model is Booking, and the slug field is as follows:
booking_reference = models.SlugField(verbose_name="Slug Field", blank=False, unique=True)
(I wanted to use the booking_reference as the slug field).
My views.py is as follows:
class EditBookingView(UpdateView, NextUrlMixin):
model = Booking
form_class = BookingForm
template_name = 'bookings/edit_booking.html'
success_url = '/'
default_next = '/'
def form_valid(self, form):
form.instance.user = self.request.user
form.save()
next_path = self.get_next_url()
return redirect(next_path)
def get_context_data(self, **kwargs):
context = super(EditBookingView, self).get_context_data(**kwargs)
context['slug'] = self.kwargs['booking_reference']
return context
def get_object(self):
slug = self.kwargs.get('booking_reference')
return get_object_or_404(Booking, booking_reference=slug)
And my urls.py are:
from django.urls import path
from .views import (
CreateBookingView,
ViewBookingsView,
EditBookingView,
DeleteBookingView
)
app_name = 'bookings'
urlpatterns = [
path('createbooking/<str:slug>/', CreateBookingView.as_view(), name='create_booking'),
path('viewbookings/<str:slug>/', ViewBookingsView.as_view(), name='view_booking'),
path('editbooking/<str:slug>/', EditBookingView.as_view(), name='edit_booking'),
path('cancelbooking/<str:slug>/', DeleteBookingView.as_view(), name='delete_booking'),
]
Please note, I have a list view for which the booking reference is being displayed under each model instance successfully. It is from this view that I can then go into the above EditView with the following line in the listview template:
<div class="row text-center mt-3">
<a class="btn btn-primary justify-content-md-center mb-5" href='{% url "bookings:edit_booking" slug=item.booking_reference %}' role="button" >Edit Booking</a>
</div>
As I say I have double checked whether the slug being parsed into my url is in fact present in my database under the "booking_reference" field for the model instance, and it is.
To trace the problem back:
You are getting a 404
You have a get_object_or_404 in your get_object() method, where you look up a Booking based on a variable 'slug'
The variable slug is set when you ask for self.kwargs.get('booking_reference')
And the kwarg 'booking_reference' is set...?
In your URLs file the variable is <str:slug>. Django doesn't actually know much else about it in relation to the model until it is used in some way,eg, to get an instance.
So trying to get('booking_reference') is returning None. The model knows what a booking_reference is, but the URL doesn't provide that connection. Hence the 404.
To fix: you just need to switch to
self.kwargs.get('slug')
(You may also need to update your get_context() in a similar manner)
Hi I am currently facing a problem in redirecting the user to my directs-app.
The NewConversation in directs/views.py starts a new conversation. It can be clicked in the profile-page user-profile.html of the users-app. Now I wanna do the same in my single-project.html in my projects-app. But I am getting the error above. Thanks for your help!
directs/views.py
def NewConversation(request, username):
from_user = request.user
body = ''
try:
to_user = User.objects.get(username=username)
except Exception as e:
return redirect('search-users')
if from_user != to_user:
Message.sender_message(from_user, to_user, body)
return redirect('message')
directs/urls.py
urlpatterns = [
path('new/<username>', views.NewConversation, name="conversation"),
]
users/models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True)
name = models.CharField(max_length=200, blank=True, null=True)
username = models.CharField(max_length=200, blank=True)
users/views.py
def userProfile(request, pk):
profile = Profile.objects.get(id=pk)
context = {'profile':profile}
return render(request, 'users/user-profile.html', context)
users/user-profile.html
<h2 class="dev__name">{{profile.name}}</h2>
<img class="avatar avatar--xl dev__avatar" src="{{profile.profile_image.url}}" />
Message
projects/views.py
def project(request, pk):
projectObj = Project.objects.get(id=pk)
context = {'project':projectObj}
return render(request, 'projects/single-project.html', context)
projects/models.py
class Project(models.Model):
owner = models.ForeignKey(Profile, null=True, blank=True, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False)
projects/single-project.html
<div class="project__tags" style="margin-top: 20px;">
Send message
</div>
projects.urls.py
urlpatterns = [
path('project/<str:pk>/', views.project, name='project'),
]
I think you should also post the entire traceback in your post for more coherence.
Let me start with the error:
NoReverseMatch at /project/360c2471-44ff-4d8c-b536-c9da3448c002/ Reverse for 'conversation' with arguments '('',)' not found. 1 pattern(s) tried: ['message/new/(?P<username>[^/]+)\\Z']
Your url for conversation is declared as follows:
path('new/<username>', views.NewConversation, name="conversation"),
]
What you should improve here: First you should declare what type of argument you are expecting. <str:username> I think you are expecting the string.
Now let's look at the error. You are receiving the argument '' which is an empty string. The urlpath conversation is expecting something that is not an empty string. Why though it is an empty string?
{% url 'conversation' profile.user %} and {% url 'conversation' project.owner.user %}
profile.user will not be what you are expecting as there is no specific representation given to profile.user that django templates knows how to translate. Instead, you are giving it the entire User object.
I suggest you to either put there profile.user.username, or adding the ___str___ method to your Profile Model.
Something like:
def __str__(self):
return self.username
I also recommend to improve your function based views by telling what kind of request are you expecting (here it is a GET I assume).
Also, have a look here
I have an application called "school" inside one of my django projects.
Below is the code of models.py
from django.db import models
class Student(models.Model):
name = models.CharField(max_length=255)
birthday = models.DateField(blank=True)
class Class(models.Model):
name = models.CharField(max_length=255)
student = models.ForeignKey(Student,related_name='classes',null=True)
def __str__(self):
return self.name
And now, views.py:
from django.shortcuts import render
from .models import *
def test(request):
obj2 = Student.objects.get(name='john')
return render(request,'test/list.html', {'obj2':obj2} )
And finally, my template looks like this:
{% block content %}
<h2>
{{ obj2.classes }}
</h2>
{% endblock %}
In my template, I am using obj2.classes (i.e., responseobject.related_name). I want it to print the class name.
However when I access the site at http://127.0.0.1:8000/shop/ ,
it gives me this output:
shop.Class.None
How will I get the output as only "Class", that is the class name?
Would obj2._meta.get_field('classes').related_model.__name__ do the job? This will work on your view only, not on the template.
def test(request):
obj2 = Student.objects.get(name='john')
classes_name = obj2._meta.get_field('classes').related_model.__name__
return render(request, 'test/list.html',
{'obj2':obj2, 'classes_name': classes_name})
Using this method, you avoid to use obj2.classes, which hits the database to retrieve the object.
You can also get the verbose_name with obj2._meta.get_field('classes').related_model._meta.verbose_name.
UPDATE #2
Status: Still not solved
Updated: Thurs. Dec. 18, 11:30 a.m.
I'm currently using FullArticle.objects.order_by('?').first() to get a random article from my database, but it's not working. There is probably something missing from my models, view or url.py that's missing.
models.py
from django.db import models
from django.core.urlresolvers import reverse
# Create your models here.
class FullArticleQuerySet(models.QuerySet):
def published(self):
return self.filter(publish=True)
class FullArticle(models.Model):
title = models.CharField(max_length=150)
author = models.CharField(max_length=150)
slug = models.SlugField(max_length=200, unique=True)
pubDate = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
category = models.CharField(max_length=150)
heroImage = models.CharField(max_length=250, blank=True)
relatedImage = models.CharField(max_length=250, blank=True)
body = models.TextField()
publish = models.BooleanField(default=True)
gameRank = models.CharField(max_length=150, blank=True, null=True)
objects = FullArticleQuerySet.as_manager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("FullArticle_detailed", kwargs={"slug": self.slug})
class Meta:
verbose_name = "Blog entry"
verbose_name_plural = "Blog Entries"
ordering = ["-pubDate"]
views.py
from django.views import generic
from . import models
from .models import FullArticle
# Create your views here.
class BlogIndex(generic.ListView):
queryset = models.FullArticle.objects.published()
template_name = "list.html"
randomArticle = FullArticle.objects.order_by('?').first()
class BlogDetail(generic.DetailView):
model = models.FullArticle
template_name = "detailed.html"
urls.py
from django.conf.urls import patterns, url
from . import views
urlpatterns = patterns(
'',
url(r'^$', views.BlogIndex.as_view(), name="list"),
url(r'^(?P<slug>\S+)', views.BlogDetail.as_view(), name="detailed"),
)
Section in list.html that I want to be random
<div class="mainContent clearfix">
<div class="wrapper">
<h1>Top 10 Video Games</h1>
{% for article in object_list|slice:":1" %}
<p class="date">{{article.pubDate|date:"l, F j, Y" }}</p> | <p class="author">{{article.author}}</p>
<img src="{{article.heroImage}}" alt="" class="mediumImage">
<p class="caption">{{article.body|truncatewords:"80"}}</p>
{% endfor %}
I assume that FullArticle.objects.order_by('?')[0] will give me a
random item from my class of FullArticle. But, let's say that out of
my model, I only want data associated with the specific parts of the
article: title, author, heroImage and body. How would I go about doing
that?
To get specific fields of an object, use values or values_list. The first will return dictionaries, the second tuples:
FullArticle.objects.order_by('?').values('title','author','heroImage','body').first()
The above would result in something like:
{'title': 'Foo', 'author': 'Foo Author', ... }
I've also tacked on your suggestion of random =
FullArticle.objects.order_by('?')[0] called it "random" instead.
Not sure what this is about, but try to avoid shadowing built-in libraries, like random.
1) Actually you almost did it.
try:
article = FullArticle.objects.order_by('?')[0]
except IndexError:
article = None
2) You could use this in models.py as well as in views.py. IMHO there is no need to extract this string to separate method so I would write this code wherever I need it.
3) Better use ORM don't convert db result to list to choose first item. This is can be really memory and CPU expensive.
Getting a random article would usually be done in a view, or as a modelmanager method, or as a class method. Fullarticle.random should not be a class attribute. That will not work as you expect.
# Used in a view.
article = FullArticle.objects.order_by('?').first()
# you can also make a random() method in your model manager.
def random(self):
return self.get_queryset().order_by('?').first()
# or as a class method inside FullArticle
#classmethod
def random(cls):
return cls.objects.order_by('?').first()
I'm not quite sure what exactly you mean by this.
I only want data associated with the specific parts of the article: title, author, heroImage and body. How would I go about doing that?
To access specific attributes you do this:
title = article.title
author = article.author
If you don't need to use article.category, just don't access it.
from django.views.generic import DetailView
from books.models import Publisher, Book
To pass data from your (class based) View to the template it has to be added to the context.
Here's an example from the official documentation:
class PublisherDetail(DetailView):
model = Publisher
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(PublisherDetail, self).get_context_data(**kwargs)
# Add in a QuerySet of all the books
context['book_list'] = Book.objects.all()
return context
source: https://docs.djangoproject.com/en/1.7/topics/class-based-views/generic-display/#adding-extra-context
Lots of people find Class Based Views in Django to be a bit confusing. I would recommend that you understand how function based views work before you start doing anything complicated with CBVs.
I am trying to create a delete function for my Workout model.
This is the model:
class Workout(models.Model):
workoutID = models.AutoField(primary_key=True)
name = models.CharField(max_length=40)
created_by = models.ForeignKey(User)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def delete(self):
return reverse("delete_workout", kwargs = {'workout_id': self.workoutID})
Next I have the view:
def delete_workout(request, workout_id):
workout = get_object_or_404(Workout, workoutID = workout_id)
print(workout)
if request.user != workout.created_by:
return HttpResponse('Not ur workout')
else:
workout.delete()
return HttpResponseRedirect('/')
This is the url:
url(r'^(?P<workout_id>\d+)/delete/$', views.delete_workout, name='delete_workout'),
And finally the html:
<a href='{{ instance.delete }}'>
<button>Delete Workout</button>
</a>
I'm not getting any errors in the console, which is why I don't know what is going wrong.
You are overriding delete method of the class just for getting the delete url. You will get the url by url function in the template like {% url delete_workout instance.workoutID %}. So remove the delete function from the model change your html href url. Leave the view and url as the same. No issues there
class should be
class Workout(models.Model):
workoutID = models.AutoField(primary_key=True)
name = models.CharField(max_length=40)
created_by = models.ForeignKey(User)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
And your html should be
<a href='{% url delete_workout instance.workoutID %}'>
<button>Delete Workout</button>
</a>
NOTE: django model itself adds id for each table, so you dont have to specify it as you did workoutID = models.AutoField(primary_key=True).
By default each model will have a id field just like id = models.AutoField(primary_key=True)
If you consider removing the workoutID then the model becomes
class Workout(models.Model):
name = models.CharField(max_length=40)
created_by = models.ForeignKey(User)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
and the html will be
<a href='{% url delete_workout instance.id %}'>
<button>Delete Workout</button>
</a>
Django has all the tools for you under the hood. Don't reinvent the wheel. You can refactor and simplify your code.
First remove the method delete in Workout.
Second, replace your function-based-view with a class-based-view:
from django.views.generic.edit import DeleteView
from django.urls import reverse_lazy
from django.http import Http404
from .models import Workout
class WorkoutDeleteView(DeleteView):
model = Workout
success_url = reverse_lazy('delete_workout')
def get_object(self):
obj = super().get_object()
if obj.created_by != self.request.user:
raise Http404
return obj
A workout can be deleted only by its author. In success_url you specify the target where the user should be redirected after deleting.
Just adapt slightly your urls.py (pay attention to the emphasised part):
url(r'^(?P<pk>\d+)/delete/$', views.WorkoutDeleteView.as_view(), name='delete_workout'),
EDIT:
You can name your views as you please, however it would be better to follow already well established conventions. Thus the names for the class based views should be workout-list, workout-detail, workout-create, workout-update and workout-delete.