Cannot resolve keyword 'name' into field using get_queryset - django

Sorry is this is basic but I'm new at this
I am attempting to take the captured group from a url in the template, (the primary key of the story model) and then use that to filter the correct posts from the Post datbase, which it has a one(story) to many(Post) relationship with. I based the code of the docs: https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-display/#dynamic-filtering
But when I run it, I get the error:
FieldError at /story/1/
Cannot resolve keyword 'name' into field. Choices are: body, id, title, work, work_id
My code:
#views
from django.shortcuts import get_object_or_404, render
from django.views.generic import ListView, DetailView
from . models import Post, Story
class StoryListView(ListView):
model = Story
template_name = 'home.html'
class PostListView(ListView):
template_name = 'story_overview.html'
def get_queryset(self):
self.work_id=get_object_or_404(Post, name=self.kwargs['pk'])
return Post.objects.filter(work_id=self.work_id)
#urls
from django.urls import path
from . import views
urlpatterns = [
path('', views.StoryListView.as_view(), name='home'),
path('story/<int:pk>/', views.PostListView.as_view(), name='story_overview'),
]
#templates/home.html
{% extends 'base.html' %}
{% block content %}
{% for post in object_list %}
<h2>{{ post.title }}</h2>
<p>{{ post.description }}</p>
{% endfor %}
{% endblock content %}
#models
from django.db import models
class Story(models.Model):
title = models.CharField(max_length=200)
description = models.CharField(max_length=1500, default= "Description")
def __str__(self):
return self.title
class Post(models.Model):
title = models.CharField(max_length=200, default= "Title")
work = models.ForeignKey(Story,on_delete=models.CASCADE,)
body = models.TextField()
def __str__(self):
return self.title

If you filter using work_id then you should use the integer.
def get_queryset(self):
return Post.objects.filter(work_id=self.kwargs['pk'])
If you want to fetch the Story instance then you should call get_object_or_404 with Story, and filter on the pk field instead of name:
def get_queryset(self):
self.work=get_object_or_404(Story, pk=self.kwargs['pk'])
return Post.objects.filter(work=self.work)
Note I've renamed work_id to work because it's a model instance, not an id.

You are trying to get object from Post() model using name field. Is name field present in your Post() model?
Instead of doing this
self.work_id=get_object_or_404(Post, name=self.kwargs['pk'])
Do this
self.work_id=get_object_or_404(Post, id=self.kwargs['pk'])
Hope it helps!

Related

Slugfield not working if field name is different from slug

