Replacement for num_latest with class-based date-based generic views? - django

I've switched to Django 1.3 in order to get pagination for my date based generic views. This works fine, however there is a page where I want a specific number of items but do not want it paginated. For example, return the first 5 news entries.
In 1.2 we had num_latest which we could put in our info dict to get the latest items. This doesn't seem to exist with the new class-based generic views.
I could set paginate_by to 5 and just not use the pagination links in the template, but then people will still be able to see the old entries by punching in the url manually (which I don't want). Furthermore I don't want Django to set up pagination that I'm not going to use.
Edit: This is the urlconf line I'm currently using:
url(r'^$',
ArchiveIndexView.as_view(
model = Entry,
context_object_name = 'entry_list',
template_name = 'news/news.html',
date_field = 'published',
), name = 'archive_index'
),
Further edit: Attempting to override get_dated_queryset I've used this bit of code in conjunction with the urlconf as above but with the new view called:
class MainIndex(ArchiveIndexView):
def get_dated_queryset(self):
return Entry.objects.all()[:2]
I get almost the same error as mentioned in the comments:
Cannot reorder a query once a slice has been taken.

Try overriding this instead:
def get_dated_items(self):
date_list, items, extra_context = super(MainIndex, self).get_dated_items()
return (date_list, items[:2], extra_context)
Note: this implementation may leave the date_list inconsistent with the items query set after the latter is sliced. I think that to fix that you would need to regenerate date_list too. see the implementation of BaseArchiveIndexView.get_dated_items in SVN for more details: http://code.djangoproject.com/browser/django/trunk/django/views/generic/dates.py.
Something like this might work:
def get_dated_items(self):
date_list, items, extra_context = super(MainIndex, self).get_dated_items()
items = items[:2]
date_list = self.get_date_list(items, 'year')
if not date_list:
items = items.none()
return (date_list, items, extra_context)
but if it works without this, I would not touch it because it looks too messy.

I ran into this exact problem myself. I've found that using ListView (instead of ArchiveIndexView) for this saved me time and hassle.
For your first chunk of code, the difference would be:
from django.views.generic import ListView
url(r'^$',
ListView.as_view(
model = Entry,
context_object_name = 'entry_list',
template_name = 'news/news.html',
queryset=Entry.objects.all().order_by("-published")[:2],
), name = 'archive_index'
),

Related

DJANGO How to filter queryset with ListView?

I'm currently building a website with Django and my problem is the following : I have a page, home, which goal is to display all Plat objects in the database (Plat is a model from the database). What I want is having kind of a filtering table next to the list, which enables to filter the objects on various attributes. For example, we could filter the objects with a price greater than 10 euros, or the objects with a certain attribute lower than 5, or both at the same time.
here are the relevant parts of my files :
views.py
class home(ListView):
model = Plat
context_object_name = "plats"
template_name = "actualites/home.html"
paginate_by = 9
urls.py
urlpatterns = [
path('home', views.home.as_view(), name = 'home'),
]
The home.html file is made with bootstrap and is really big so I will not display it since I don't think it's much useful. A solution to my problem may be to put parameters in the url path('home/<int:price>', views.home.as_view(), name = 'home'), and overwrite get_queryset in the home view def get_queryset(self):
return Plat.objects.filter(prix=self.kwargs['price'])
The problem is that the filtering can be done on many attributes so my url would be really big, like home/<int:attr1_min_value>/<int:attr1_max_value>/<int:attr2_min_value>/...... Moreover, I would have to put default value for parameters for example attr2_min_value = 0 by default to enable me to filter only on attr1. But I don't think it's possible with Django. And I'm not sure about how to make the link between the filter buttons in the template and the arguments in the url.
What is the correct way to proceed?

Django REST Framework - let ListAPIView behave similar to pure ListView

I am trying to convince ListAPIView to behave the way the ListView from pure Django does (that means, renders a template with an object_list variable, possibly some pagination stuff, and so on). This is what I tried:
class UserListView(ListAPIView):
permission_classes = (AllowAny, )
queryset = User.objects.all()
serializer_class = UserListSerializer
renderer_classes = (TemplateHTMLRenderer, )
template_name = 'user/list.html'
Assume the User to be the builtin Django user model, UserListSerializer to be a ModelSerializer with fields = "__all__" and the template containing just a for loop over object_list displaying all the users.
When I try it, I get the following error:
TypeError: context must be a dict rather than ReturnList.
I must be doing something terribly wrong, I believe there must me a way to make use of the genericity and I just have no idea how.
A serializer requires context in dict format but you are providing ReturnList which generated by renderer_classes.
I didn't get if you need ListView then why are you using ListAPIView? you are clearly rendering a templet instead JSON. So please mention specific reason you need to do that so that anyone can understand what you are trying to achieve.
Ok, I just stepped into a trap. Everything works as expected if I enable pagination, e. g. adding this into my settings:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 100
}
I would never guess this would be the problem.

Modelchoicefield Queryset Confusion

