I'm getting an error when accesing my url "...pokemon/list/"
Reverse for 'pkmn-detail' not found. 'pkmn-detail' is not a valid view function or pattern name.
What i'm trying to do is first show a list of the created pokemons, then link to the detail page of each one through it's number.
I have defined a get_absoulte_url method and used reverse with it in my model, so here are my models, views, urls and templates relevant:
pokeworld/models.py
class Pokemon(models.Model):
pkmn_number = models.IntegerField(unique=True)
pkmn_name = models.CharField(max_length=30)
pkmn_type = models.CharField(max_length=20, choices=TYPE_CHOICES, default='Normal')
pkmn_desc = models.CharField(max_length=150)
pkmn_slug = AutoSlugField(populate_from='pkmn_name', unique_with='pkmn_number')
def get_absolute_url(self):
return reverse('pkmn-detail', kwargs={'pk': self.id})
pokeworld/views.py
class PokemonList(ListView):
model = Pokemon
template_name = 'pokeworld/pokemonlist.html'
def get_queryset(self):
return Pokemon.objects.all().order_by('pkmn_number')
def PokemonDetail(request, pk):
try:
pokemon_id = Pokemon.objects.get(pk=pk)
except Pokemon.DoesNotExist:
raise Http404("Pokemon does not exist")
return render(request, 'pokemondetail.html', context={'pkmn_id':pkmn_id})
pokeworld/templates/pokeworld/pokemondetail.html
{% extends 'baseP.html' %}
{% block title %} Detail {{ pkmn.pkmn_name }} {% endblock %}
{% block content %}
<h1>#{{ pkmn.pkmn_number }} {{ pkmn.pkmn_name }}</h1>
<p><strong>Type:</strong> {{ pkmn.pkmn_type }}</p>
{% endblock %}
pokeworld/urls.py
urlpatterns = [
...
path('pokemon/list/', PokemonList.as_view(), name='pkmn-list'),
path('pokemon/detail/<int:pk>', PokemonDetail, name='pkmn-detail'),
I really don't know what to edit, i've searched and searched but i don't know if i'm not understading the solutions that others have been given or if i have a made a mess in my code, i'm really hoping someone can point me in the right direction
Related
My django view returns a dictionary people with values for all keys in list format.
The code for the view is:
class ProjectDetail(View):
def get(self, request, pk, *args, **kwargs):
project = Project.objects.get(pk=pk)
roles = Role.objects.filter(project=pk)
people = {}
for role in roles:
try:
people[role] += Person.objects.filter(role=role.pk)
except KeyError:
people[role] = [Person.objects.filter(role=role.pk)]
context = {
'project': project,
'people': people
}
return render(request, 'project_management/project_detail.html', context)
My Models
class Project(models.Model):
title = models.CharField(max_length=2000)
introduction = models.TextField(blank=True)
class Role(models.Model):
role_name = models.CharField(max_length=30)
project = models.ForeignKey(Status, on_delete=models.CASCADE)
class Person(models.Model):
name = models.CharField(max_length=30, blank=True, null=True)
role = models.ForeignKey(Role, on_delete=models.CASCADE)
In order to iterate through the dictionary I used:
{% for designation, persons in people.items %}
<h5> {{ designation.role_name }} </h5>
<ul>
{% for person in persons %} <!-- My Problem lies here, this loop is not iterating, it's running only once-->
<li> {{person}} </li>
{% endfor %}
</ul>
{% endfor %}
The result I got is:
I want the items inside queryset listed out separately, instead of being shown inside square brackets. How can I make that happen?
You don't need to do all this work. You can pass only the project. For example with:
from django.shortcuts import get_object_or_404
class ProjectDetail(View):
def get(self, request, pk, *args, **kwargs):
project = get_object_or_404(Project, pk=pk)
return render(request, 'project_management/project_detail.html', {'project': project})
or even simpler with a DetailView [Django-doc]:
class ProjectDetail(DetailView):
queryset = Status.objects.prefetch_related(
'role_set', 'role_set__person_set'
)
template_name = 'project_management/project_detail.html'
context_object_name = 'project'
Then in the template you can render this with:
{% for role in project.role_set.all %}
<h5> {{ role.role_name }} </h5>
<ul>
{% for person in role.person_set.all %}
<li> {{ person.name }} </li>
{% endfor %}
</ul>
{% endfor %}
I have a category model and list of posts related to those category also some post with same category name but when i wanted to make list of category section in template,
it showing duplicate name of category as it related to posts like:
food,
food,
desert,
style,
desert,
but I want like:
food,
desert,
style,
here is my code:
views.py
class ListCategory(ListView):
model = Post
paginate_by = 2
template_name = 'shit.html'
context_object_name = 'queryset'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
cate = Post.objects.all()
context['cate'] = cate
return context
models.py
class Category(models.Model):
title = models.CharField(max_length=20)
thumbnail = models.ImageField()
detail = models.TextField()
featured = models.BooleanField(default=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-category', kwargs={
'pk': self.pk
})
class Post(models.Model):
title = models.CharField(max_length=100)
overview = models.TextField()
featured = models.BooleanField(default=True)
timestamp = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(Author,on_delete=models.CASCADE)
thumbnail = models.ImageField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
tags = TaggableManager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={
'pk': self.pk
})
templates
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="sidebar-box ftco-animate">
<ul class="categories">
<h3 class="heading mb-4">Categories</h3>
{% for cat in cate %}
<li>{{cat.category}}<span>(12)</span></li>
{% endfor %}
</ul>
</div>
{% endblock content %}
Thank you so much!
Seems like you want to group your Posts, based on their category; so you can achieve that by iterating over the Category (instead of Post), and use the backward relationship to find out the related Post objects.
views.py
class ListCategory(ListView):
model = Category
paginate_by = 2
template_name = 'shit.html' # :)
context_object_name = 'queryset'
template:
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="sidebar-box ftco-animate">
<ul class="categories">
<h3 class="heading mb-4">Categories</h3>
{% for category in queryset %}
<li>{{category}}<span>{{ category.posts_set.count }}</span></li>
<ul>
{% for post in category.post_set.all %}
<li>{{ post }}</li>
{% endfor %}
</ul>
{% endfor %}
</ul>
</div>
{% endblock content %}
I also use {{ category.post_set.count }} instead of 12, since I think you are looking for the number of Post objects within each category.
You can use unique=True in desired field, to make every value unique. If you'll try to add new record with same value of unique field, a django.db.IntegrityError will be raised.
More about unique
More about model's fields options
I have the following models:
class TutorialCategory(models.Model):
category_title = models.CharField(max_length=150)
category_summary = models.CharField(max_length=150)
category_slug = models.SlugField(default=1, blank=True)
class TutorialSeries(models.Model):
series_title = models.CharField(max_length=200)
series_maincategory = models.ForeignKey(
TutorialCategory, default=1, on_delete=models.SET_DEFAULT)
series_summary = models.CharField(max_length=200)
class Tutorial(models.Model):
tutorial_title = models.CharField(max_length=150)
tutorial_content = models.TextField()
tutorial_published = models.DateTimeField(
"date Published", default=datetime.now())
tutorial_series = models.ForeignKey(
TutorialSeries, default=1, on_delete=models.SET_DEFAULT)
tutorial_slug = models.SlugField(default=1, blank=True)
As shown above TutorialCategory is main category, TutorialSeries is sub category and Tutorial is sub-sub-category. I created a simple view that shows sub categories of main categories, but don't know how to show the sub-sub categories of sub category.
Please check out views.py and urls.py if you can improve its quality and if there is an easy and better way of doing it. Anyway, this is view:
def single_slug(request, single_slug):
matching_series = TutorialSeries.objects.filter(
series_maincategory__category_slug=single_slug)
series_urls = {}
for i in matching_series.all():
part_one = Tutorial.objects.filter(
tutorial_series__series_title=i.series_title).earliest("tutorial_published")
series_urls[i] = part_one.tutorial_slug
return render(request, 'tutorial/sub-category.html', context={
"tutorial_series": matching_series,
'part_ones': series_urls
})
urls here:
urlpatterns = [
path('', views.home_page, name='home'),
path('tutorial/<int:id>/', views.tutorial_detail, name='tutorial_detail'),
path('<single_slug>/', views.single_slug, name='single_slug'),
]
the template that shows sub-category of main category:
{% for tut, partone in part_ones.items %}
<div class="card">
<div class="card-body">
<h5 class="card-title">{{ tut.series_title }}</h5>
<p>{{ tut.series_summary }}</p>
Read more
</div>
</div>
{% endfor %}
Please help me how to do it and if you know any better way of doing it please let me know and help me. Thank you so much in advance.
edit: #ruddra
I changed views.py to this passing matching_series
def single_slug(request, single_slug):
matching_series = TutorialSeries.objects.filter(
series_maincategory__category_slug=single_slug)
series_urls = {}
for i in matching_series.all():
part_one = Tutorial.objects.filter(
tutorial_series__series_title=i.series_title).earliest("tutorial_published")
series_urls[i] = part_one.tutorial_slug
return render(request, 'tutorial/sub-category.html', context={
"matching_series": matching_series,
'part_ones': series_urls
})
and replaced the previous template with yours. template here:
{% for tutorial in matching_series %}
{% for sub_cat in tutorial.tutorialseries_set.all %}
{{ sub.series_title }}
{% for sub_sub_cat in sub.tutorial_set.all %}
{{ sub_sub_cat.tutorial_title }}
{% endfor %}
{% endfor %}
{% endfor %}
You can try like this:
{% for sub_cat in matching_series %}
{% for sub_sub_cat in sub_cat.tutorial_set.all %}
{{ sub_sub_cat.tutorial_title }}
{% endfor %}
{% endfor %}
Here I am assuming matching_series is being passed through context from view in single_slug. Then I am using backward relation between different models to fetch the data.
Explanation: Assuming there is object named tutorial_category which is instance of TutorialCategory. As there is ForeignKey from TutorialSeries to TutorialCategory, I can use tutorial_category.tutorialseries_set.all() or .filter() etc to fetch the tutorial series from tutorial_category object( As I am rendering it in template, I removed parenthesis after all). Similarly I fetch Tutorial from TutorialSeries.
I've read through several other questions, yet what I was looking for wasn't there.
I want to count how many times the user has gone through the ToPost/to_post.html section.
Delayed redirecting works, but incrementing number of visits doesn't: post.visit_num += 1
I have a view whose template redirects after a second:
def ToPost(request, pk):
post = Post.objects.get(pk=pk)
post.visit_num += 1
args = {'post': post}
return render(request, 'posts/to_post.html', args)
The pk is in the URL:
url(r'^(?P<pk>\d+)/to-post/$', views.ToPost, name='to_post'),
Template to_post.html:
{% extends 'base.html' %}
{% block head %}
<meta http-equiv="refresh" content="1;url={% url 'home_space:view_post' pk=post.pk %}">
{% endblock %}
{% block body %}
<div class="container">
<h2>
To post {{ post.pk }}...
</h2>
</div>
{% endblock %}
The model of Post:
class Post(models.Model):
title = models.CharField(max_length=128, verbose_name='Title')
body = models.CharField(max_length=500, verbose_name='Description')
user = models.ForeignKey(User, on_delete=models.CASCADE)
visit_num = models.PositiveIntegerField(default=0)
def __str__(self):
return self.title
You have to save the post instance after incrementing and also use F object to avoid race condition. Also don't capitalize the function name use snake_case instead, more about F objects here https://docs.djangoproject.com/en/2.1/ref/models/expressions/#f-expressions
from django.db.models import F
def to_post(request, pk):
post = Post.objects.get(pk=pk)
post.visit_num = F('visit_num') + 1
post.save()
args = {'post': post}
return render(request, 'posts/to_post.html', args)
You need to save the changes to the post:
post.save()
Edit: as #HåkenLid said, I should've asked this as 2 separate posts, the first question (regarding the template) has been answered!
I'm new to Django and I've been having a nice time with it so far. I'm trying to build a blog for fun but I've run with the some problems.
Here are my models, forms, urls, views and file tree:
http://i.imgur.com/KsLV3d9.png
For the first problem: (To save space I've only added parts of each file)
#blog/views.py
class BlogDetail(generic.DetailView):
model = Entry
template_name = "blog/entry_detail.html"
#blog/templates/blog/entry_detail.html
{% extends "base.html" %}
{% load django_markdown %}
<div class="post">
<h2>{{ object.title }}</h2>
<p class="meta">
{{ object.creation_date }} |
Tagged under {{ object.tags.all|join:", " }}
</p>
{{ object.body|markdown }}
</div>
#blog/urls.py
urlpatterns = patterns(
'',
url(r'^$', views.BlogIndex.as_view(), name="index"),
url(r'^entry/(?P<slug>\S+)$', views.BlogDetail.as_view(), name="entry_detail"),
)
#blog/models.py
class Entry(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
slug = models.SlugField(max_length=100, unique=True)
creation_date = models.DateTimeField(editable=False)
publication_date = models.DateTimeField(editable=False, null=True)
modification_date = models.DateTimeField(editable=False)
author = models.ForeignKey(User)
categories = models.ManyToManyField(Category)
tags = models.ManyToManyField(Tag)
objects = EntryQuerySet.as_manager()
def __str__(self):
return self.title
def was_published_recently(self):
now = datetime.datetime.now()
return now - datetime.timedelta(days=1) <= self.publication_date <= now
def is_published(self):
return self.publication_date is not False
is_published.boolean = True
is_published.short_description = 'Is it Published?'
def get_absolute_url(self):
return reverse("entry_detail", kwargs={"slug": self.slug})
class Meta():
verbose_name = "Blog Entry"
verbose_name_plural = "Blog Entries"
ordering = ['-creation_date']
def save(self, *args, **kwargs):
""" On save, update timestamps """
if not self.id:
self.creation_date = datetime.datetime.now()
self.modification_date = datetime.datetime.now()
return super(Entry, self).save(*args, **kwargs)
When I enter something like
localhost:8000/entry/this-is-an-entry-title-slug
e.j: http://i.imgur.com/c7yir79.png
I don't get the detail of the entry, just the title defined in base.html and that kind of stuff, I really don't know why it isn't being loaded.
As always, thank you very much for your help, and sorry if this are very basic questions but I haven't been able to find any solutions so far.
When extending templates you need to use {% block %}
If you have a block called 'content' in base.html, you can do this:
#blog/templates/blog/entry_detail.html
{% extends "base.html" %}
{% load django_markdown %}
{% block content %}
<div class="post">
<h2>{{ object.title }}</h2>
<p class="meta">
{{ object.creation_date }} |
Tagged under {{ object.tags.all|join:", " }}
</p>
{{ object.body|markdown }}
</div>
{% endblock content %}
For part 1, I think the issue is with your regex:
r'^entry/(?P<slug>\S+)$
Should be:
r'^entry/(?P<slug>[-\w\d]+)/'
Since your current pattern isn't capturing the slug, it's not being loaded into the context.
I am not much help on part 2, sorry. If you were doing this in your own view you could override the form_valid method of the CBV, but I am not sure how to accomplish the same within the built-in admin panel.