Django get custom queryset into ListView - python

I have a listview that I access in a pretty bog standard way to return all metaobjects.
#url
url(r'^metaobject/$', MetaObjectList.as_view(),name='metaobject_list'),
#ListView
class MetaObjectList(ListView):
model = MetaObject
I've recently added a search form that I want to scan my objects (I've got about 5 fields but I've simplified the example). What I'd like to do is re-use my MetaObjectList class view with my specific subset. I am guessing I need to override the get_queryset method but I'm not clear in how I get the queryset from my FormView into the listview. I mucked around a bit with calling the as_view() in the formveiw's form_valid function with additional parameters but couldn't get it to work and it seemed hacky anyway.
class SearchView(FormView):
template_name = 'heavy/search.html'
form_class = SearchForm
#success_url = '/thanks/'
def form_valid(self, form):
#build a queryset based on form
searchval=form.cleaned_data['search']
list = MetaObject.objects.filter(val=search)
#where to from here?
I also looked at trying to post the data from the form view over to the listview but that seemed like I'd need to re-write the form logic into the listview.
I'm on python 3.x and django 1.11.

I found what I feel is more elegant than the comment on the question:
My form valid now points to the list object's as_view method and passes the request and the queryset I want
def form_valid(self, form):
#build a queryset based on form
searchval=form.cleaned_data['search']
list = MetaObject.objects.filter(val=search)
return MetaObjectList.as_view()(self.request,list)
This hits the ListView as a post which I use to alter the queryset
class MetaObjectList(ListView):
model = MetaObject
queryset = MetaObject.objects.prefetch_related('object_type','domain')
def post(self, request, *args, **kwargs):
self.queryset = args[0]
return self.get(request, *args, **kwargs)
The only obvious change is using kwargs to make it a bit clearer. Otherwise this seems to work well.

Related

Django Class Based View - How to access POST data outside of post() method

I have a CBV based on a TemplateView
It has a post(self) method to access user input.
Also, it has a template_name property which should be populated with data from post().
No Django models or forms are used here.
Attempt to extract data within post method:
# views.py
class ReturnCustomTemplateView(TemplateView):
def post(self, *args, **kwargs):
chosen_tmplt_nm = self.request.POST.get('tmplt_name')
print(chosen_tmplt_nm) # correct value.
# how do I use it outside this method? (like bellow)
template_name = chosen_tmplt_nm
... or is there any other way I can get the data from request.POST without def post()?
You can override the get_template_names() method [Django-doc] which returns an iterable (for example a list) of template names to search for when determining the name of the template, so:
class ReturnCustomTemplateView(TemplateView):
def get_template_names(self):
return [self.request.POST.get('tmplt_name')]
def post(self, *args, **kwargs):
return self.get(*args, **kwargs)
I would however advise to be careful with this: a POST request could be forged, so a hacker could make a POST request with a different template name, and thus try to obtain for example the content of the settings.py file or another file that contains sensitive data.

When to override get method in Django CBV?

I've been learning Django and one source of confusion I have is with class based views and when to override the get method. I've looked through the documentation and it explains what get does but it doesn't explain when I should override get.
I originally created a view this way:
class ExampleView(generic.ListView):
template_name = 'ppm/ppm.html'
paginate_by = 5
def get(self, request):
profiles_set = EmployeeProfile.objects.all()
context = {
'profiles_set': profiles_set,
'title': 'Employee Profiles'
}
return render(request, self.template_name, context)
But I was recently told that my code was simple of enough for the default implementation, and that all I needed was this:
class ExampleView(generic.ListView):
model = EmployeeProfile
template_name = 'ppm/ppm.html'
So my Question is this: In what scenario/circumstance should I override the get method?
If you are using the builtin generic views, then you should rarely have to override get(). You'll end up either duplicating lots of functionality, or break features of the view.
For example, the paginate_by option will no longer work in your view, because you are not slicing the queryset in your get() method.
If you are using a generic class based view like ListView, you should try to override specific attributes or methods where possible, rather than overriding get().
The advantage of your view which overrides get() is that it's very clear what it does. You can see that the view fetches a queryset, includes it in a context, then renders the templates. You don't need to know about ListView to understand the view.
If you like the explicitness of overriding get() subclass View instead. You aren't using any of the features of ListView, so it doesn't make sense to subclass it.
from django.views.generic import View
class ExampleView(View):
template_name = 'ppm/ppm.html'
def get(self, request):
...
You should override the get method when you specifically want to do something other than the default view does. In this case, your code isn't doing anything other than rendering the template with the list of all EmployeeProfile objects, which is exactly what the generic ListView would do.
You might override it if you want to do something more complicated. For example, maybe you want to filter based on a URL parameter:
class ExampleView(generic.ListView):
template_name = 'ppm/ppm.html'
def get(self, request):
manager = request.GET.get('manager', None)
if manager:
profiles_set = EmployeeProfile.objects.filter(manager=manager)
else:
profiles_set = EmployeeProfile.objects.all()
context = {
'profiles_set': profiles_set,
'title': 'Employee Profiles'
}
return render(request, self.template_name, context)

unbound method get_queryset() must be called with WorkoutList instance as first argument (got nothing instead)

