django generic DetailView - django

New to django and really like the simplicity in getting things done. However having problem rendering a generic DetailView as I get a 405 error stating method not supported. Below is my code.
from django.shortcuts import render, get_object_or_404, get_list_or_404
from django.views.generic import View, ListView, DetailView
from store.managers import StoreManager
from .models import Store
# Create your views here.
class StoreDetails(DetailView):
model = Store
template_name = 'store/details.html'
class StoreIndex(ListView):
model = Store
template_name = 'store/index.html'
context_object_name = 'stores'
# url
urlpatterns = [
url(r'^view/([0-9]+)/$', StoreDetails.as_view(), name='details'),
url(r'^index/$', StoreIndex.as_view(), name='index'),
]
While my StoreIndex view works perfectly, I get an error for my StoreDetails view. Tried overriding get_context_data function but same result.

The problem is in the url pattern. The DetailView needs the primary key to find the right object to display, but the pattern r'^view/([0-9]+)/$' does not specify that the matching number should be used as the primary key. Try r'^view/(?P<pk>[0-9]+)/$' (pk stands for primary key).
Also see the example at DetailView doocs (which provides slug instead of pk). Custom get_context_data should not be neede for pk and slug.

Related

Image response from DetailView in Django

I'd like to have a DetailView respond with an image file that is saved to the model/object.
I have seen this related question but what I am doing is a little different.
I have the following model/views/urls - this allows me to display the image in an img tag inside a template when accessing the url localhost/MY-SKU-FIELD. I'd rather not use a template and just respond with the image directly.
# models.py
from django.db import models
class SignModel(models.Model):
sku = models.CharField('SKU', max_length=50)
sign_image = models.ImageField(upload_to='images/sign')
# views.py
from django.views.generic import DetailView
from signs.models import SignModel
class SignDetailView(DetailView):
model = SignModel
template_name = 'signs/sign_detail.html'
slug_url_kwarg = 'sku'
slug_field = 'sku'
# urls.py
from django.urls import path
from signs.views import SignDetailView
urlpatterns = [
path('<slug:sku>', SignDetailView.as_view(), name='sign-detail'),
]
{# 'signs/templates/sign_detail.html #}
<img src="{{object.sign_image.url}}" alt="">
Based on the related question/answer, I figure I need to define my own get() method, then fetch the image, and then respond accordingly, but I'm not quite sure how to fetch the image based on the object fetched in DetailView. I've tried fetching with r = requests.get(self.sign_image.url) but that is clearly not working.
How do I get the image and respond correctly?
I haven't done this before but I was able to quickly check, and you can give the value of the field to a django.http.FileResponse object. As for the view, I think you want to overwrite the render_to_response method.
from django.http import FileResponse
class SignDetailView(DetailView):
model = SignModel
template_name = 'signs/sign_detail.html'
slug_url_kwarg = 'sku'
slug_field = 'sku'
def render_to_response(self, context, **kwargs):
return FileResponse(self.object.sign_image)

How to redirect url for CreateView in views.py

class A_modelCreateView(CreateView)in views.py
path('create/'),view.A_modelCreateView().as_view(),name='create' in urls.py
build an HTML as form for A_model form but not in its default directory(my_app/A_model.form)
Is there filled parameter somewhere for redirect the url you want?
Yes, a CreateView has as one of its baseclasses a the FormMixin. This FormMixin class has a success_url attribute [Django-doc].
You can add a real url, or work with reverse_lazy [Django-doc] to calculate the URL based on the name of a view. For example:
from django.urls import reverse_lazy
from django.views.generic.edit import CreateView
class A_modelCreateView(CreateView):
success_url = reverse_lazy('overview_page')
# ...
where overview_page is here the name of a hypothetical view.
If you do not specify a success_url in a CreateView or UpdateView, it will take the get_absolute_url of the model object that is created/updated, if the model has such method. For more information, see the Django documentation.

Django Rest Framework - CreateAPIView doesn't let use POST method

I try to create a view, that will accept POST requests and create new instances of my model(see bottom of post). I follow this tutorial. The problem is that when I access URL associated with view, which inherits from CreateAPIView I dont see a form in html representation of API for creation of new instances and I also see that it accepts GET requests, not POST as it mentioned in documentation.
Page looks like this
My views.py
from django.shortcuts import render
from rest_framework.generics import ListAPIView, CreateAPIView
from datingapp.models import Profile
from .serializers import ProfileSerializer, ProfileCreateSerializer
class ProfilesAPIView(ListAPIView):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
class ProfileCreateAPIView(CreateAPIView):
queryset = Profile.objects.all()
serializer_class = ProfileCreateSerializer
My urls.py
from django.conf.urls import url
from django.contrib import admin
from datingapp.views import ProfilesAPIView, ProfileCreateAPIView
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'api/profiles/', ProfilesAPIView.as_view(), name='list'),
url(r'api/profiles/create/$', ProfileCreateAPIView.as_view(), name='create')
]
My serializers.py
from rest_framework.serializers import ModelSerializer
from datingapp.models import Profile
class ProfileSerializer(ModelSerializer):
class Meta:
model = Profile
fields = [
'name',
'age',
'heigth'
'location',
]
class ProfileCreateSerializer(ModelSerializer):
class Meta:
model = Profile
fields = [
'name',
'age',
'heigth'
'location',
]
In my settings.py I have crispy_forms installed.
What am I doing wrong ?
UPD: here is what I want to achieve
As you see there is a form and it accepts only POST and also says that GET is not allowed
The problem is in your router. The first pattern matches both api/profiles/ and api/profiles/create/ so the second one will never be evaluated. You are seeing the ProfilesAPIView instead of the create view.
url(r'api/profiles/', ProfilesAPIView.as_view(), name='list'),
url(r'api/profiles/create/$', ProfileCreateAPIView.as_view(), name='create')
To fix it, either swap the order of the urls, or add a $ to the end of the first pattern. r'api/profiles/$'
I was following a tutorial and had a similar problem. Probably I was not following the same version of Django Rest Framework and they had changes.
But I solved this problem doing this.
class AssetBundleList(generics.ListAPIView):
to
class AssetBundleList(generics.ListCreateAPIView):
Hope this helps someone.

