How to create an instance created from class-based views - django

I am learning Django's class-based views.
I don't understand what's the instance created from class Views
For example:
from django.http import HttpResponse
from django.views import View
# views.py
class MyView(View):
def get(self, request):
# <view logic>
return HttpResponse('result')
# urls.py
from django.conf.urls import url
from myapp.views import MyView
urlpatterns = [
url(r'^about/$', MyView.as_view()),
]
What's the instance of class 'MyView' during this request-and-respond process?

Although your question is not very clear, I think that I get what you are asking:
Take a look at the source code of url() method:
def url(regex, view, kwargs=None, name=None):
if isinstance(view, (list, tuple)):
# For include(...) processing.
urlconf_module, app_name, namespace = view
return RegexURLResolver(
regex, urlconf_module, kwargs,
app_name=app_name, namespace=namespace
)
...
And a look at the as_view() method documentation:
Returns a callable view that takes a request and returns a response:
response = MyView.as_view()(request)
The returned view has view_class and view_initkwargs attributes.
When the view is called during the request/response cycle, the HttpRequest is assigned to the view’s request attribute.
Therefore because you are passing in the url method the MyView.as_view() argument you are giving the instruction to instantiate the MyView class when a request object gets passed to the url() method.

Related

django-allauth login_redirect page with username as slug

I am using Django 3.2 and django-allauth 0.44
I have set my LOGIN_REDIRECT_URL in settings.py as follows:
LOGIN_REDIRECT_URL = 'profile-page'
in urls.py, I have the following route defined:
path('accounts/profile/slug:username', AccountProfileView.as_view(), name='profile-page'),
When I log in, (unsurprisingly), I get the following error message:
NoReverseMatch at /accounts/login/
Reverse for 'profile-page' with no arguments not found. 1 pattern(s) tried: ['accounts/profile/(?P[-a-zA-Z0-9_]+)$']
How do I pass (or specify) a parameter of the logged in user's username to the route?
If your view needs to perform redirects which are not very simple, you need to override the get_success_url method, considering that you use django-allauth, you will need to override allauth.account.views.LoginView and also write your own url pattern for it so that your overriden view is used. First override the view:
from django.urls import reverse
from allauth.account.views import LoginView as AllauthLoginView
from allauth.account.utils import get_next_redirect_url
class LoginView(AllauthLoginView):
def form_valid(self, form):
self.user = form.user # Get the forms user
return super().form_valid(form)
def get_success_url(self):
ret = (
get_next_redirect_url(self.request, self.redirect_field_name)
or reverse('profile-page', kwargs={'username': self.user.username})
)
return ret
Next wherever you define the urls for allauth, just add your own url before it:
from path_to.view_above import LoginView # Change this import properly
urlpatterns = [
...
path('accounts/login/', LoginView.as_view(), name="account_login"),
path('accounts/', include('allauth.urls')),
...
]
Another alternative solution using allauth is to use a custom ACCOUNT_ADAPTER and override it's get_login_redirect_url because LoginView will internally call it in case there is no next parameter. To do this, first inherit from allauth.account.adapter.DefaultAccountAdapter:
from django.urls import reverse
from allauth.account.adapter import DefaultAccountAdapter
class MyAccountAdapter(DefaultAccountAdapter):
def get_login_redirect_url(self, request):
return reverse('profile-page', kwargs={'username': request.user.username})
Next in settings.py set ACCOUNT_ADAPTER:
ACCOUNT_ADAPTER = "path.to.MyAccountAdapter"
The LOGIN_REDIRECT_URL should point to the page on which user lands after he successfully logs into your website, unless he was redirected to the login page by trying to visit any of the pages that require the authorization.
If you want to redirect user to a specific page, in this example to his own profile page, you can create an intermediate URL that will redirect the user to his own profile page, when visited. It can be accomplished like so:
Using class-based views:
class CurrentUserProfileRedirectView(LoginRequiredMixin, RedirectView):
def get_redirect_url(self, *args, **kwargs):
return reverse('profile-page', kwargs={'username': request.user.username})
or using function-based views:
#login_required
def current_user_profile(request):
return redirect('profile-page', username=request.user.username)
Next, register this redirect view as a regular view, not requiring any parameters, and set the LOGIN_REDIRECT_URL to this view's name.

Acquire model instance as view parameter instead of model id

In laravel, it's possible to have access to entire models with type-hinting the parameter with a model class. for example:
routes/web.php
Route::get('posts/{post}', function(\App\Post $post) {
/*
$post is now the model with pk in url and
if the pk is wrong it throws an 404 automatically
*/
});
How is that possible in django? (with functions as view)
def posts(request, post_id):
post = get_model_or_404(Post, pk=post_id)
# ...
The second line is completely boilerplate and so much repeated in my code.
You can make a custom path converter [Django-doc] for that. We can for example make model path converter:
# app/converters.py
from django.shortcuts import get_object_or_404
from django.urls.converters import IntConverter
class ModelConverter(IntConverter):
model = None
def to_python(self, value):
return get_object_or_404(self.model, pk=super().to_python(value))
def to_url(self, value):
if instanceof(value, int):
return str(value)
return str(value.pk)
def model_converter(model):
class Converter(ModelConverter):
model = model
return Converter
Then in your urls.py you can make model converters and register these once:
# app/urls.py
from app.converters import model_converter
from app import views
from app.models import Post
from django.urls import path, register_converter
register_converter(model_converter(Post), 'post')
urlpatterns = [
path('posts/<post:post>', views.post, name='post'),
# …
]
Then in the view, you obtain the Post object (given a post for that primary key exists) through the post parameter:
app/views.py
def post(request, post):
# post is here a Post object
# …
In your template, you can then just pass the Post object in the {% url … %} template tag, so:
<a href="{% url 'post' mypostobject %}">