I am implementing DRF's Pagination over my existing web service (RESTful API). Now I learned from the docs of DRF Pagination that pagination is authomatically applied to ListCreateAPIView , only need to add some of the lines in settings.py file.
So I did the changes according to the documentation and for my webservice I wanted my queryset to be dynamic.
Below are the changes done:
urls.py
url(r'^users/(?P<pk>[0-9]+)/workouts/get/$',
ListCreateAPIView.as_view(WorkoutList.get_queryset(), serializer_class=WorkoutSerializer), name='list'),
views.py
class WorkoutList(generics.ListCreateAPIView):
queryset = Workout.objects.all()
serializer_class = WorkoutSerializer
permission_classes = (UserPermissions,)
def get_queryset(self):
workout_instance = WorkoutList()
workout_instance.get_queryset()
query_params = self.request.QUERY_PARAMS.dict()
if 'date' in query_params and 'exclude_app_install_time' in query_params:
query_set = Workout.objects.filter(created__contains=date).exclude(
app_install_time=query_params['exclude_app_install_time'])
else:
query_set = {}
return query_set
def list(self, request, *args, **kwargs):
workouts = self.get_queryset()
serializer = WorkoutSerializer(workouts, many=True)
return Response(serializer.data)
PS : I have stackoverflowed (pun intented) the problem but couldn't find the right solution(s).
Also I want to implement OffsetLimitPagination in the DRF. A small example link will be helpful
You're doing a couple of very strange things here.
If you subclass a view, you should use that subclass in the urls, not a strange mash-up of the original class and a method from the subclass. So:
url(r'^users/(?P<pk>[0-9]+)/workouts/get/$',
WorkoutList.as_view(serializer_class=WorkoutSerializer), name='list'),
Once you've fixed that, you'll get into an infinite recursion inside your get_queryset method. Again, when you subclass, if you want to call the original implementation you use super; you don't initialize another instance of the current class and try to call that method, because it'll be the same method.
def get_queryset(self):
query_set = super(WorkoutList, self).get_queryset()
Edit I guess the pagination doesn't work because you are starting from a blank Workout query rather than using the returned value from the super call. So you should do:
def get_queryset(self):
query_set = super(WorkoutList, self).get_queryset()
query_params = self.request.QUERY_PARAMS.dict()
if 'date' in query_params and 'exclude_app_install_time' in query_params:
query_set = query_set.filter(created__contains=date).exclude(
app_install_time=query_params['exclude_app_install_time'])
return query_set

Django Class Based View UpdateView Restricted User

I am trying to use a Django UpdateView to display an update form for the user. https://docs.djangoproject.com/en/1.8/ref/class-based-views/generic-editing/
I only want the user to be able to edit their own form.
How can I filter or restrict the the objects in the model to only show objects belonging to the authenticated user?
When the user only has one object I can use this:
def get_object(self, queryset=None):
return self.request.user.profile.researcher
However, I now need the user to be able to edit multiple objects.
UPDATE:
class ExperimentList(ListView):
model = Experiment
template_name = 'part_finder/experiment_list.html'
def get_queryset(self):
self.researcher = get_object_or_404(Researcher, id=self.args[0])
return Experiment.objects.filter(researcher=self.researcher)
class ExperimentUpdate(UpdateView):
model = Experiment
template_name = 'part_finder/experiment_update.html'
success_url='/part_finder/'
fields = ['name','short_description','long_description','duration', 'city','address', 'url']
def get_queryset(self):
qs = super(ExperimentUpdate, self).get_queryset()
return qs.filter(researcher=self.request.user.profile.researcher)
URL:
url(r'^experiment/update/(?P<pk>[\w\-]+)/$', login_required(ExperimentUpdate.as_view()), name='update_experiment'),
UpdateView is only for one object; you'd need to implement a ListView that is filtered for objects belonging to that user, and then provide edit links appropriately.
To prevent someone from simply putting the URL for an edit view explicitly, you can override get_object (as you are doing in your question) and return an appropriate response.
I have successfully been able to generate the list view and can get
the update view to work by passing a PK. However, when trying to
override the UpdateView get_object, I'm still running into problems.
Simply override the get_queryset method:
def get_queryset(self):
qs = super(ExperimentUpdate, self).get_queryset()
# replace this with whatever makes sense for your application
return qs.filter(user=self.request.user)
If you do the above, then you don't need to override get_object.
The other (more complicated) option is to use custom form classes in your UpdateView; one for each of the objects - or simply use a normal method-based-view with multiple objects.
As the previous answer has indicated, act on the list to show only the elements belonging to the user.
Then in the update view you can limit the queryset which is used to pick the object by overriding
def get_queryset(self):
qs = super(YourUpdateView, self).get_queryset()
return qs.filter(user=self.request.user)

Access POST data outside of post() in Django 1.5 TemplateView

I am new to Django (1.5) and I am trying to do a basic POST form. I have a TemplateView that implements the form (passed to the template using get_context_data).
When the form fails for some reason (e.g. validation error), I want to show the form again, containing the data that the user has filled. When it succeeds, I want to redirect to a success page (e.g. the just-created item).
Here's what I've done so far:
class WriteForm(forms.Form):
subject = forms.CharField()
text = forms.CharField(widget=forms.Textarea)
# some other stuff
class WriteView(MailboxView):
# MailboxView extends TemplateView and defines some context
template_name = 'messages/write.html'
form_data = None
def post(self, request, *args, **kwargs):
# treat form data...
# lets make things simple and just assume the form fails
# I want to do something like that:
self.form_data = request.POST
# should I return something?
def get_context_data(self, **kwargs):
context = super(WriteView, self).get_context_data(**kwargs)
if self.form_data is None:
context['form'] = WriteForm()
else:
context['form'] = WriteForm(self.form_data)
return context
Thanks in advance!
Django already has a FormView that you might be able to use. If you want to see how it works, here's the code on GitHub.
If you want to write your own view instead of using the built in form view, you might also find it useful to look at the FormView in Django Vanilla Views, which has a simpler implementation.

Categories

Resources