Hi I want to understand this line of code:
return super().get(request, *args, **kwargs)
This line is part of a method in class based view in views.py file , for example
class HomePage(TemplateView):
template_name = "index.html"
def get(self, request, *args, **kwargs):
if request.user.is_authenticated:
return HttpResponseRedirect(reverse("afterlogin"))
return super().get(request, *args, **kwargs)
I know this method will check if the current user is authenticated and redirect to a different tempalte (afterlogin) in this case if a user is logged in , otherwise it will stay in index.html and then this function will execute this line return super().get(request, *args, **kwargs) anyway {I don't understand this line} .for example, if we were redirect to afterlogin page for example,then what this last line will do then?? .. this is the general idea that I understand but I want to understand the details of the code like:
If anyone can explain:
1- why I have these parameters: (self, request, *args, **kwargs) in get function?
2- when this method : def get(self, request, *args, **kwargs): will be triggered or called?
3-
explain the last line in the get method: return super().get(request, *args, **kwargs) step by step and what is its purpose.
I will be grateful if anyone can explain in details because I am still learning.. Thanks a lot
The super() function represents the parent class of the current one (in this case, this means TemplateView).
In your code, this means that get() will, as you noted, first check if the user is authenticated; if it is, then it will redirect to the afterlogin URL. If the user is not authenticated however, the function will not return, so it will go on and call the get() method of TemplateView (with the same arguments that have been passed to your get()), that will output whatever it would normally output if you hadn't inherited it.
To see what exactly that would be, you can navigate to TemplateView in the source code and read its get() method.
About your second question: when a user navigates to a URL you defined in your project, Django will check the method used for the request (if they're just viewing the page for the first time, this will be GET; another example is POST, which you probably heard before) and call the method of your view (HomePage) with the corresponding name, get(). If you also define a post() method, that will be called when the user requests the page with a POST method, for example by submitting a form with the method="post".
About your first question: request is an HTTP request object that is passed to the function to basically tell them what the user('s browser) has requested, what it wants to do. *args is a list of all other positional arguments that might have been added to the get() call. The same states for **kwargs, which is instead a dictionary of keyword arguments. These can very well be empty, but it's good that we pass them on to the upper level so that we don't loose any functionality that may be needed.
Just as an example, if you go to a url like mysite.com/page/search?=myquery, this would be a GET request, and it would include a keyword argument with key search and value myquery, so your **kwargs would look like: kwargs = {"search": "myquery"}. In the same way, the contents of a form submitted by POST would be available to your method to do what you need to do with them.
Related
I have to write a custom function inside class based view which is like below:
class ExampleView(ListCreateAPIView,
UpdateAPIView,
DestroyAPIView):
queryset = Example.objects.all()
serializer_class = ExampleSerializer
def get(self, request, *args, **kwargs):
/.........some code............./
def random_custom(self, request, *args, **kwargs):
/.........some code............./
Here above I have a random custom function which I need to call from the url. Now I am not sure how to do that. If it was a modelviewset, we can do it easily like this:
path("example/",users.ExampleView.as_view({"get": "random_custom"}),
),
I have done it before in ModelVIewset,ie call custom functions like above but I am not sure how to do that is genericviews like above.
Update
def dispatch(self, request, *args, **kwargs):
if request.method == 'PUT' and kwargs.get('slug'):
return self.custom_function(request,*args,**kwargs)
return super().dispatch(request, *args, **kwargs)
def custom_function(self, request, *args, **kwargs):
instance = self.get_object()
print(instance)
slug = kwargs.get("slug")
print(slug)
print(request.data)
Here I can see that from dispatch, the custom function is called and I can see the print of instance and slug but when I print(request.data) it says the error below:
AttributeError: 'ExampleView' object has no attribute 'data'
I need request.data to perform some logic in the data.
Have you tried putting random_custom() into get() method? It should be executed right after GET method is initialised by client.
class ExampleView(...):
...
def get(self, request, *args, **kwargs):
self.random_custom()
/.........some code............./
def random_custom(self, request, *args, **kwargs):
/.........some code............./
The code which makes the decision about which method on your APIView-derived class to call is in APIView.dispatch(). You can read the source here.
Here's how it works:
Convert the method name (e.g. GET, POST, OPTIONS) to lowercase. Check that the method name is a valid HTTP method. If not, return an error code.
Check if the object defines a lowercase version of the method name. If not, return an error code.
Use getattr() to get that method, and call it.
This means that there are only two ways to redirect the call from get().
Define get() and make it call random_custom(), as #NixonSparrow describes.
Override the dispatch() method to call something else.
Those are the only two ways to do it.
I'm new to Django. I understand what are the usage of *args and **kwargs. And also know how to use them in method overriding.
But, I don't understand what purpose they serve while overriding the save() method in a model class.
My observation is that no number of arguments, either non-keyworded or keyworded, were assigned to them anywhere. Still why do I must use them and how.
Have this example:
class DemoModel(models.Model):
title = models.CharField(max_length = 200)
slug = models.SlugField()
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(DemoModel, self).save(*args, **kwargs)
Please explain.
From Django model documentation:
It’s also important that you pass through the arguments that can be
passed to the model method – that’s what the *args, **kwargs bit does.
Django will, from time to time, extend the capabilities of built-in
model methods, adding new arguments. If you use *args, **kwargs in
your method definitions, you are guaranteed that your code will
automatically support those arguments when they are added.
Very late but,
You should add return to the save method.
return super(DemoModel, self).save(*args, **kwargs)
Secondly, the kwargs which means keyword arguments are also URL parameters that are passed to the views like kwargs={"pk": self.object.id} when you save the method and it needs to redirect to a detail view for example, it needs the id of the just created object. The magic happens already in Django views, but you can pass extra parameters if you want.
This code is from here (http://gswd-a-crash-course-pycon-2014.readthedocs.org/en/latest/talksmodel.html). . .In the TalkListRemoveTalkView, he overrides get_object and gets the specific Talk. Then he overrides the get method, and again retrieves the object. . .
My question is, if he gets the object we need in the get_object method, why do we need to again call get_object in the get method?
Thinking out loud, does the get method pull in the kwargs from the URL for the Talk and TalkList, then pass them to the get_object method for the query? Or do I have this completely wrong? Thanks in advance.
class TalkListRemoveTalkView(views.LoginRequiredView, RedirectView):
model = Talk
def get_redirect_url(self, *args, **kwargs):
return self.talklist.get_absolute_url()
def get_object(self, pk, talklist_pk):
try:
talk = self.model.objects.get(
pk=pk,
talk_list_id=talklist_pk,
talk_list__user=self.request.user
)
except Talk.DoesNotExist:
raise Http404
else:
return talk
def get(self, request, *args, **kwargs):
self.object = self.get_object(kwargs.get('pk'),
kwargs.get('talklist_pk'))
self.talklist = self.object.talk_list
self.object.delete()
return super(TalkListRemoveTalkView, self).get(request, *args, **kwargs)
He's not doing it twice. The call to get_object in the get method is the only time that method is called. There's no reference to it outside that call, and this view does not inherit from any other views that would call it elsewhere.
Note though that this code is bad for other reasons; in particular, you must never do a destructive action like a delete in a GET call, those should always be done on POST.
Developing my first application here and im using a class based view(django.views.generic.base.View) to handle requests from a webpage.
On the webpage I have different forms that send out POST requests, for example, there is text posting form, comment form, vote button, etc. and Im checking POST.has_key() to see which form has been posted and processing according to that.
Whats a better way to do it? And is it possible to define method names such as post_text, post_comment etc and configure dispatch() to run the method accordingly?
I would do it like this:
class AwesomeView(View):
def post(self, request, *args, **kwargs):
# This code is basically the same as in dispatch
# only not overriding dispatch ensures the request method check stays in place.
# Implement something here that works out the name of the
# method to call, without the post_ prefix
# or returns a default method name when key is not found.
# For example: key = self.request.POST.get('form_name', 'invalid_request')
# In this example, I expect that value to be in the 'key' variable
handler = getattr(
self, # Lookup the function in this class
"post_{0}".format(key), # Method name
self.post_operation_not_supported # Error response method
)
return handler(request, *args, **kwargs)
def post_comment(self, request, *args, **kwargs):
return HttpResponse("OK") # Just an example response
With function based Django view it was simple to switch between several different views based on a condition, e.g. something like:
def base_view(request):
if some_condition():
return foo_view(request)
else:
return bar_view(request)
I can't find a simple way to do the same with the new class-based generic views. The only way I can think of is to redisrect, which I would like to avoid for various reasons:
def base_view(request):
if some_condition():
return redirect(reverse("name_of_url_to_class-based_view_foo"))
else:
return redirect("/url_to_class-based_view_bar/")
Any suggestions?
This is equivalent to your example with class based views.
class FooView(View):
pass
class BarView(View):
pass
class BaseView(View):
# staticmethod to avoid adding 'self' to the arguments
foo_view = staticmethod(FooView.as_view())
bar_view = staticmethod(BarView.as_view())
def dispatch(self, request, *args, **kwargs):
if some_condition():
return self.foo_view(request, *args, **kwargs)
else:
return self.bar_view(request, *args, **kwargs)
Even though the Django docs do say that the function based generic views are now deprecated I think the only reason to switch would be if you're writing less code.
If you're still set on switching, you'll want to first identify which class based views or mixins are most appropriate (single object, multiple objects, date based, forms, etc.). If the conditional was used to select a function that returns different context data / template to provide to the view, you can push the conditional down into an overridden get_queryset|get_context_data|get_object|get_template_names depending on your use case.
For example,
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(BaseView, self).get_context_data(**kwargs)
# Add in the publisher
if some_condition():
context['some_data'] = ...
else:
context['other_data'] = ...
return context
If all else fails and you're still determined to have class based views, you could probably also override get(self, request, *args, **kwargs) and do the switching there to the appropriate method. The detailed docs are getting better but I've still found myself poking through the source code to figure out how to accomplish what I want.