HttpResponse error django generic template - django

I am able to render class based view generic ListView template using parameter hard coded in views.py.
class ResourceSearchView(generic.ListView):
model = creations
context_object_name = 'reviews'
template_name = 'reviews.html'
query = 'theory'
# def get(self, request):
# if request.GET.get('q'):
# query = request.GET.get('q')
# print(query)
queryset = creations.objects.filter(narrative__contains=query).order_by('-post_date')
However, when parameter is sent via form by GET method (below),
class ResourceSearchView(generic.ListView):
model = creations
context_object_name = 'reviews'
template_name = 'reviews.html'
query = 'theory'
def get(self, request):
if request.GET.get('q'):
query = request.GET.get('q')
print(query)
queryset = creations.objects.filter(narrative__contains=query).order_by('-post_date')
I receive this error
The view creations.views.ResourceSearchView didn't return an
HttpResponse object. It returned None instead.
Note that the parameter name q and associated value is being retrieved successfully (confirmed using print(query)).

So with CBV in Django, you have to return some kind of valid response that the interpreter can use to perform an actual HTTP action. Your GET method isn't returning anything and that's what is making Django angry. You can render a template or redirect the user to a view that renders a template but you must do something. One common pattern in CBV is to do something like:
return super().get(request, *args, **kwargs)
...which continues up the chain of method calls that ultimately renders a template or otherwise processes the response. You could also call render_to_response() directly yourself or if you're moving on from that view, redirect the user to get_success_url or similar.
Have a look here (http://ccbv.co.uk) for an easy-to-read layout of all the current Django CBVs and which methods / variables they support.

Thanks for the responses. Here is one solution.
class ResourceSearchView(generic.ListView):
model = creations
context_object_name = 'reviews'
template_name = 'reviews.html'
def get_queryset(self):
query = self.request.GET.get('q')
queryset = creations.objects.filter(narrative__contains=query).order_by('-post_date')
return queryset

Related

Customize queryset in django-filter ModelChoiceFilter (select) and ModelMultipleChoiceFilter (multi-select) menus based on request

I'm using django-filter in 2 places: My Django Rest Framework API, and in my FilterViews (Django Filter's Generic ListViews.) In the case of my FilterViews I'm showing both select boxes (ModelChoiceFilter) and multi-select boxes (ModelMultipleChoiceFilter) to be filtered on.
I need to be able to limit what's in those select and multi-select inputs based on a field inside the request.
It's relatively simple to change what's listed as a kwarg in the relevant field in the FilterSet. For example, here's my FilterSet where the queryset is set as a kwarg:
class FieldFilter(django_filters.FilterSet):
"""Filter for the field list in the API"""
dataset = ModelChoiceFilter(queryset=Dataset.objects.all())
class Meta(object):
"""Meta options for the filter"""
model = Field
fields = ['dataset']
And it's relatively straightforward to limit what the result is in DRF inside the get_queryset() method. For example, here's my DRF ViewSet:
class FieldViewSet(viewsets.ReadOnlyModelViewSet):
"""A ViewSet for viewing dataset fields"""
queryset = Field.objects.all()
serializer_class = FieldSerializer
filter_class = FieldFilter
def get_queryset(self):
"""Get the queryset"""
queryset = super(FieldViewSet, self).get_queryset()
queryset = queryset.filter(
dataset__organization=self.request.organization)
return queryset
I just can't find anywhere to edit the Dataset field in the filter_class when the view is being displayed.
This is super straightforward in Django FormView generic views, but it doesn't appear that FieldViewSet follows the same get_form() structure as generic views. It's also relatively straightforward to do in the admin, but DRF/Django-Filter don't seem to follow that structure either.
Is there any way to customize the queryset in those inputs on a per-request basis? Preferably both on FilterViews and in the HTML API browser, but just in FilterViews would be fine if it's too complicated for the HTML API browser.
After hours of search, I found the solution in the official documentation here!
The queryset argument for ModelChoiceFilter and ModelMultipleChoiceFilter supports callable behavior. If a callable is passed, it will be invoked with the request as its only argument.
import django_filters as filters
from django.utils.translation import gettext as _
def ourBranches(request):
if request is None:
return Branch.objects.none()
company = request.user.profile.company
return Branch.objects.filter(company=company)
class UnitFilter(filters.FilterSet):
branch = filters.ModelChoiceFilter(
queryset=ourBranches, empty_label=_("All Branches"))
class Meta:
model = Unit
fields = ('branch', )
and in the view, I made sure to pass the request as well
qs = Unit.objects.all()
filter = UnitFilter(self.request.GET, request=self.request, queryset=qs)
table = UnitTable(filter.qs)
I also had problems finding a resolution to this.
I solved it (I think) via the following:
views.py
table_filter = ExampleFilter(request.GET, kwarg_I_want_to_pass=request.user, queryset=qs)
filters.py
class ExampleFilter(django_filters.FilterSet):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('kwarg_I_want_to_pass', None)
super(ExampleFilter, self).__init__(*args, **kwargs)
self.filters['field_to_filter'].extra.update({
'queryset': Supplier.objects.filter(related_user=self.user),
'empty_label': '',
'help_text': False
})
class Meta:
model = ExampleModel
fields = ['related_user', 'field_to_filter', ... other fields]