All,
I'm new to Django and have been doing pretty good so far but this one has me stumped. I'm trying to utilize ModelChoiceField for a number of records that have the same name. I'm using Postgresql so I was able to determine that I need to use the distinct command and that is working perfectly. The records in my dropdown are all stripped down to just one version of each of the records. However, when I try to get all of the versions of a particular record, that's where I'm getting lost. I am able to get the detail of each record if I don't use distinct via a DetailView, but I am really trying to get all versions of each record on the screen after the modelchoicefield.
Here is my form:
class History(forms.Form):
dropdown = forms.ModelChoiceField(queryset=History.objects.all())
def __init__(self, user, *args, **kwargs):
super(History, self).__init__(*args, **kwargs)
self.fields['dropdown'].widget.attrs['class'] = 'choices1'
self.fields['dropdown'].empty_label = ''
qs = History.objects.all().distinct('record_name')
self.fields['dropdown'].queryset = qs
I am ultimately trying to get a view the queryset on the screen via my template. I have tried several different versions of code in the template but nothing seems to work. If I use the CBV DetailView without distinct I can get all of the records with their detail view fine. However, that's not what I'm trying to do. I have played with several versions of the queryset command in the template as I found several questions similar to mine but can't seem to get it to work. I found a couple of references to something similar to:
{% for record in form.history.field.queryset %}
etc.
{% endfor %}
But can't seem to get it to work in my Django template. Any and all help is appreciated! Thank you in advance!
In this case I'd either suggest
a) to put the value of your dropdown field into your url matching. See the django docs for named groups in URL. Additionally, you could add an onchange event to your dropdown-field which redirects to <current url>/<value of dropdown> or simply change the value of (if existing) buttons which links to the following page. Caution: With this solution you must ensure that the values of your dropdown field matches url-format (django's slugify might be useful for this).
or
b) to add your dropdown field to your input form or as input field. Then you can extract the value of your dropdown with:
try:
dropdown_value = request.POST['dropdown-field-name'] # dict-error if field is not in request.POST
except:
# some error actions
then you can add this value as filter to your queryset:
def get_queryset(self, dropdown_value=None):
# ...
qs = qs.filter('field-name' = dropdown_value) # possibly no/wrong results if dropdown_value is corrupted or manipulated

Django-haystack: how do I select which index to use in a SearchQuerySet?

I've been looking through the Haystack documentation on multiple indexes, but I can't figure out exactly how to use them.
The main model in this example is Proposal. I want to have two search indexes that return a list of proposals: one that only searches in the proposals themselves, and one that searches in proposals along with their comments. I've set up search_indexes.py like this:
class ProposalIndexBase(indexes.SearchIndex, indexes.Indexable)
title = indexes.CharField(model_attr="title", boost=1.1)
text = indexes.NgramField(document=True, use_template=True)
date = indexes.DateTimeField(model_attr='createdAt')
def get_model(self):
return Proposal
class ProposalIndex(ProposalIndexBase):
comments = indexes.MultiValueField()
def prepare_comments(self, object):
return [comment.text for comment in object.comments.all()]
class SimilarProposalIndex(ProposalIndexBase):
pass
Here's my search in views.py:
def search(request):
if request.method == "GET":
if "q" in request.GET:
query = str(request.GET.get("q"))
results = SearchQuerySet().all().filter(content=query)
return render(request, "search/search.html", {"results": results})
How do I set up a separate view that gets a SearchQuerySet from a specific index?
The Haystack (and other auto-generated) documentation is not a good example of clarity and it's about as exciting as reading a phone book. I think the section you referred to on "Multiple Indexes" is actually about accessing different backend search engines (like whoosh, solr, etc.) for queries.
But your question seems to be about how to query the "SearchIndexes" for different models. In your example, you want to have one search query for "proposals" and another one for "proposals" + "comments," if I understand your question correctly.
I think you want to look at the SearchQuerySet API, which describes how to filter the queryset returned by the search. There is a method called models that allows you to supply a model class or a list of model classes to limit the queryset results.
For example, in your search view, you may want to have a query string parameter for, say, "content" that specifies whether the search is for "proposals" or for "everything" (proposals and comments). So your frontend needs to supply the extra content parameter when calling the view (or you can use separate views for the different searches).
Your query strings need to look something like:
/search/?q=python&content=proposal
/search/?q=python&content=everything
And your view should parse the content query string parameter to get the model(s) for filtering the search query results:
# import your model classes so you can use them in your search view
# (I'm just guessing these are what they are called in your project)
from proposals.models import Proposal
from comments.models import Comment
def search(request):
results = None
if request.method == "GET":
if "q" in request.GET:
query = str(request.GET.get("q"))
# Add extra code here to parse the "content" query string parameter...
# * Get content type to search for
content_type = request.GET.get("content")
# * Assign the model or models to a list for the "models" call
search_models = []
if content_type is "proposal":
search_models = [Proposal]
elif content_type is "everything":
search_models = [Proposal, Comment]
# * Add a "models" call to limit the search results to the particular models
results = SearchQuerySet().all().filter(content=query).models(*search_models)
return render(request, "search/search.html", {"results": results})
If you have a lot of search indexes (i.e. a lot of content from many models), you might not want to hardcode the models in your view but use the get_model function from django.db to fetch the model class dynamically.

Create empty queryset by default in django form fields

I have this fields in form:
city = forms.ModelChoiceField(label="city", queryset=MyCity.objects.all())
district = forms.ModelChoiceField(label="district", queryset=MyDistrict.objects.all())
area = forms.ModelChoiceField(label="area", queryset=MyArea.objects.all())
district comes from click on city and area comes from click on area. With queryset=MyDistrict.objects.all() and queryset=MyArea.objects.all() form will be very heavy. How can I make querysets empty by default?
You can have an empty queryset by doing this:
MyModel.objects.none()
Although i don't know how are you going to use that form, you can put that as your field's queryset in order to get what you need...
You can find more information here
#radtek's comment should be an answer as it is useful in similar scenarios but with different approach than the accepted answer.
If your queryset changes with the url in your view.
I am extending the answer with example as I used:
def my_view(request):
...
form = YourForm(initial={'field1':value1, 'field2':value2})
form.fields['field3'].queryset = YourModel.objects.filter('foo'=bar)