Paginate category by posts in django - django

I've got a blog system, with categories and posts, as shown simplified below.
I want to have a detail page for category, where I paginate the posts belonging to that category.
class Category(models.Model):
name = models.CharField(max_length=150)
slug = models.SlugField()
class Post(models.Model):
title = models.CharField(max_length=90)
category = models.ForeignKey(Category, related_name="posts")
I use django 1.4 and class based views. What is the correct way of doing this? I guess I need to use paginate_queryset(queryset, page_size), but I don't know how. This is my basis for the view:
class CategoryDetailView(DetailView):
model = Category
queryset = object.posts.all()
def paginate_queryset(self, queryset, page_size):
pass

You might have a look at the docs. It should help to get you started.

Related

Order a queryset by a field in another queryset

In my django blog app, I have a model for Bookmark, which includes fields pointing to Post and User, and a field for when the bookmark was created.
# models
class Post(models.Model):
# ...
title = models.CharField(max_length=70)
body = RichTextField()
author = models.ForeignKey(
UserProfile,
on_delete=models.CASCADE,
related_name='posts')
created_on = models.DateTimeField(default=timezone.now)
# ...
class Meta:
ordering = ['created_on']
class Bookmark(models.Model):
post = models.ForeignKey(
Post, on_delete=CASCADE,
related_name="bookmarked_post"
)
user = models.ForeignKey(
User, on_delete=CASCADE,
related_name="bookmarks"
)
created_on = models.DateTimeField(default=timezone.now)
class Meta:
ordering = ['created_on']
I have a view to display my users' bookmarks. I want to order the posts queryset in the order they were bookmarked, with the recently bookmarked posts first.
# views.py
#login_required
def bookmarks_view(request):
bookmarks = Bookmark.objects.filter(user=request.user)
bookmarked_posts = Post.objects.filter(
bookmarked_post__in=bookmarks).order_by(???)
return render(request, 'bookmarks.html', {'posts': bookmarked_posts})
I'm struggling to figure out how to order my Posts queryset by the created_on field in the Bookmark model. To add additional complexity, both the Post and Bookmark model have a created_on field.
Can anyone point me in the right direction with this? I've scoured the django documentation and SO and not finding what I need, possibly I just don't know the right keywords... Or have I set up my models incorrectly to achieve what I want?
Something like this should work:
bookmarks = Bookmark.objects.filter(user=u0)
bookmarked_posts = Post.objects.filter(
bookmarked_post__in=bookmarks
).order_by("-bookmarked_post__created_on")
You can drop the __created_on at the end - Django will then use the default ordering specified on the related model. The - at the beginning means descending, so newer dates come first.
See also the documentation for order_by:
To order by a field in a different model, use the same syntax as when you are querying across model relations. That is, the name of the field, followed by a double underscore (__), followed by the name of the field in the new model, and so on for as many models as you want to join.
Because you specified the related name as bookmarked_post, that's also the name of the field you need to specify, from the perspective of Post, just like you used it in the filter.
Without the related_name, it would look like this:
bookmarks = Bookmark.objects.filter(user=u0)
bookmarked_posts = Post.objects.filter(
bookmark__in=bookmarks
).order_by("-bookmark__created_on")
(Note that you may want to specify a unique_together constraint for post and user on the Bookmark model, to make sure that a post can only be bookmarked once per user)

Filtering one model based on another model's field

I have the following models:
class User(AbstractUser):
pass
class Post(models.Model):
post_user = models.ForeignKey(User, on_delete=models.CASCADE)
post_content = models.TextField()
post_date = models.DateTimeField(default=timezone.now)
class Follow(models.Model):
follow_user = models.ForeignKey(User, on_delete=models.CASCADE)
follow_target = models.ForeignKey(User, on_delete=models.CASCADE, related_name='follow_target')
I'm trying to create a queryset of all posts that were created by users I follow (me being the currently logged in user). My queryset currently looks like this but I keep getting NameError: field not found:
postresult = Post.objects.all().filter(post_user=follow__follow_target, follow__follow_user=request.user)
Any help will be greatly appreciated!
You filter with:
postresult = Post.objects.filter(
post_user__follow_target__follow_user=request.user
)
The post_user will follow the ForeignKey from Post to User. Then we use the related_name='follow_target' to follow the relation to the Follow object, and then we use follow_user to retrieve the following user.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: The related_name=… parameter [Django-doc]
is the name of the relation in reverse, so from the User model to the Follow
model in this case. Therefore it (often) makes not much sense to name it the
same as the forward relation. You thus might want to consider renaming the follow_target relation to followers.
In case you rename the related name, the query is thus:
postresult = Post.objects.filter(
post_user__followers__follow_user=request.user
)