Django view doesn't display model value - Django 1.5

I created a very simple example code using Django, but cannot get model value to be displayed on my page:
----------------------------- home/models.py
from django.db import models
class Home(models.Model):
msg = models.CharField(max_length=100)
#classmethod
def create(cls, msg):
home = cls(msg=msg)
# do something with the book
return home
home = Home.create("Hello World!")
------------------------------------home/views.py
from django.views.generic import TemplateView
from project.models import Home
class IndexView(TemplateView):
model = Home
template_name = 'home/index.html'
------------------------------------------ templates/home/index.html
{{ home.msg }}
this is a test page. I am expecting to see this....
------------------------------------------- urls.py
from django.conf.urls.defaults import patterns, include, url
from django.contrib import admin
from django.views.generic import TemplateView
admin.autodiscover()
urlpatterns = patterns('',
# Home pagetentacl.urls
url(r'^$', TemplateView.as_view(template_name='home/index.html')),
# Uncomment the admin/doc line below to enable admin documentation:
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment the next line to enable the admin:
url(r'^admin/', include(admin.site.urls)),
)
-------------------------------------- result page on browser:
this is a test page. I am expecting to see this....
I don't want to have DB access for my example. I want my model returns "hello world" string. home.msg on index.html doesn't return anything. What is missing here?
You're not giving the template an instance of Home. You need to create one and pass it to the template as a Context, in the form {'msg': msg}.
EDIT: Adding some code
First of all, you should create your instance of home in your view. I've never used TemplateViews, so I'm going to use a regular view method instead.
def IndexView(request):
home=Home.create("Hello World!")
return render(request, 'index.html', {'home': home},)
As #Daniel rightly points out, you're not giving your template an instance of Home to work with.
If you want to use class-based views, subclass TemplateView and override get_context_data():
class IndexView(TemplateView):
template_name = "home/index.html"
def get_context_data(self, **kwargs):
context = super(HomePageView, self).get_context_data(**kwargs)
context["home"] = Home.create("Hello World!")
return context
And make sure your urls.py is using IndexView.as_view() - your version above is just referencing the generic TemplateView.
The fact that you added a model field to your subclass of TemplateView makes me think you're confusing it with DetailView. See the documentation for the difference.

Writing a Django detail view with a generic class based view

I know this is really simple, but I'm missing something. And because I can't ever remember this, I'm hoping this can document a solution here.
All I want to do is pass a PK for the object in the URL and get the detail view back.
Url:
url(regex=r'^(?P<pk>\d+)/$',
view=AdventureDetail.as_view(),
name='adventure_detail',
),
View:
class AdventureDetail(DetailView):
""" Get a time entry detail view """
template_name = "adventure/AdventureDetail.html"
def get_object(self):
return get_object_or_404(Page)
But I'm getting a "multiple objects returned error"
MultipleObjectsReturned at /1/
get() returned more than one Page -- it returned 5! Lookup parameters were {}
This feels really silly. It should "just work" but I'm missing something obvious.
Thanks for the help.
In DetailView it is more simpler: you can just specify the model:
class AdventureDetail(DetailView):
""" Get a time entry detail view """
model = Page
template_name = "adventure/AdventureDetail.html"
And that's all. DetailView will do the rest of work.
Another way is to specify queryset:
class AdventureDetail(DetailView):
""" Get a time entry detail view """
queryset = Page.objects.all()
template_name = "adventure/AdventureDetail.html"
This will have the same result.
And the last way is to override the get_object method.
Look here for details
You're not passing any other parameters to get_object_or_404, other than the Page class. So now you're basically querying for all pages. So you'd need to do:
return get_object_or_404(Page, pk=self.kwargs.get('pk', None))
Also, why are you overriding get_object? The DetailView already contains this functionality so all you need to do is have a URL with pk in it.
In views.py
from django.views.generic import DetailView
# Import your model that you want to use in details view for example
from .models import Post
class PostDetailView(DetailView):
model = Post
Then create a template with the following name conversion
<appname>/<model_viewtype>.html
In urls.py
First import the class view that you created. In our case it is
from .views import PostDetailView
Then
path("post/<int:pk>/", views.PostDetailView.as_view(), name="PostDetailView")