Good evening, Django is completely new for me and it's my first question here.
I'm trying to create a Webapp. I'm using Django, Python and MariaDB.
I created a project with two apps and I have a model for each app. In the fist one I used "slug" as field name and everything is working fine. In the second one I wanted to differentiate that field giving a different name (bk_slug) defined as SlugField. I tried to use the same kind of instructions lines and modifying them for the field name it seems not working. I cannot have the right URL (Class based ListView) and cannot access to the Class based DetailView....
Thanks in advance for your support.
models.py
from django.db import models
from django.conf import settings
from django.utils import timezone
from django.urls import reverse
# book masterdata
class Book(models.Model):
bk_title = models.CharField("Book Title", max_length=100,
primary_key=True)
bk_originaltitle = models.CharField("Book original Title",
max_length=100, blank=True)
bk_author = models.CharField("Author", max_length=50)
bk_editor = models.CharField("Editor", max_length=50)
bk_editiondate = models.CharField("Book Edition date",
max_length=30)
bk_recblocked = models.BooleanField("Book record blocked")
bk_creator = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.PROTECT,
related_name='+', verbose_name="Created by")
bk_creationdate = models.DateTimeField("Creation date",
auto_now=True)
bk_modifier = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.PROTECT,
related_name='+', verbose_name="Last modification by")
bk_modified = models.DateTimeField("Last modification date",
auto_now=True)
bk_slug = models.SlugField(max_length=100, allow_unicode=True,
blank=True)
def __str__(self):
return self.bk_title
def get_absolute_url(self):
return reverse('book_detail', kwargs={'slug': self.bk_slug})
urls.py
from django.urls import path
from dimlibrary.views import BookListView
from dimlibrary.views import BookDetailView
urlpatterns = [
path('', BookListView.as_view(), name='book_list'),
path('<slug:bk_slug>/', BookDetailView.as_view(),
name='book_detail'),
]
views.py
from django.shortcuts import render
# Create your views here.
from django.shortcuts import render
from django.utils import timezone
from django.views.generic.list import ListView
from django.views.generic.detail import DetailView
from dimlibrary.models import Book
# book views :
# List view
class BookListView(ListView):
model = Book
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
return context
# Details View
class BookDetailView(DetailView):
model = Book
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
return context
```html
{% extends 'base/base.html' %}
{% load static %}
{% block content %}
<h1>Books List</h1>
<ul>
{% for book in object_list %}
<li><a href="{{ dimlibrary.book.get_absolute_url }}">{{
book.bk_title }}</a> - {{ book.bk_author }} -
{{ book.bk_editor }}</li>
{% empty %}
<li>No book registred yet.</li>
{% endfor %}
</ul>
{% endblock content %}
In your DetailView, you need to specify the slug_field [Django-doc] as 'bk_slug':
class BookDetailView(DetailView):
model = Book
slug_field = 'bk_slug'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
return context
In the model, you will need to slugify(…) [Django-doc] the name, so:
from django.utils.text import slugify
class Book(models.Model):
# …
def save(self, *args, **kwargs):
if not self.bk_slug:
self.bk_slug = slugify(self.title)
return super().save(*args, **kwargs)

get_absolute_url: error of Reverse for 'article-detail' not found

I am using class based views to create a post. I have used get_absolute_url to go to the post page after clicking on post but it is giving an error of no reverse match.
this is my modelspy
from django.db import models
from django.conf import settings
from django.urls import reverse
# Create your models here.
class BlogPost(models.Model):
title = models.CharField(max_length = 50 , null=False,blank=False)
body = models.TextField(max_length = 5000 , null=False,blank=False)
date_published = models.DateTimeField(auto_now_add=True)
date_update = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('article-detail', args=(str(self.id)))
this is my urls.py:
urlpatterns = [
path('',views.home,name="home"),
path('home2/',HomeView.as_view(),name = "home2"),
path('article/<int:pk>',ArticleDetailView.as_view(),name = "article-detail"),
path('add_post/',AddPostView.as_view(),name="add_post"),
]
this is home2.html:
<ul>
{%for post in object_list %}
<li>{{post.title}}-{{post.author}}<br/>
{{post.body}}</li>
{%endfor%}
</ul>
this is views.py:
from django.shortcuts import render
from .models import *
from .forms import *
from django.views.generic import ListView,CreateView
from django.views.generic.detail import DetailView
# Create your views here.
def home(request):
return render(request,'post/home.html')
class HomeView(ListView):
model = BlogPost
template_name = "post/home2.html"
class ArticleDetailView(DetailView):
model = BlogPost
template_name = "post/article_details.html"
context_object_name = 'post'
class AddPostView(CreateView):
model = BlogPost
template_name = "post/add_post.html"
fields = '__all__'
I would suggest doing something like. I provide 2 methods.
1st method:
By using get_absolute_url
from django.urls import reverse
def get_absolute_url(self):
return reverse('post:article-detail', args=(self.id)) #post is the app_name
urls.py
url(r'^article/(?P<pk>\d+)/$', ArticleDetailView.as_view(), name='article-detail'),
home2.html
<ul>
{%for post in object_list %}
<li>
{{post.title}}-{{post.author}}<br/>{{post.body}}
</li>
{%endfor%}
</ul>
2nd Method:
By using template tag.Here get absolute url with help of template tag.
home2.html
{% load templatehelpers %}
<ul>
{%for post in object_list %}
<li>
{{post.title}}-{{post.author}}<br/>
{{post.body}}
</li>
{% endfor %}
</ul>
Here we use a new simple tag namely abs_url (absolute url) in templatehelpers.py (Template tag). And app_name is the name of the app.
templatehelpers.py
from django import template
from django.urls import reverse
register = template.Library()
#register.simple_tag
def abs_url(value, request, **kwargs):
return reverse(value,kwargs=kwargs)
If you change this
def get_absolute_url(self):
return reverse('article-detail', args=(str(self.id)))
to this
def get_absolute_url(self):
return reverse('home')
You will not get the error, but you will not either be redirected to the post you just posted.
So if your goal is to just get rid of this error, but don't care about where you get redirected afterwards, then this is the solution for you.

Django modelManager - can not see instances/objects in View/listview - no erroer

I am new to django. I am trying to display with a models.Manager only the published=True instances. In the terminal no error comes. What I am doing wrong? I have a feeling it has something to do with my view.
Any help would be highly appreciated.
models.py
from django.db import models
# Create your models here.
class BlogPostManager(models.Manager):
use_for_related_fields = True
def freetosee(self, **kwargs):
return self.filter(published=True, **kwargs)
class Post(models.Model):
NOT_RATED = 0
RATED_G = 1
RATED_PG = 2
RATED_R = 3
RATINGS =(
(NOT_RATED, 'NR-Not Rated'),
(RATED_G, 'G - General Audience'),
(RATED_PG, 'Parental'),
(RATED_R, 'Restriced'),
)
title = models.CharField(max_length=140)
body = models.TextField()
published = models.BooleanField(default=False)
rating = models.IntegerField(
choices=RATINGS,
default=NOT_RATED,
)
objects = BlogPostManager()
def __str__(self):
return self.title
views.py
from django.shortcuts import render
# Create your views here.
from django.views.generic import DetailView, ListView
from .models import Post
class PostListView(ListView):
model = Post
context_object_name = 'posts'
template_name = 'postlist.html'
template
{% extends "base.html" %}
{% block content %}
{% for post in posts.objects.freetosee %}
{{ post.title }} - {{ post.body }}
{% endfor %}
{% endblock %}
urls.py
from django.urls import path, include
from django.views.generic import TemplateView
from .views import PostListView
app_name = 'blog'
urlpatterns = [
path('', TemplateView.as_view(template_name='home.html'), name='home'),
path('list/', PostListView.as_view(), name='post-list'),
]
I expect to see all models instances in the ListView with published=True
That's not how it works. posts is a queryset, it doesn't have an objects attribute. You need to call that in the view:
class PostListView(ListView):
queryset = Post.objects.freetosee()
context_object_name = 'posts'
template_name = 'postlist.html'
and then in your template just do {% for post in posts %}
Since you're using 2.1
As per django 2.0 deprecation docs use_for_related_fields = True is removed
You'll have to use base_manager_name in model Meta. like this:
class Post(models.Model):
# your fields here
objects = BlogPostManager()
class Meta:
base_manager_name = 'objects'
As suggested above in comments, when you have context_object_name set you don't have to do posts.objects.freetouse
Change your template to:
{% extends "base.html" %}
{% block content %}
{% for post in posts %}
{{ post.title }} - {{ post.body }}
{% endfor %}
{% endblock %}
from docs: the ListView has a get_queryset() method we can override. Previously, it has just been returning the value of the queryset attribute, but now we can add more logic.
This means you can do
class PostListView(ListView):
queryset = Post.objects.freetosee()
context_object_name = 'posts'
template_name = 'postlist.html'
and you can also do:
class PostListView(ListView):
context_object_name = 'posts'
template_name = 'postlist.html'
def get_queryset(self):
# this method can be used to apply as many filters as you want
# Just a quick example,
# filter_id = self.request.GET.get('filter_by')
# if filter_id:
# return Post.objects.filter(id=filter_id)
return Post.objects.freetosee()
NOTE: Please understand Views are there to handle all data and pass it to templates. You make managers to keep your custom queries methods in one place. So it's one place for one kind of thing also your template should not make any query request unless it's super necessary. templates are just to display. If you want filters in templates use template tags. That will keep your code clean and readable.

Model object has no attribute 'get'

using Django 1.11 I'm stuck with uuid referencing in my views.
I read through all similar looking questions here, but they either focus on forms or anything else.
Minimal example
It's an app called MegaTest. It's bound to /testuuid/
I have a working index view that generate links to /testuuid/<uuid>
If I click on a link on my index page, I get:
AttributeError at /testuuid/5a147a14-a9a9-4045-8e79-0ce2e8258a68/
'MegaTest' object has no attribute 'get'
models.py
class MegaTest(models.Model):
uuid = models.UUIDField(db_index=True, default=uuid.uuid4,
editable=False, unique=True)
name = models.CharField(max_length=128, blank=True, null=True)
views.py
class IndexView(generic.ListView):
template_name = 'testuuid/index.html'
context_object_name = 'test_list'
def get_queryset(self):
"""Return the last five published questions."""
return MegaTest.objects.all()
class MegaTestDetailView(generic.DetailView):
model = MegaTest
def get(self, request, uuid):
try:
megatest = MegaTest.objects.get(uuid=uuid)
return megatest
except MegaTest.DoesNotExist:
raise Http404
urls.py
app_name = 'testuuid'
urlpatterns = [
# ex: /polls/
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<uuid>[\w-]+)/$', views.MegaTestDetailView.as_view(), name='detail'),
]
testuuid/index.html
{% if test_list %}
<ul>
{% for test in test_list %}
<li>
{{ test.name }}</li>
{% endfor %}
</ul>
{% else %}
<p>No tests available.</p>
{% endif %}
I assume a damn simple mistake, but I really have no clue at this point.
The complete traceback can be found here http://dpaste.com/3JTB5NZ
You should override get_object() method instead of get().
class MegaTestDetailView(generic.DetailView):
model = MegaTest
def get_object(self):
try:
megatest = MegaTest.objects.get(uuid=self.kwargs['uuid'])
return megatest
except MegaTest.DoesNotExist:
raise Http404
You can also work with get_queryset() or even just specify pk field in url and don't write any method in the view class. Django will automatically query on your specified field in pk_url_kwarg

No ReverseMatch URL django

I have a scenario that looks like this:
Models
from django.db import models
from django.core.urlresolvers import reverse_lazy, reverse
class State(models.Model):
short_name = models.CharField('Abbreviation', max_length=2)
state = models.SlugField('State Name', max_length=20)
def __str__(self):
return self.state
def get_absolute_url(self):
return reverse('state_list', kwargs={'state': self.state})
class City(models.Model):
state = models.ForeignKey(State)
city = models.CharField('City', max_length=100)
class Meta:
verbose_name = 'City'
verbose_name_plural = 'Cities'
def __str__(self):
return self.city
class School(models.Model):
name = models.CharField(max_length=69, default='')
def __str__(self):
return self.name
class Meta:
verbose_name = 'School'
verbose_name_plural = 'Schools'
Views
from django.shortcuts import get_object_or_404, render
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.core.urlresolvers import reverse_lazy
from .models import School, City, State
def reviews_index(request):
state_list = State.objects.all()
context = {'states': state_list}
return render(request, 'reviews/reviews_index.html', context)
def state_detail(request, state=None):
city_list = City.objects.order_by('city')
context = {'cities': city_list}
return render(request, 'reviews/state_detail.html', context)
def city_detail(request, state=None, city=None):
school_list = School.objects.all()
context = {'schools': school_list}
return render(request, 'reviews/city_detail.html', context)
URLs
from django.conf.urls import url, include
from . import views
app_name = 'reviews'
urlpatterns = [
url(r'^$', views.reviews_index, name='reviews_index'),
url(r'^(?P<state>[a-z]+)/$', views.state_detail, name='state_detail'),
url(r'^(?P<state>[a-z]+)/(?P<city>[a-z]+)/$', views.city_detail, name='city_detail'),
]
However, when I try to create a link from the state_detail template to the city_detail template, I get this error:
NoReverseMatch at /reviews/alabama/
Reverse for 'city_detail' with arguments '('', 'Auburn')' and keyword arguments '{}' not found. 0 pattern(s) tried: []
This is how I am linking in the template:
{% block main_content %}
<div class="content">
<div class="row">
{% if cities %}
{% for city in cities %}
<div class="medium-3 column">
{{ city.city }}
</div>
{% endfor %}
{% endif %}
</div>
</div>
{% endblock %}
Can someone please tell me that I am doing wrong and help me fix it. Thanks in advance.
The city_detail url pattern includes the state.state, so you need to include the state in the template context.
In your view, you can use get_object_or_404 to fetch the state using the slug.
def state_detail(request, state=None):
state = get_object_or_404(State, state=state)
city_list = City.objects.filter(state=state).order_by('city')
context = {'cities': `city_list`, 'state': state}
return render(request, 'reviews/state_detail.html', context)
Note that I've changed city_list, so that it only displays cities in the state you are viewing.
It's not a great idea to use the same variable name state for the instance and the slug. It would be a good idea to rename one of them, e.g. to state_obj or state_slug. If you do this, you'll have to make sure you update your urls, views and templates to be consistent.