I know that there are several generic views like ListView, DetailView, or simply View.
The thing is can I actually get the context data that are declared in a BaseMixin's get_context_data() and use it in a View that doesn't have override get_context_data()?
Example:
class BaseMixin(object):
def get_context_data(self, *args, **kwargs):
context = super(BaseMixin, self).get_context_data(**kwargs)
context['test'] = 1
return context
And the view that extends this BaseMixin:
class FooView(BaseMixin, View):
def foo(self, request):
context = super(BaseMixin, self).get_context_data(**kwargs)
# do something
return
This is not working actually, even after put **kwargs as a parameter in foo(). The error is 'super' object has no attribute 'get_context_data'.
So is there a way to get context data which was set in BaseMixin in FooView ?
Thanks for your answers :)
Thanks to #Sayes and all answer posters, I finally solved this problem.
From what I figured out, the problem is actually in BaseMixin, the inherited class of BaseMixin, which is object, doesn't have a get_context_data() function, just like #Sayes commented.
After replace this object with ContextMixin, everything works perfectly, at least perfectly for now.
Here is the modified BaseMixin:
class BaseMixin(ContextMixin):
def get_context_data(self, *args, **kwargs):
# do something
return context
Related
I need to pass id from the url slug. I am using generic views. This is my code for urls.py:
path('category/<int:pk>/details/',
CategoryDetailView.as_view(),
name='category-details'),
and I need to pass the <int:pk> value into views.py, so I can filter my queryset with this id.
My views.py code:
class CategoryDetailView(DetailView):
model = Category
def get_context_data(self, *, object_list=Expense.objects.get_queryset(), **kwargs):
queryset = object_list
return super().get_context_data(
summary_per_year_month = summary_per_year_month(queryset.filter(category_id= <int:pk> ))
)
You can access values from the URL in self.kwargs.
queryset.filter(category_id=self.kwargs['pk'])
Note that your get_context_data is the other way round than normal. Typically, you call super() and then add to the context dict. It looks like your way will work, but it will seem odd to other Django users. You could try writing it as follows:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
queryset=Expense.objects.get_queryset()
context['summary_per_year_month'] = summary_per_year_month(queryset.filter(category_id=self.kwargs['pk']))
return context
Yes, the path parameters are stored in self.kwargs, a dictionary that maps the name of the parameter to the value. So you can make use of:
class CategoryDetailView(DetailView):
model = Category
def get_context_data(self, *args, **kwargs):
summary=summary_per_year_month(
Expense.objects.filter(category_id=self.kwargs['pk'])
)
return super().get_queryset(*args, **kwargs, summary_per_year_month=summary)
You use self.kwargs.get('pk').
class CategoryDetailView(DetailView):
model = Category
def get_context_data(self, *, object_list=Expense.objects.get_queryset(), **kwargs):
queryset = object_list
return super().get_context_data(
summary_per_year_month = summary_per_year_month(queryset.filter(category_id=self.kwargs.get('pk')))
)
Something that really helped me learn Django was to add breakpoints (pdb) in my code, then run dir() on each object I came across.
For example, dir(self) will tell you what properties and methods 'self' has (ie, kwargs, model, request, etc). Then you can start experimenting around with these properties: self.kwargs, self.request, self.model, etc, see what they return.
Soon enough, you would find out that self.kwargs returns a dictionary of arguments that includes 'pk', which you can access using get(). That's how you can access 'pk'.
To me, this simple trick unlocked most of my understanding of Django and python.
I will have standard class-based views for CRUD operations that inherit from various generic views like ListView, DetailView and so on.
I will be setting all of their
context_object_name
attribute to the same value.
I was wondering if there is a way to do it more pythonic, to not repeat the operations many times in the code, but to be able to change that variable in one place if necessary?
ps. what comes to my mind is of course further inheritance, but maybe there is some more django-like way?
You can also use a mixin, instead of a middleware app:
class CommonContextMixin(object):
def get_context_data(self, *args, **kwargs):
context = super(CommonContextMixin, self).get_context_data(*args, **kwargs)
context['foo'] = 'bar'
return context
Then use that mixin in your views:
class MyView(TemplateView, CommonContextMixin):
""" This view now has the foo variable as part of its context. """
Relevant Django docs: https://docs.djangoproject.com/en/2.1/topics/class-based-views/mixins/
Middleware can do the trick
class SetContextObjectNameMiddleware:
def process_template_response(self, request, response):
if 'object' in response.context_data:
response.context_data['foo'] = response.context_data['object']
return response
Then add the middleware to your settings.py
It's not really setting the view's context_object_name but it achieves the same outcome.
I'm trying to define a variable in my view like this:
class PostMessageView(LoginRequiredMixin, generic.TemplateView):
url_redirect = None
def post(self, request, *args, **kwargs):
return redirect(self.url_redirect)
I know this is not the good way, and there are build-in classes for that, but my problem is not here. My problem is about pure Python (I guess). If I make a descendant, I can do it like that, it works:
class ContactDetailView(PostMessageView):
template_name = 'my_home/contact_detail.html'
url_redirect = 'my_profile_contact_detail'
My problem is when I want to change url_redirect with a dynamic value, like:
class ContactDetailView(PostMessageView):
template_name = 'my_home/contact_detail.html'
def get_context_data(self, **kwargs):
self.url_redirect = self.request.build_absolute_uri(self.request.path)
Then I get argument of type 'NoneType' is not iterable because, I guess, self.url_redirect doesn't overwrite url_redirect.
How to do it properly in Python?
You can use a property for this:
class ContactDetailView(PostMessageView):
template_name = 'my_home/contact_detail.html'
#property
def url_redirect(self):
return self.request.build_absolute_uri(self.request.path)
This url_redirect method essentially acts like an attribute of the class. Using the decorator version like this will make it a getter only. You can use property as a method instead, if you wanted to make a setter as well.
The issue is that get_context_data() is not called as it should be called by your post() method.
This should work:
def post(self, request, *args, **kwargs):
self.get_context_data()
return redirect(self.url_redirect)
However, get_context_data() is supposed to return a dictionary of data to pass to the template, it is not supposed alter the object state.
i am working on a django project and struggling to understand class based views.
class readBooks(TemplateView):
template_name="books_read.html"
def get_context_data(self, **kwargs):
context = super(readBooks, self).get_context_data(**kwargs)
return context
def get_books(self):
wb=books.objects.all()
return wb
This is my class.
Problem is using get_books function is not callable.how am i goin to call this function.
sorry if i being too stupid asking. But i could not think of a better place than this.
I also need to understand how am i goin to use the returned result of this function in the Html file.
I am very new to django and Django tutorial is not so helpful for me.Any help is highly appreciated.
You should use generic model interface of Class-Based Views
# Use this snippet to call Books.objects.all()
class ReadBooks(ListView):
template_name = "books_read.html"
model = Books
# if you want filter the Book model
class ReadBooks(ListView):
template_name = "books_read.html"
model = Books
def get_queryset(self, *args, **kwargs):
return self.model.objects.filter(author='someone')
that's the beauty of the Class-Based Views, if you just need the all records of books, the code below is enough but ig you want to more customization for the view like calling other model queries, then you should use get_context_data
class ReadBooks(ListView):
template_name = "books_read.html"
model = Books
def get_context_data(self, *args, **kwargs):
ctx = super(ReadBooks, self).get_context_data(*args, **kwargs)
ctx['authors'] = Author.objects.all()
return ctx
Basically I want to use a generic view that lists objects based on a username. Now, the question is, how do I do something like:
(r'^resources/$',
ListView.as_view(
queryset=Resources.objects.filter(user=request.user.username),
...
)
)
I couldn't find a way to access the HttpRequest (request) object though... Or do I need to use my own views and do all object selection there?
You could try subclassing the generic view:
class PublisherListView(ListView):
def get_queryset(self):
return Resources.objects.filter(user=self.request.user.username)
Then your urls entry would look like:
(r'^resources/$',
PublisherListView.as_view(
...
)
)
More information on dynamic filtering in class based views can be found here: http://docs.djangoproject.com/en/dev/topics/class-based-views/#dynamic-filtering
If you really want to clutter your URLconf directly, you can do it like so:
(r'^resources/$',
lambda request: ListView.as_view(queryset=Resources.objects.filter(user=request.user.username), ...)(request)
)
Or access the request by subclassing the view:
class MyListView(ListView):
def dispatch(self, request, *args, **kwargs):
self.queryset = Resources.objects.filter(user = request.user.username)
return super(MyListView, self).dispatch(request, *args, **kwargs)