Get List of Categories on the basis of foreign model in django

I've two models as below.
class Category(models.Model):
title = models.CharField(max_length=55)
class Meta:
verbose_name = 'Food Category'
verbose_name_plural = 'Food Categories'
def __str__(self):
return self.title
class FoodItem(TimeStampWithCreator):
CATEGORY_CHOICES = (
('takeway', 'Takeaway'),
('dine_in', 'Dine In'),
('function', 'Function'),
)
type_menu_select = models.CharField(max_length=20, choices=CATEGORY_CHOICES, default='takeway')
category = models.ForeignKey(FoodCategory, on_delete=models.CASCADE)
i want to filter all the categories containing takeaway, I've no idea how to achieve this
You've included category choices in your FoodItem model but the model also has a ForeignKey to a Category model, this isn't needed if the only category objects you have are those three choices (the category field must refer to one of those anyway as it's the ForeignKey). To filter the items by category you would need to use a queryset filter.
https://docs.djangoproject.com/en/3.0/topics/db/queries/#retrieving-specific-objects-with-filters
FoodItem.objects.filter(category=YourCategory)
This is usually the kind of thing I'd like to test in the shell as I don't do it very often. If what you want is actually all Category with a FoodItem that have type_menu_select set to 'takeway' then the following should work (but I haven't tested it):
Category.objects.filter(fooditem__type_menu_select='takeway')
This is using the "reverse" relationship on the ForeignKey and there's more information in the Django docs (search for 'reverse').

Update several objects with formset

I'm 1st year on CS so forgive my noobness if i am totally unclear.
I have several objects from my "Products" Model. Now I would like to update the same field on all of the objects, the 'quantity' field with different values. But instead of clicking in and out of each product as with updateview, i would like to list all products and set the value for each and change them at the simultaneously. As far as i can see "FormSet" should do the trick?
My category model looks like this (to assign for a product)
class Category(models.Model):
category = models.CharField(max_length=30)
def __str__(self):
return self.category
My Product model looks like this:
class Product(models.Model):
title = models.CharField(max_length=200)
description = models.CharField(max_length=200)
category = models.ForeignKey(Category)
price = models.DecimalField(max_digits=5, decimal_places=2)
stock = models.PositiveIntegerField()
def __str__(self):
return self.title
My update view for updating single product looks like this:
class UpdateProductView(UpdateView):
model = Product
form_class = ProductForm
template_name = "product_form.html"
success_url = '/products'
class CreateCategoryView(FormView):
template_name = "category_form.html"
form_class = CategoryForm
I read the documentation on formset, but i gotta admit i didn't feel much smarter about how to actually use it afterwards.. can anyone give a hand?
Didn't find quantity field on your Product model, but as I can see, you want to use ModelFormSet.
# Generate your formset class with `modelformset_factory`
ProductFormSetClass = modelformset_factory(Product, fields=('quantity',))
# Now you can create your formset, bind data, etc
formset = ProductFormSetClass(request.POST)
formset.save()
More details by link:
https://docs.djangoproject.com/en/1.9/topics/forms/modelforms/#model-formsets
Also, don't forget to inspect all modelformset_factory arguments.

Using prefetch_related for reducing queries overhead

I have two models.
class Category(models.Model):
title = models.CharField(max_length=60, unique=True)
def __unicode__(self):
return self.title
class Post(models.Model):
title = models.CharField(max_length=60, unique=True)
category = models.ForeignKey(Category)
#code
views.py
class PostList(ListView):
model = Post
def get_queryset(self):
queryset = Post.objects.all().\
select_related('category')
But for my main page i need further all categories for the navbar. What the best approach to add categories to get_queryset()?
I tried to use prefetch_related.
post = Post.objects.all().prefetch_related('category')
but i don't understand how to fetch all categories.
Is it correct solution?
class PostList(ListView):
def get_queryset(self):
p = Post.objects.all().\
select_related('category') #like tag for each post
p.categories = Category.objects.all() #all categories for navbar
return p
Or Django has own methods for this task?
I don't understand what this has to do with either select_related or prefetch_related. Neither of those will help in getting categories that are not related to your posts: as their names imply, they are to do with getting related items, not unrelated ones.
If you need all the categories, you should simply do Category.objects.all().