Error Instantiating View Classes in Django - python

I'm new to Django and have used generic views for all my projects so far. I've called them in the url.py code with
patterns(r'^url$',DetailView.as_view())
However, now I'm trying to make my own class based views and am having trouble at it. I tried making a simple test where I call a model.py function from my view, but I get an error that I haven't instantiated an instance of the class. How should I instantiate the view class? Also, how come I don't get the same error when calling DetailView.as_view() the DetailView class isn't instantiated either, right?
My code:
models.py
class Post(models.Model):
title = models.CharField(max_length=140)
body = models.TextField()
def __unicode__(self):
return self.title
def getBody(self):
return self.body
view.py
class RatingView(generic.DetailView):
model = Post
template_name = "like.html"
def __init__(self):
model = Post
template_name = "like.html"
def modelFuncTest(self):
return HttpResponse(self.model.getBody())
url.py
from django.conf.urls import patterns, include, url
from django.views.generic import ListView, DetailView
from blog.views import RatingView
from blog.models import Post
urlpatterns = patterns('',
url(r'^(?P<pk>\d+)/like/$', RatingView.modelFuncTest()),
)

Change your views.py to be just:
class RatingView(generic.DetailView):
model = Post
template_name = "like.html"
and change urls.py to be:
from django.conf.urls import patterns, url
from blog.views import RatingView
urlpatterns = patterns('',
url(r'^(?P<pk>\d+)/like/$', RatingView.as_view()),
)
and that should get you started, let me know if there are errors after making these changes.
There are some good examples in the django docs if you haven't seen them yet.
EDIT: Also, in the template, you should get your body like this:
{{ post.body }}
The beauty of the Class-based views is that the context of your object gets passed for you.

You still need to call the as_view() function on your class in your urlpatterns:
urlpatterns = patterns('',
url(r'^(?P<pk>\d+)/like/$', RatingView.as_view()),
)
As for this:
Also, how come I don't get the same error when calling DetailView.as_view() the DetailView class isn't instantiated either, right?
The as_view() function is a class method, which returns an instance of the view class that can be called when your URL pattern is hit.

Related

What can I do to make 'id' = id in this class-based view in Django?

views.py
from django.shortcuts import render
from django.views.generic import DetailView
from .models import Producto
def general_view(request):
context = {
'articulos': Producto.objects.all(),
}
return render(request, 'shop/general_view.html', context)
class DetailedView(DetailView):
model = Producto
template_name = 'shop/specific_view.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['determinado'] = Producto.objects.get(pk=*¿?*)
return context
urls.py
from django.urls import path
from . import views
from .views import DetailedView
urlpatterns = [
path('', views.general_view, name='general-view'),
path('<int:pk>/', DetailedView.as_view(), name='specific-article'),
]
As you see, the problem occurs because I don't know how to call the id or pk in the detailed_view in views.py, I'm guessing you maybe have to do a dictionary but I don't know where to do it nor how. It works if I set the id to 1, but obviously what this does is that is shows in every url with a different id the same article.
You do not need to do that yourself. The idea is that the DetailView has the boilerplate code, to automatically filter on the primary key. You can simply set the .context_object_name attribute [Django-doc] to 'determinado':
class DetailedView(DetailView):
model = Producto
template_name = 'shop/specific_view.html'
context_object_name = 'determinado'
# no override of get_context_data

Django class based view: passing additional information to the next view

