Reputation: 141
I want to get the properties by categories.
When I try to reach for example : http://127.0.0.1:8000/category/Berlin the following error comes:
'CategoryDetail' object has no attribute '_state'
What am I doing wrong?
Any help will be highly appreciated.
models.py
from django.db import models
from django.urls import reverse
# Create your models here.
class Property(models.Model):
title = models.CharField(max_length=140)
description = models.TextField()
rooms = models.PositiveIntegerField()
bed_rooms = models.PositiveIntegerField()
land_size = models.PositiveIntegerField()
slug = models.SlugField()
living_size = models.PositiveIntegerField()
build_year = models.PositiveIntegerField()
price = models.PositiveIntegerField()
category = models.ForeignKey(to='Category', null=True, related_name='categories', on_delete=models.SET_NULL,)
def get_absolute_url(self):
return reverse('properties:PropertyDetail', kwargs={'slug': self.slug})
def __str__(self):
return '{} - {}'.format(
self.title, self.build_year
)
class Category(models.Model):
name = models.CharField(max_length=150)
slug = models.SlugField()
def get_absolute_url(self):
return reverse('properties:CategoryDetail', kwargs={'slug': self.slug})
def __str__(self):
return '{}'.format(self.name)
views.py
from django.shortcuts import render
from .filters import PropertyFilter
# Create your views here.
from django.views.generic import (
TemplateView, DetailView, ListView
)
from .models import Property, Category
class HomePage(TemplateView):
template_name = 'home.html'
class PropertyList(ListView):
model = Property
class PropertyDetail(DetailView):
model = Property
class CategoryList(ListView):
model = Category
class CategoryDetail(ListView):
queryset = Category.categories
template_name = 'categorydetail.html'
def search(request):
property_list = Property.objects.all()
property_filter = PropertyFilter(request.GET, queryset=property_list)
return render(request, 'prop_list.html', {'filter': property_filter})
urls.py
from django.urls import path
from .views import (PropertyList, PropertyDetail, HomePage, CategoryList, CategoryDetail)
app_name = 'properties'
urlpatterns = [
path('', HomePage.as_view(), name='home'),
path('categories', CategoryList.as_view(), name='categories'),
path('category/<slug:slug>', CategoryDetail.as_view(), name='CategoryDetail'),
path('properties', PropertyList.as_view(), name='PropertyList'),
path('property/<slug:slug>', PropertyDetail.as_view(), name='PropertyDetail'),
]
project urls
"""config URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from realestate import views
urlpatterns = [
path('admin/', admin.site.urls),
path('search', views.search, name='search'),
path('', include('realestate.urls', namespace='properties')),
]
Upvotes: 1
Views: 1473
Reputation: 6554
For some reason my model was just having an issue being called twice to do an action on a ForeignKey. Simply refetching the object before trying to reuse it fixed my issue.
Upvotes: 0
Reputation: 12032
Please adapt the following line in the class Property
:
category = models.ForeignKey(to='Category', null=True, related_name='categories', on_delete=models.SET_NULL,)
to this:
category = models.ForeignKey(
to='Category',
null=True,
related_name='properties',
on_delete=models.SET_NULL,
)
The change is in bold. The reason for the change is that you want to get properties for a given Category. Making a query like Category.categories
is confusing for the reader.
The related_name
which is now properties
can be accessed from a Category
object. The way how you do it in the class CategoryDetail
:
queryset = Category.categories
even if adapted to:
queryset = Category.properties
can't work. It is because it would call the related descriptor ReverseManyToOneDescriptor
which is of no use here.
You can call the properties for an object Category
. An example would be:
Category.objects.get(pk=1).properties.all()
The first part Category.objects.get(pk=1)
will return a single object Category
and in the second part properties.all()
you call the manager properties
on the object Category
. With the method all()
you retrieve a QuerySet containing all properties
for the given Category
object.
Remember that using something like Category.objects.all().properties.all()
won't work. Category.objects.all()
will return a QuerySet which has no attribute properties
.
With this in mind the best approach would be as suggested by @Alasdair to use DetailView
. You want to list all Properties for a Category and that confuses you a little bit. However you're still displaying the details for a Category. Using a DetailView
for the Category
here is a much cleaner and nicer approach.
Having just simple:
class CategoryDetail(DetailView):
model = Category
will do the job. The default template name will be category_detail.html
and you don't need to specify it explicitly. Just be consistent with the other classes.
The internal logic of the detail view will retrieve a single object Category
and in the template you can access the properties
for this object:
{% for property in object.properties.all %}
{{ property }}
{% endfor %}
Instead of object.properties.all
you can use category.properties.all
, which is more verbose and improves the readability.
Should you wish to change that, do this for example:
class CategoryDetail(DetailView):
model = Category
context_object_name = 'ctgr'
{% for property in ctgr.properties.all %}
I hope this can bring you forward.
Upvotes: 1
Reputation: 309089
First, change the related_name
to properties
as @cezar suggested, since it is used to get the related properties for a category.
category = models.ForeignKey(to='Category', null=True, related_name='categories', on_delete=models.SET_NULL,)
I suggest you use a DetailView
for category,
class CategoryDetail(DetailView):
model = Category
template_name = 'categorydetail.html'
Then you can use the reverse relationship to access the properties in the template:
{% for property in category.properties.all %}
If you really want to use a list view for CategoryDetail
, then you need to override get_queryset
and use the slug
. For example you could do:
from django.shortcuts import get_object_or_404
class PropertiesForCategory(ListView):
template_name = 'categorydetail.html'
def get_queryset(self):
category = get_object_or_404(Category, slug=self.kwargs['slug']
return category.properties.all()
Then in the template you would loop through the queryset with:
{% for property in object_list %}
Upvotes: 1