Suppose I have the following models.
Class Blog:
pass
class Entry:
models.ForeignKey(Blog)
I want to do something like this
filter & sort blog by some criteria
get related entries for page 2
Can I do something better than below?
blog_ids = Blog.objects.filter(q).order_by(order)
entry_qs = Entry.objects.filter(id__in=blog_ids)
paginator = Paginator(entry_qs)
entries = paginator.page(2)
I think you need to use order_by on the entries rather than the blog object. An easier way of implmenting sorting and pagination of blog entries would be using a class based view like ListView. For eg. in your views.py:
from django.views.generic import ListView
from .models import Entry
class BlogView(ListView):
queryset = Entry.objects.order_by("-date")
paginate_by = 10
You will need to mention it in the urls.py:
url(r'^$', BlogView.as_view(), name='home'),
Now, if you use a url like /page=1 it will show the second page of entries (passed as object_list)
Related
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?
I have a url set up dynamically for a project using:
path('project/<int:pk>', ProjectView.as_view(), name='project')
How can I make this so I can use two parameters, something like this:
path('project/<int:pk>/<int:category', ProjectView.as_view(), name='project')
So I need to set up links to each category, so the user will see only the updates from Project A category 1 of 7.
If ProductView is a DetailView, you need to alter the get_queryset a bit, like:
from django.views.generic.detail import DetailView
class ProductView(DetailView):
model = Product
template = 'some_template.html'
def get_queryset(self):
return super().get_queryset().filter(
category__id=self.kwargs['category']
)
Here we thus will filter the queryset first by 'category', and the boilerplate code of the DetailView will then filter by the primary key pk.
In your templates, you can generate urls, with:
{% url 'project' pk=some_pk category=some_category_id %}
or if you for example redirect:
return redirect('project', pk=some_pk, category=some_category_id)
I have a blog where i would like to construct my URLs in following fashion: blah.com/blog/this-is-my-first-blog-post where this-is-my-first-blog-post is the title of one specific blog post.
Is there a way to generate these types of urls based on the title column in my table with blogposts?
You'll be better off storing the URL key as a SlugField. You can determine that value by using slugify.
Then your code for the url would be something like:
url(r'^(?P<slug>[-\w]+)/$','example_view'),
And you can use get_object_or_404:
def example_view(request, slug):
instance = get_object_or_404(Model, slug=slug)
Or you can use a DetailView CBV as shown in the docs.
There are a couple of options:
In django admin, you can make the field pre-populate on editing.
prepopulated_fields = {'slug': ('title',), }
This needs to be put in the admin.py file.
In case you want to do it outside of admin, you can use a pre-save signal and use slugify as mentioned above by #schillingt above.
I have just completed the Django tutorials, and while excited about learning more, I am by no means proficient. I guess you could say that I don't know enough to be dangerous at this point.
Let's say that I have a database of music. I have an Artist model, an Album model, a Genre model, and a Song model. What I would like to be able to do is display albums (or even artists) based on given filters; so my front-end would display a list of albums and provide a means to filter the list. A "Jazz" link, for instance, would only display Jazz albums. Simple enough.
I can think of a couple ways to accomplish this, but I would like to start out on the right foot...to begin forming "best practice" Django methods. One way I can think of would be to write views...such that /albums/jazz would only display jazz. Another way would be to write model-level methods that filter the albums. Here I get a little fuzzy on how I would actually implement such a filter, however.
Will someone please give me broad overview of how this task is best accomplished?
Assuming you know how to structure an app within a project (i.e. what the tutorial teaches) you can work along this example with example models.py, urls.py and views.py for your sample app myapp.
Example models.py:
class Genre(models.Model):
name = models.CharField(unique=True) # set name to be unique
...
class Album(models.Model):
genre = models.ForeignKey(Genre)
...
Example urls.py:
urlpatterns = patterns('',
...
url(
r'^albums/(?P<genre>[-\w]+)/$',
ListAlbumsByGenreView.as_view(), name='list_albums_by_genre_view'
),
...
)
Note the genre parameter as the only argument in the URL pattern.
Example views.py using ListView:
from django.shortcuts import get_object_or_404
from django.views.generic.list import ListView
from myapp.models import Album, Genre
class ListAlbumsByGenreView(ListView):
model = Album
def get_context_data(self, **kwargs):
context = super(ListAlbumsByGenreView, self).get_context_data(**kwargs)
# fetch the genre; if genre not found, an HTTP 404 is returned
genre = get_object_or_404(Genre, name=kwargs['genre'])
# filter the albums by genre
context['albums'] = Album.objects.filter(genre=genre)
return context
The above ListView puts albums in your HTML template's context; this contains the list of albums filtered by genre.
The individually imported functions used above are all beautifully documented in the Django docs.
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'
),