I'm very new to Django and a bit overwhelmed by the documentation. I think my problem is pretty simple but everything i've found just confused me more.
I am building a little news app with a model NewsItem:
from django.db import models
from django.utils import timezone
# Create your models here.
class NewsItem(models.Model):
title = models.CharField(max_length=50)
newsText = models.TextField()
dateEntered = models.DateTimeField('date entered')
datePublished = models.DateTimeField('date published', blank=True, null=True)
user = models.CharField(max_length=30) #temporary field. will be changed to user foreign key
def __str__(self):
return self.title
def publish(self):
if (self.datePublished == None):
self.datePublished = timezone.now()
def published(self):
return self.datePublished != None
two views (technically 3) index and detail
from django.http import HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.urls import reverse
from django.views import generic
from .models import NewsItem
# Create your views here.
class IndexView(generic.ListView):
template_name = 'news/index.html'
context_object_name = 'latestNewsList'
def get_queryset(self):
return NewsItem.objects.order_by('-datePublished')[:5]
#todo
class DetailView(generic.DetailView):
model = NewsItem
template_name = 'news/detail.html'
def publish(request, itemId):
newsItem = get_object_or_404(NewsItem, pk=itemId)
newsItem.publish()
newsItem.save()
return HttpResponseRedirect(reverse('news:detail', args=(newsItem.id,)))
and an urlconf like this
from django.urls import path
from . import views
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('<int:itemId>/publish', views.publish, name='publish'),
]
In the detail view i have a link Publish which just triggers the function views.publish. This view is supposed to redirect back to the detail view.
What i'm trying to do now is to display a little message (like article successfully published) in detail view when it was redirected by the publish view. But i have no idea what would be a good aproach
I could just render the details template in the publish view, but then it would still say news/publish in the URL instead of news/detail
Thanks in advance for your help
Have a look at the messages framework. You could add a success message before redirecting, which will be displayed on the next page.
from django.shortcuts import redirect
from django.contrib import messages
def publish(request, itemId):
newsItem = get_object_or_404(NewsItem, pk=itemId)
newsItem.publish()
newsItem.save()
messages.success(request, "The post has been published")
return redirect('news:detail', newsItem.id)
Note that I've simplified the return statement to use redirect(...) instead of HttpResponseRedirect(reverse(...)).

Page not found 404 No Blog matches the given query

Page not found (404) Request Method: GET Request URL:
http://127.0.0.1:8000/like/?csrfmiddlewaretoken=UJ6I7mm2cjjSXK0MeuOLqm4E7OfMKTKtO461mCAsnTPdXT0UVw1z3JfMqijyIJAM&blog_id=
Raised by: blog.views.like_post
No Blog matches the given query.
I was making a like section for my blog app and this error is displayed below are my views, models and urls files
views.py
from django.shortcuts import render,get_object_or_404
from django.views.generic import ListView
from .models import Blog
class BlogsList(ListView):
model=Blog
template_name='blog/home.html'
context_object_name='blogs'
ordering=['-date_posted']
def like_post(request):
post= get_object_or_404(Blog, id=request.POST.get('blog_id'))
post.likes.add(request.user)
return HttpResponseRedirect(Blog.get_absolute_url())
models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
class Blog(models.Model):
title=models.CharField(max_length=100)
content=models.TextField()
date_posted=models.DateTimeField(default=timezone.now)
author=models.ForeignKey(User, on_delete=models.CASCADE)
likes=models.ManyToManyField(User,related_name='likes',blank=True)
def __str__(self):
return self.title
urls.py
from django.urls import path
from . import views
from django.conf.urls import url
urlpatterns=[
path('',views.BlogsList.as_view(),name='blog-home'),
url(r'^like/$', views.like_post, name='like_post')
]
Your view function like_post is the culprit here.
The like_post should be called with a POST method and passed a request parameter blog_id. I don't see where do you do this.
I suggest you to rewrite the view function:
def like_post(request, blog_id):
post = get_object_or_404(Blog, id=blog_id)
# the rest can stay unchanged
and in the urls.py change the following line:
url(r'^like/$', views.like_post, name='like_post')
to:
path('<int:blog_id>/like/', views.like_post, name='like_post')
Mixing the new path and the old url doesn't look very nice to me. Now you can pass the blog_id in the URL and the view will take care to return 404 NOT FOUND if a blog doesn't exist.

Django REST - Can't access my edit page for my API