How do I get the value in my views? I am trying to use class based views

I am trying to send a variable using class based views. Below is the code for the url file
from django.conf.urls import patterns, include, url
from myapp.views import foo
urlpatterns = patterns('',
(r'^/$', foo.as_views(template = 'home.html')),
(r'^about/$', foo.as_views(template = 'about.html')),
)
How do I access this in the foo class in my views file? I am trying to do something like this:
return render(request, template_n)
The parameters you pass to as_view are assigned as instance variables on your class based view so you can can self.template, i.e:
...
return render(request, self.template, {...})
As a sidenote, if you were using a named url patterns that captured a slug, for example:
url(r'^about/(?P<slug>[\w-]+)/$', foo.as_views(template='about.html')),
you would have access to the slug variable that is passed via the url by using the kwargs instance variable:
self.kwargs['slug']

How to apply a decorator to all views (of a module) in django

It happens a lot, when all the views in a specific module are supposed to be available only when the user is authorized, or they should all do the same checks.
How could I avoid repeating the annotations all over the file?
In your urls
url(r'someregexp/$', mydecorator(view.myview.dude), 'name_of_view'),
When using class-based views you can create a base class/mixin for all these views which implements the desired functionality (also using decorators) and then have all the views inherit from this base view.
from django.views.generic import TemplateView
class BaseView(TemplateView):
def get(self, request, *args, **kwargs):
# do some checking here
if not request.user.is_authenticated():
# do something if anonymous user
return super(BaseView, self).get(request, *args, **kwargs)
class MyView(BaseView):
pass
You could write a dispatcher, but if you have many urls for a module it would probably be more convenient to create a middleware layer.
See also: decorating-all-django-admin-views-1-4
When many urls need to be added, better wrap the decorator inside a function and call that function.
from django.conf.urls import re_path
from . import views
from somewhere import decorator
def url_with_decorator(regex, view, kwargs=None, name=None):
return re_path(regex, decorator(view), kwargs, name)
urlpatterns = [
url_with_decorator(r'^$', views.index, name='index'),
url_with_decorator(r'^any_thing$', views.any_view, name='index'),
# TODO: add url here
]

Django Rest Framework: 'function' object has no attribute 'as_view'

I've been trying for a while to get a ModelResource or a View working using the Django Rest Framework. I'm following the examples but the code in the examples is not working for me. Can anyone tell me why I might be getting this error.
views.py
# Create your views here.
from django.http import HttpResponse
from django.utils import simplejson
from django.core import serializers
from djangorestframework.views import View
from djangorestframework.response import Response
from djangorestframework import status
from interface.models import *
def TestView(View):
def get(self, request):
return Person.objects.all()
urls.py
from django.conf.urls.defaults import *
from djangorestframework.resources import ModelResource
from djangorestframework.views import ListOrCreateModelView, InstanceModelView, View
from interface.models import *
from interface.views import *
class PersonResource(ModelResource):
model = Person
ordering = ('LastName')
urlpatterns = patterns('',
url(r'^$', 'interface.views.index'),
url(r'^testview/$', TestView.as_view()),
url(r'^people/$', ListOrCreateModelView.as_view(resource=PersonResource)),
)
I'm now getting the error 'function' object has no attribute 'as_view'.
Since this is the #1 hit on google for this error message and there's a more subtle and probably common cause for it than the OPs, I'm posting this comment here.
This error can also be caused by using a standard view decorator on a class based view instead of on the __dispatch__ method within the view.
def TestView(View): should be class TestView(View):. As it stands, you define a function called TestView which takes an argument called View -- its body defines an inner function, then returns None.
To add to Tim Saylor point,
https://docs.djangoproject.com/en/dev/topics/class-based-views/intro/#id1
To decorate every instance of a class-based view, you need to decorate
the class definition itself. To do this you apply the decorator to the
dispatch() method of the class.
A method on a class isn’t quite the same as a standalone function, so
you can’t just apply a function decorator to the method – you need to
transform it into a method decorator first. The method_decorator
decorator transforms a function decorator into a method decorator so
that it can be used on an instance method. For example:
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView
class ProtectedView(TemplateView):
template_name = 'secret.html'
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(ProtectedView, self).dispatch(*args, **kwargs)
I am also getting this error but in my case i solved it with following idea.
That error usually happens if you try to override a class. That sometimes happens if you copy&paste code and forget to change e.g. the class name. But in my case it was little different
If you apply #login_required to a class, you will receive the error message:
‘function’ object has no attribute ‘as_view’
So, how should you decorate classes in Django now?
For class-based views, you have two options of decorating your classes.
1) Decorating the URLconf
2) Decorating the class
Both options leads to the same result - restricting the access to a class only for logged users. The difference between the options is how the decorator is applied to the class instance.Refer this page for decorators implementation
https://docs.djangoproject.com/en/1.4/topics/class-based-views/#decorating-class-based-views
use this if you use a class your decorator should be
import first:
from django.utils.decorators import method_decorator
then
#method_decorator(login_required(login_url='login'),name="dispatch")
class YourClassView(YourView):