Django
Following the official documentation, I am creating a Django app (the same poll app as in the documentation page). While using the class-based view, I got a error. I did not understand much about class based view, for instance, may someone explain, what is the difference between a class based view from a normal view?
Here's my code:
class DetailView(generic.DetailView):
model = Poll
template_name = 'polls/details.html'
def get_queryset(self):
def detail(request, poll_id):
try:
poll = Poll.objects.get(pk=poll_id)
except Poll.DoesNotExist:
raise Http404
return render(request, 'polls/details.html', {'poll': poll})
*********************Error ********************
TypeError at /polls/2/results/
as_view() takes exactly 1 argument (3 given)
Request Method: GET
Request URL: <app-path>/polls/2/results/
Django Version: 1.5.1
Exception Type: TypeError
Exception Value:
as_view() takes exactly 1 argument (3 given)
*****the url***
url(r'^(?P<pk>\d+)/$', views.DetailView.as_view, name='detail')
as_view should be called, not referenced, according to the docs, your url should look like:
url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail')
Note the usage of parenthesis.
Also, you should rather call your class PollDetailView to avoid confusion for code readers.
Also, the detail() method you have defined will not be called at all. So you shouldn't define it at all. Also, leave the get_queryset() method alone for the moment, try to get the basic View to work first.
When you use url for the CBV ensure that you can not use the reference of the of the url filed, so first you need to add change the as_view to as_view().
And you can use DetailView like this.,
class PollDetail(DetailView):
model=Book
def get_context_data(self,*args,**kwargs):
context=super(PollDetail,self).get_context_data(*args,**kwargs)
print(context) #It will give you the data in your terminal
return context
And for accessing data you need to use {{object}},
and if you want to access other fields at that time use like this {{object.fieldname}}
In CBV the template name is automatically named as per class name so you don't need to give it.
Note:Don't give class name same as DetailView,In future you will confuse.
Since you are not modifying the view functionality, to use this generic view, you can simply do this:
in your urls.py (along with other stuff):
from django.views.generic.detail import DetailView
from poll.models import Poll
url(r'^(?P<pk>\d+)/$',
DetailView.as_view(model=Poll,
template_name='polls/details.html'), name='detail')
Then in poll/details.html, you just need:
{{ object }}
Related
I have several function based views in a django project, and I noticed that they have some repeated code, in fact, they all do the same thing:
def my_view(request):
form = MyForm(request.POST or None)
if requqest.POST:
if form.is_valid():
do_somethin()
and_something_else()
return redirect('another:page')
return render(request, 'my/tempalate.html', {'form': form})
The only thing that is different is the url where the user is redirected to in case of a succcesful form validation, the template, and maybe the form in the future.
Is it a good idea to use something like this to avoid that repetition?:
def a_view(request, success_redirect_url, template):
form = MyForm(request.POST or None)
if request.POST:
if form.is_valid():
do_something()
and_something_else()
return redirect(success_redirect_url)
return render(request, template, {'form': form})
and then reuse it in other views that have the repeated code? like:
def my_view1(request, url='another_page', template='my/template.html'):
return a_view(request, url, template)
Typically such patterns are defined in class-based views where one can use inheritance to override only some parts of the code flow.
Class-based views
Your view for example looks quite similar to a FormView [Django-doc]. Indeed, we can define a view with:
# app_name/views.py
from django.urls import reverse_lazy
from django.views.generic import FormView
class MyView(FormView):
template_name = 'my/template.html'
success_url = reverse_lazy('another_page')
form_class = MyForm
def form_valid(self, form):
do_somethin()
and_something_else()
return super().form_valid(form)
Here the class-based view has implemented a handler for a POST request that will first construct the form, then check if that form is valid, and if it is invalid rerender the template with the form that now contains the errors. It thus minimizes the amount of code that one has to write to handle a simple form.
Such views also explain clearly what they are doing. A CreateView for example explains by its name that it is used to create new objects, it will thus trigger the .save() method of the form it is operating on. A DeleteView on the other hand makes it clear that a POST request to that view will delete an object.
You can also override the attributes in the urls.py by specifying a value in the .as_view(…) [Django-doc] call:
# app_name/urls.py
from app_name.views import MyView
from django.urls import path
urlpatterns = [
# ⋮,
path('some/path/', MyView(template_name='other/template.html'), name='some-name'),
# ⋮
]
A class-based function thus acts as a template method pattern
Decorators
Another way to do this is to work with decorators that implement small pieces of logic that run before/after a call to the function-based view, and can alter the request/response, or decide to raise an error instead of calling the view.
For example the #require_http_methods(…) decorator [Django-doc] [GitHub] is implemented to first check if the method is one of the listed ones. This is implemented as:
def require_http_methods(request_method_list):
# ⋮
def decorator(func):
#wraps(func)
def inner(request, *args, **kwargs):
if request.method not in request_method_list:
response = HttpResponseNotAllowed(request_method_list)
log_response(
'Method Not Allowed (%s): %s', request.method, request.path,
response=response,
request=request,
)
return response
return func(request, *args, **kwargs)
return inner
return decorator
here the decorator thus checks if the request.method is a member of the request_method_list. If that is not the case, it will return a HTTP 405 response, and specify that that method is not allowed.
While Django offers a lot of decorators, most decorators have a mixin counterpart for a class-based views, and some are implemented already in the View class. For example if the View does not contains a get method, then it will return a HTTP 405 response, so here the required_http_method is not needed as a mixin/decorator for a class-based view.
Skeleton functions
You can implement a view function and use parameters instead to pass values. Usually however this will not be as flexible as a class-based view: it is rather easy to pass some parameters, but it is less useful to specify behavior in a pattern: in that case you need to pass a reference to a function, but then the question arises what parameters should be passed to that.
For example if we want to make a function that renders the template, we can work with a view that looks like:
from django.shortcuts import render
def render_some_template(request, parameter, context_generator, template='our/template.html'):
context = context_generator()
return render(request, template, context)
but perhaps the context_generator function should be called together with the parameter? or perhaps know what template will be rendered. This is one of the reasons why altering code flow is usually done with a class-based view, and less with a function-based view.
While a function-based view can work with a skeleton function, it is usually less exendible than the class-based counterpart. Django's builtin apps (the ones defined in the django.contrib module) are moving mainly towards class-based views, since it is easier to extend these.
You can do a lot better if just give a name to any button wich lunch this function:
<button name='lukaku'>
<button name='ronaldo'>
then into the view check for the name
if form.get('lukaku'):
do something
if form.get('ronaldo'):
do other thing
and so on. this can be limitless
I was working on an application wherein I created a generic ListView. Now, while defining that view in my urls.py, I read from the documentation that I need to use the as_view() method as follows:
from django.conf.urls import patterns, include, url
from .views import BlogIndex
urlpatterns = patterns(
'',
url(r'^$', BlogIndex.as_view(), name="index"),
)
Now, I didn't really understood what the documentation had to say about this method. Can someone shed some light into this concept?
In Class-based views, you have to call as_view() function so as to return a callable view that takes a request and returns a response. Its the main entry-point in request-response cycle in case of generic views.
as_view is the function(class method) which will connect my MyView class with its url.
From django docs:
classmethod as_view(**initkwargs)
Returns a callable view that takes a request and returns a response:
You just can't use class-based views like you could in normal function-based views.
BlogIndex(request) # can't do this in case of CBVs
The above code is not valid if you want the CBVs to function properly. For that, you need to provide a view which is callable and then pass request to it. For example:
response = MyView.as_view()(request) # valid way
By calling the as_view() function on my view class MyView will give me a view which i will call with request parameter to initiate the request-response cycle.
In your case:
my_callable_view = BlogIndex.as_view() # returns a callable view
<function blog.views.BlogIndex>
Now, call this function and pass the request.
response = my_callable_view(request) # generate proper response
view function have different format than before because :
This view will actually be implemented as a class
We will be inheriting from an existing generic view function that already does most of what we want this view function to do, rather
than writing our own from scratch.
Class method as_view()- this does all the work of creating an instance of the class, and making sure that the right handler methods
are called for incoming HTTP requests.
ref : https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Generic_views
Maybe I can try to write the as_view with pseudo-code:
class ViewClass():
#other stuff
def as_view(self):
return self.render(*args)
It return the render function inside the CBV.
So it actually is the same as path('some_path',views.some_view_function,name='some_name')
Of course, there there is actually much more things going on than only render function, for example to verify and save the content inside post queryDict, actually you need def post(): to handle the post, in function you just if request.method == 'POST' they are actually mutual.
To be specific, as_view() just generate a overall function, including if request.method =='POST': #some code Maybe the actual code doesn't work like that, but you could understand it this way if you are not prepared to contribute to django source code yourself.
This is simplified code example, but may be someone knows the reason the problem occurs without the exact copy of the code. So:
class FooView(TemplateView):
template_name = 'foo.html'
def get_context_data(self, **kwargs)
context = super(FooView, self).get_context_data(**kwargs)
...
# here we get link to domain
...
args = {'domain': domain}
context.update(args)
import pdb; pdb.set_trace() # here 'domain' has a value
return context
The html template foo.html tries to access the 'domain' value with {{ domain }}, but it is None.
This happens only if I use 'domain' as the label. So if I change domain to domain1 or foo - it works.
How is this possible? Is it possible domain name gots overwritten somehow, maybe some has a clue?
EDIT Change get_context_view --> get_context_data (mistype)
EDIT2 My 'domain' context variable gets ovewritten by context_processor. All worked fine when function-based views were used. After switching to class-based views - the 'domain' started to get overwritten. May be get_context_data() is not strong enough? Is there any way to prevent context processor from overwriting the variable?
Yes, it is possible if some or template tags of context processors injects domain variable into context.
Are you sure than method is called get_context_view? Imho it should be get_context_data.
The TemplateView returns a TemplateResponse which is known to overwrite the context passed to it with data from context processors. This is something that's changing in Django 1.8 to be more consistent with render.
You should be able to fix this by overwriting render_to_response on your view with something like:
from django.shortcuts import render
...
class FooView(TemplateView):
def render_to_response(self, context, **response_kwargs):
return render(self.request, self.get_template_names()[0], context)
When I submit a certain CreateView, I want to move on to create another object in another CreateView. However, when I try
def get_success_url(self):
return FooView.as_view()(self.request, itempk = self.object.id)
I get
ContentNotRenderedError: The response content must be rendered before
it can be accessed.
I also tried
def get_success_url(self):
return render(FooView.as_view()(self.request, itempk = self.object.id))
which gives me
AttributeError: 'TemplateResponse' object has no attribute 'META'
I'm fairly certain I'm just going about this the wrong way, and that I've done it correctly before, but I'm stumped. What is the proper way to do this?
You don't want to call the view, you want to redirect the user to it. So just use the redirect function:
from django.shortcuts import redirect
...
return redirect('foo_view_name', kwargs={'itempk': self.object.id})
Since you're defining the get_success_url, I would say that you just need something like
def get_success_url(self):
# assuming that your FooView urlconf was named "foo_view"
return reverse('foo_view', kwargs={'itempk':self.object.id})
Cf. https://docs.djangoproject.com/en/dev/ref/urlresolvers/
I'd like to pass a success_url to the class-based ActivationView in Django Registration like this answer covers for function-based views and this answer covers for RegistrationView. What I have tried so far that has been unsuccessful:
url(r'^activate/(?P<activation_key>\w+)/$',
ActivationView.as_view({'success_url':'/activation_routing'}),
name='registration_activate',
),
returns "TypeError: as_view() takes exactly 1 argument (2 given)" I have also tried
and:
url(r'^activate/(?P<activation_key>\w+)/$',
ActivationView.as_view(success_url='/activation_routing'),
name='registration_activate',
),
returns "TypeError: ActivationView() received an invalid keyword 'success_url'. as_view only accepts arguments that are already attributes of the class."
I feel like I'm missing something with class-based views, or is subclassing ActivationView and putting in custom logic my best bet?
You can indeed only pass existing attributes to as_view(). As such, and looking at the source of django-registration, the view doesn't have a success_url attribute but it obtains its value by calling self.get_success_url(...).
By default this method is not implemented so you have little choice besides subclassing ActivationView and implementing get_success_url yourself.
I think you have to subclass the view and override the get_success_url method.
I opened pull request 57 to enable setting success_url as a class attribute, but it has not been merged yet.
As others confirmed, I was able to resolve this by subclassing ActivationView and overriding the get_success_url() and activate() methods:
"""
views.py
"""
from registration.views import ActivationView
class CustomActivation(ActivationView):
def activate(self, request, *args, **kwargs):
return True
def get_success_url(self, request, user):
success_url = # insert success URL here
return success_url
It's also important to set the correct URL in your urls.py file to override the default ActivationView that will be called by django-registration. One quirk to remember is that django-registration will set its URLs according to the filepath of auth.urls and not what you specify in your app's urls.py file:
"""
urls.py
"""
from yourapp.views import CustomActivation
urlpatterns += patterns('',
url(r'^user_settings/', include('auth.urls')),
url(r'^user_settings/activate/(?P<activation_key>\w+)/$',
CustomActivation.as_view(),
name='registration_activate',
),
# will still set registration URLs under user_settings!
url(r'^accounts/', include('registration.backends.default.urls')),
)