So I have a code that lists all my posts in the API and it is doing what it is supposed to be doing. However, the code to edit the details of every posts wasn't working. What I am trying to do is that if I access the url for the post that I want to edit, I should be able to. But for some reason I can't. I thought I was doing the right thing.
I was trying to access the posts by their ids through the urls. But it only lists all my posts instead of the one with the matching post_id or primary key.
My codes are down below.
views.py
# To retrieve and list all posts
class ListPosts(generics.ListCreateAPIView):
queryset = Posts.objects.all()
serializer_class = PostsSerializer
# To view the details of the listed posts
class DetailPosts(generics.RetrieveUpdateDestroyAPIView):
queryset = Posts.objects.all()
serializer_class = PostsSerializer
urls.py
from django.conf.urls import url, include
from . import views
urlpatterns = [
url(r'^', views.ListPosts.as_view(), name="list_posts"),
url(r'^(?P<post_id>\d+)/$',views.DetailPosts.as_view(), name="detail_posts"),
]
For DetailPosts view works, you must specify a lookup_field with the same name of your named parameter configured in urls.py. Be sure that this field is a Posts field which defines it uniquely
class DetailPosts(generics.RetrieveUpdateDestroyAPIView):
lookup_field = 'post_id'
queryset = Posts.objects.all()
serializer_class = PostsSerializer
Use Viewsets and Routers
I also detect that your views an urls can be encapsulated by django rest framewok tools like viewsets and routers since your views define a basic CRUD of Posts
views.py
from rest_framework import viewsets
class PostsViewSet(viewsets.ModelViewSet):
queryset = Posts.objects.all()
serializer_class = PostsSerializer
routers.py
from rest_framework import routers
from .views import PostsViewSet
router = routers.SimpleRouter()
router.register(r'posts', PostsViewSet)
urlpatterns = router.urls

What Form should I create to display these models?

I'm brand new to Django and I'm trying to create a simple web, so sorry if this seems quite trivial or simple. Currently I have these models:
class Fighter(models.Model):
name = models.CharField(max_length=50)
rating = models.DecimalField(default=1600, max_digits=8, decimal_places=2)
url = models.URLField(default='')
class Fight(models.Model):
member1 = models.ForeignKey(Fighter, related_name='fighter_1')
member2 = models.ForeignKey(Fighter, related_name='fighter_2')
I want to display a random fight and it's associated members every time I refresh the page, and then have a radio button (or something similar) that allows a user to choose who they think would win.
I think I need to use forms to do this, but after reading lots of Django documentation I have no idea about how to do this. Could someone please explain the best way for me to go about this!
I have been using class based views, is this the correct thing to do? If I haven't included enough code then please let me know.
To keep track of which fight instance is being referred to, I have used the URL to mention the fight id. A new fight instance will be created each time the user visits the (home) page. Then the view will redirect to the URL for that fight's form.
urls.py
from django.conf.urls import patterns, include, url
from fights.views import FightView, random_fight
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^$', random_fight, name='home'),
url(r'^fight/(?P<fight_id>\d+)/$', FightView.as_view(), name='fight_view'),
url(r'^admin/', include(admin.site.urls)),
)
forms.py
The form will be initialized with a dynamically created radio box with the names of fighters passed as a keyword argument.
from django import forms
from .models import Fighter
class FighterSelectForm(forms.Form):
def __init__(self, *args, **kwargs):
choices = kwargs.pop("choices", [])
super(FighterSelectForm, self).__init__(*args, **kwargs)
self.fields["fighter_choices"] = forms.ChoiceField(
widget=forms.RadioSelect,
choices=choices)
views.py
random_fight() creates a new Fight instance with two random Fighter members and redirects to the corresponding fight_view. FightView class overrides the get_form() function to pass the fighter names from the Fight object, which is in turn derived from the URL.
from django.shortcuts import render
from .models import Fight, Fighter
from .forms import FighterSelectForm
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.views.generic.edit import FormView
def random_fight(request):
fighters = Fighter.objects.order_by("?")[:2]
fight = Fight.objects.create(member1=fighters[0], member2=fighters[1])
return HttpResponseRedirect(reverse('fight_view', kwargs={"fight_id": str(fight.id)}))
class FightView(FormView):
template_name = 'fight.html'
success_url = '/thanks/' # Not defined here
def get_form(self, form_class):
fight = Fight.objects.get(id=self.kwargs.get("fight_id"))
choices = [(fight.member1.id, fight.member1.name),
(fight.member2.id, fight.member2.name)]
kwargs = super(FightView, self).get_form_kwargs()
kwargs.update({"choices": choices})
form = FighterSelectForm(**kwargs)
return form
def form_valid(self, form):
selected = Fighter.objects.get(id=form.cleaned_data['fighter_choices'])
selected.rating += 100 # Use any logic to change rating here
selected.save()
print("Fighter selected: {}".format(selected.name))
return super(FightView, self).form_valid(form)
fight.html
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>

Categories

Resources