Django access queryset variabile from a custom method on the same view

I am implementing csv export on django. In particular I have a link on my template to export actual query and for this I am trying to handle all on the same class based view.
Here is my code
# views.py
class MyView(ListView):
template_name = 'my_template.html'
model = Archivio
def get_queryset(self):
if self.request.GET.get('q'):
dateC = '01/01/'+self.request.GET.get('q')
queryset = Archivio.objects.filter(~Q(quoteiscrizione__anno_quota__exact=self.request.GET.get('q'))
return queryset
# my custom method
#staticmethod
def csv_output():
qs = MyView.get_queryset(): # i want to access to the queryset variable from get_queryset() method of the class
# here i have other code to produce csv output
But the method csv_output() is wrong.. I get this TypeError: csv_output() takes 0 positional arguments but 1 was given
I have also tried with a classmethod decorator but without success.
My question is: How can i access queryset variable from another method of the same class?
Note the self in get_queryset(self). It means the first argument of this method is always the instance of the class (in your case, MyView). So when you want to call it in another method, you should provide this instance.
The solution is to replace your #staticmethod decorator with the #classmethod one:
class MyView(ListView):
template_name = 'my_template.html'
model = Archivio
def get_queryset(self):
if self.request.GET.get('q'):
dateC = '01/01/' + self.request.GET.get('q')
queryset = Archivio.objects.filter(~Q(quoteiscrizione__anno_quota__exact=self.request.GET.get('q'))
return queryset
#classmethod
def csv_output(cls):
qs = cls.get_queryset(cls)
# Class methods have one required positional argument
# which is the class that contains them
# They are called like this:
# MyClass.class_method()
# So in your case, you can call
# get_queryset() by doing MyView.get_queryset()
# But as you are in a class method, you do
# cls.get_queryset()
# As get_queryset() needs one positional argument *self*,
# which is the instance of your class, you do
# cls.get_queryset(cls)
# and it will work as expected :-)
I solved my Issue saving my queryset on the request and then using it somewhere on my views like this:
views.py
class SomeClass(ListView):
def get_queryset(self):
.....
queryset = MyModel.objects.filter(*somefilterlist)
self.request.session['search_queryset'] = serialize('json', queryset) # in order to save something on session in must be JSON serialized
class Output(SomeClass):
#classmethod
def cvsOutput(cls):
deserialized = list(deserialize('json', request.session.get('search_queryset')))
pk_list = []
for arch in deserialized:
pk_list.append(arch.object.pk) # List of pk
queryset = Archivio.objects.filter(pk__in=pk_list) # Query the list ok pk's
In this way I was able to make available the list ok pk of the queryset of my ListView, then I make again the same query based on that list of pk's..

django request.GET in models

Is it possible in Django to have models method with request.GET ?
e.g.
class Car(models.Model):
owner = ForeignKey(Owner)
car_model = ...
def car_filter(self, request):
query = request.GET.get("q")
if query:
Car.objects.filter(owner = self.id.order_by('id')
else:
Car.objects.filter(owner = me).order_by('id'
)
?
Purely technically speaking, sure, you can - as long as you can pass the request object from the view. The example code you've posted is syntactically incorrect, but, something like this is technically possible. You just have to make sure that the method is class-method, not instance-method one (since you don't have any instances in this case):
class Car(models.Model):
...
#classmethod
def get_by_owner(cls, request):
query = request.GET.get("q")
if query:
return cls.objects.filter(owner=query)
elif request.user.is_authenticated():
return cls.objects.all()
def your_view(request):
cars = Car.get_by_owner(request)
...
However, DON'T DO THIS. It's a bad idea because you're moving your request processing logic to a model. Models should only care about the data, and user request handling is view's job.
So, I'd suggest to have all the logic in the views:
def your_view(request):
cars = Car.objects.all().order_by("id")
query = request.GET.get("q")
if query:
cars = cars.filter(owner=query)
...
If you need some complicated logic, that a lot of views would share, you can use model managers:
class CarManager(model.Manager):
def owned(self, username=None):
queryset = super(CarManager, self).get_query_set()
if username:
user = Owner.objects.get(username=username)
queryset = queryset.filter(owner=user)
return queryset
class Car(models.Model):
...
objects = CarManager()
...
def your_view(request):
query = request.GET.get("q")
cars = Car.objects.owned(query)
...
Possible, but you have to pass the request manually:
# inside your views
qs_ = car_object.car_filter(request)
but I dont see any sense in doing so.
Everything that has to do with request should go into views which is the place for request-response flow.
Actually you can handle this stuff in your view only
def yourview(self, request):
query = request.GET.get("q")
if query:
Car.objects.filter(owner = self.id).order_by('id')
else:
Car.objects.filter(owner = me).order_by('id')
else other wise you have to send your request object to the model function from your view.

django MultiValueDictKeyError while requesting get object

I have made a form to filter ListView
class SingleNewsView(ListView):
model = News
form_class = SearchForm
template_name = "single_news.html"
def get(self, request, pk, **kwargs):
self.pk = pk
pub_from = request.GET['pub_date_from']
pub_to = request.GET['pub_date_to']
return super(SingleNewsView,self).get(request,pk, **kwargs)
My form fields are pub_date_from and pub_date_to. When I run the site it says:
MultiValueDictKeyError .
I don't know what's going on. When I remove the two line of getting pub_from and pub_to the site works fine. I want these two values to filter the queryset.
On first request there is no form data submitted so request.GET would not have any data. So doing request.GET['pub_date_from'] will fail. You shall use .get() method
pub_from = request.GET.get('pub_date_from')
pub_to = request.GET.get('pub_date_to')
If these keys are not in the dict, will return None. So handle the such cases appropriately in your code.
Also, if you want to filter objects for ListView add get_queryset() method to return filtered queryset as explained here Dynamic filtering

How to do a DetailView in django 1.3?

I'm currently learning how to use the class-based views in django 1.3. I'm trying to update an application to use them, but I still don't uderstand very well how they work (and I read the entire class-based views reference like two or three times EVERY day).
To the question, I have an space index page that needs some extra context data, the url parameter is a name (no pk, and that can't be changed, it's the expected behaviour) and the users that don't have that space selected in their profiles can't enter it.
My function-based code (working fine):
def view_space_index(request, space_name):
place = get_object_or_404(Space, url=space_name)
extra_context = {
'entities': Entity.objects.filter(space=place.id),
'documents': Document.objects.filter(space=place.id),
'proposals': Proposal.objects.filter(space=place.id).order_by('-pub_date'),
'publication': Post.objects.filter(post_space=place.id).order_by('-post_pubdate'),
}
for i in request.user.profile.spaces.all():
if i.url == space_name:
return object_detail(request,
queryset = Space.objects.all(),
object_id = place.id,
template_name = 'spaces/space_index.html',
template_object_name = 'get_place',
extra_context = extra_context,
)
return render_to_response('not_allowed.html', {'get_place': place},
context_instance=RequestContext(request))
My class-based view (not working, and no idea how to continue):
class ViewSpaceIndex(DetailView):
# Gets all the objects in a model
queryset = Space.objects.all()
# Get the url parameter intead of matching the PK
slug_field = 'space_name'
# Defines the context name in the template
context_object_name = 'get_place'
# Template to render
template_name = 'spaces/space_index.html'
def get_object(self):
return get_object_or_404(Space, url=slug_field)
# Get extra context data
def get_context_data(self, **kwargs):
context = super(ViewSpaceIndex, self).get_context_data(**kwargs)
place = self.get_object()
context['entities'] = Entity.objects.filter(space=place.id)
context['documents'] = Document.objects.filter(space=place.id)
context['proposals'] = Proposal.objects.filter(space=place.id).order_by('-pub_date')
context['publication'] = Post.objects.filter(post_space=place.id).order_by('-post_pubdate')
return context
urls.py
from e_cidadania.apps.spaces.views import GoToSpace, ViewSpaceIndex
urlpatterns = patterns('',
(r'^(?P<space_name>\w+)/', ViewSpaceIndex.as_view()),
)
What am I missing for the DetailView to work?
The only problem I see in your code is that your url's slug parameter is named 'space_name' instead of 'slug'. The view's slug_field attribute refers to the model field that will be used for slug lookup, not the url capture name. In the url, you must name the parameter 'slug' (or 'pk', when it's used instead).
Also, if you're defining a get_object method, you don't need the attributes queryset, model or slug_field, unless you use them in your get_object or somewhere else.
In the case above, you could either use your get_object as you wrote or define the following, only:
model = Space
slug_field = 'space_name'