I have my page where I have my posts list, and I also want to have sidebar with suggestions. I used generic ListView for my posts, and needed to pass suggestions somehow so I used extra_context which should(?) work like that according to few examples I've read, but in template there is no 'suggestions' object.
class PostList(generic.ListView):
model = models.Post
paginate_by = 10
context_object_name = 'mj'
def get_queryset(self):
return models.Post.objects.filter(user = self.request.user)
def extra_context(self):
return {'suggestions':models.Pla.objects}
I don't have experience in django so maybe there is better way to pass suggestions for sidebar. Maybe it's possible to do this with wrapping view function since I want to have suggestions..
Class-based views don't use extra_context the way the older function-based generic views did. Instead, the usual way to do this is with a custom get_context_data call as shown in the docs:
https://docs.djangoproject.com/en/dev/topics/class-based-views/generic-display/#adding-extra-context
The example in the docs is almost exactly what you're trying to do. You may want to follow its example further and pass in a queryset (models.Pla.objects.all()) rather than the manager object (models.Pla.objects).
Related
I want to filter the user manager self.get_queryset() method in such a way that users on the client application don't see admin and staff users when searching for or viewing other accounts. The issue I'm running into is I am unable to login with my auth system if I override get_queryset entirely. My current setup is:
class AccountManager(BaseUserManager):
def get_public_queryset(self):
return self.get_queryset().filter(active=True, verified=True, admin=False, staff=False)
Using this design works fine if I define various sorting methods in the manager (because I can simply call that method), but it seems as though there should be a better way to do this. Any ideas?
I think additional methods, as you've implemented is good a solution, but if you insist using get_queryset method it would be fine to override the method and preserve base functionality. I'd do something like this:
...
def get_queryset(self, *a, **kw):
queryset = super().get_queryset(*a, **kw)
# filter your queryset here as you wish
queryset = queryset.filter(active=True, verified=True, admin=False, staff=False)
return queryset
...
As I've spotted from question text, you tried to call self.get_queryset() which would be recursive call (not super class implementation call), which will finally cause maximum recursion depth exceeded error.
Hope it helps
I have a set of function in views.py which are currently only user-accessible. I'm asked to make it publicly accessible and currently I am using the #login_required decorator in my views. Is there a way to apply this decorator conditionally based on the object being served?
For example, part of my views.py:
#login_required
def details(request, object_id):
o = get_object_or_404(Model, pk=object_id)
if o.user_id == request.user.pk:
return render(request, 'app/details.html')
else:
return redirect('app:home')
What I want to do:
if not o.is_public:
#login_required
def details(request, object_id):
o = get_object_or_404(Model, pk=object_id)
if o.user_id == request.user.pk:
return render(request, 'app/details.html')
else:
return redirect('app:home')
Of course, the code doesn't work since (i) it's not valid Python and (ii) I need to first get the object. I believe there might be an elegant solution using Django as this is quite a common feature in web applications but I've gone through the docs to no avail. I think I should surround the #login_required decorator with another decorator but I'm not too familiar with decorators in Python. Any help is appreciated.
You can use user_passes_test for this kind of behaviour.
from django.contrib.auth.decorators import user_passes_test
def public_check(user):
if user.is_public:
return True
#user_passes_test(public_check)
It will not work with a decorator because decorators are evaluated and applied on import time and, as you've said, you need to get the object first, which is based on the request.
Since you've asked for an elegant solution, and I'm assuming you want to create a decorator for it so you can reuse it on multiple views, then the solution is a class-based view. You can implement the behavior that you want as a class-based view mixin, which you can mix-in to different class-based views. This improved flexibility is one of the reasons class-based views were introduced.
In the example below where does context's index 'book_list' comes from, if is arbitrary what is the naming convention?
class PublisherDetail(DetailView):
model = Publisher
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(PublisherDetail, self).get_context_data(**kwargs)
# Add in a QuerySet of all the books
context['book_list'] = Book.objects.all()
return context
The naming convention you're referring to (_list) is based on the ListView's template_name_suffix. That inherits from MultipleObjectTemplateResponseMixin.
In practice if you use a ListView like this one based on your example:
class PublisherList(ListView):
model = Publisher
...you would able to refer to publisher_list in your template for the queryset of all publishers.
In your example you're including a list of all the books in your database using the same naming convention, but you could call that context variable (book_list) anything you wanted.
In that example, the variable name book_list is arbitrary. You could use books or anything else you like instead.
Using book_list is consistent with the ListView, which makes the list available in the template context as <lowercase model name>_list . See the docs on making friendly template contexts for more info.
I want to use generic class base views using django 1.9
What i am trying to understand that
from django.views.generic import CreateView
from braces.views import LoginRequiredMixin
from .models import Invoice
class InvoiceCreateView(LoginRequiredMixin,CreateView):
model = Invoice
def generate_invoice(self):
...
return invoice
now i want to bind this custom method to url. How can i achive this?
I know using function base view its simple but i want to do this using class base views.
Help will be appreciated.
Yes, this is the main issue to grasp in CBV: when things run, what is the order of execution (see http://lukeplant.me.uk/blog/posts/djangos-cbvs-were-a-mistake/).
In a nutshell, every class based view has an order of running things, each with it's own method.
CBV have a dedicated method for each step of execution.
You would call your custom method from the method that runs the step where you want to call your custom method from. If you, say, want to run your method after the view found that the form is valid, you do something like this:
Class InvoiceCreateView(LoginRequiredMixin,CreateView):
model = Invoice
def generate_invoice(self):
... do something with self.object
return invoice
def form_valid(self,form):
self.object = form.save()
self.generate_invoice()
return super(InvoiceCreateView,self).form_valid(form)
So you have to decide where your custom method should run, and define your own method on top of the view generic method for this step.
How do you know what generic method is used for each step of executing the view? That the method the view calls when it gets the initial data for the form is def get_initial? From the django docs, and https://ccbv.co.uk/.
It looks complex, but you actually have to write very few methods, just where you need to add your own behaviour.
So the background detail, Post is a model, and I am basically trying to create a blog, the same blog as the one shown in this video.
Here is the code:
from django.views.generic import ListView, DetailView
from models import Post
class PublishedPostsMixin(object):
def get_queryset(self):
queryset = super(PublishedPostsMixin, self).get_queryset()
return queryset.filter(published=True)
class PostListView(PublishedPostsMixin, ListView):
# PostListView takes default template name as `post_list.html`,
# as list was the name it was assigned.
model = Post
template_name = 'blog/post_list.html'
class PostDetailView(PublishedPostsMixin, DetailView):
model = Post
template_name = 'blog/post_detail.html'
So, if you can see, PublishedPostsMixin is inheriting from object, so how is it that the super() is working. If you can understand what is going on, could you please explain step by step, I'm a little confused.
The trick is in what super does. It's a dynamic call: it refers to the next class up in the MRO (method resolution order). Because (as Adrián says in the comments) the mixin is only supposed to be used in conjunction with other classes, there will always be something in between PublishedPostsMixin and object in the MRO.
For more details on super, you should read Raymond Hettinger's article Super considered super (note that it's written with Python 3 syntax in mind, but the principles are the same).