How to get IntegerField from ForegienKey for objects model - django

I have a profile, and in this profile I want to display bookmarks for all messages (this is my IntegerField). In other words, how many people have bookmarked a particular author's posts.
models.py
class Post(models.Model):
slug = models.SlugField(unique=True)
title = models.CharField(max_length=255, db_index=True)
author = models.ForeignKey(
"users.CustomUser", on_delete=models.SET_NULL, null=True, db_index=True
)
bookmarkscount = models.IntegerField(null=True, blank=True, default=0)
class Profile(models.Model):
user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE)
This is my try in template but it does not work
<p>Bookmark</p>
<p>{{posts.bookmarkscount}}</p>
But work only if I use "for"
{% for post in posts %}
<p>{{ post.bookmarkscount}}</p>
{% endfor %}
views.py
class ProfileDetailView(DetailView):
model = Profile
template_name = "users/profile/profile.html"
def get_context_data(self, **kwargs):
try:
context["posts"] = Post.objects.filter(
author=self.object.user.is_authenticated
)
except Post.DoesNotExist:
context["posts"] = None

posts is a QuerySet type, a representation of the query to be sent to the DB. More like a list on steroids rather than a single instance of Post. This is a crucial concept you need to understand before coding anything in Django. (Docs here)
In order to get a sum of all the bookmarkscount values from all posts of a user, you need to use aggregation. (Docs here)
from django.db.models import Sum
posts.aggregate(Sum('bookmarkscount'))
# returns i.e.: {'bookmarkscount': 234}

Related

Is there a way to automate creation of pages(and slugs) as and when a record is created in Django

I'm trying to make a Django website which has all products with all their details(each product has it's own page). However, since there are too many products and they keep changing, I want to automate the creation and deletion of pages according to the database model (models.py).
Am I supposed to use a class based view? Am I supposed to use a function based view with a loop in it?
# models.py
import uuid
from django.db import models
class Brand(models.Model):
brand_id = models.AutoField(primary_key=True, unique=True)
brand_name = models.CharField(max_length=100, unique=True)
class Product(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=100, null=False, blank=False)
brand_id = models.ForeignKey(Brand)
# A lot more columns will follow (didn't lock on them yet)
# This is what I had thought of initially
If I understand what you are trying to do (create product pages automatically), you can override the save method in your Products model to create a new Page each time a Product is added, and then use models.CASCADE in the foreign key so that if a product is deleted, the associated page is deleted, like so:
from django.utils.text import slugify
class Page(models.Model):
product = models.ForeignKey('Product', on_delete=models.CASCADE)
...
class Product(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(blank=True, null=True)
...
def save(self, *args, **kwargs):
self.slug = slugify(instance.name)
page = Page.objects.create(product=self,...)
page.save()
super(GeeksModel, self).save(*args, **kwargs)
You can also use Django signals, like so in your models.py file:
#receiver(post_save, sender=Product)
def create_product_page(sender, instance, created, **kwargs):
if created:
instance.slug = slugify(instance.name)
page = Page.objects.create(product=instance,...)
page.save()
instance.save()
In your views.py, for example, to get a page:
def product_page(request, product_id, product_slug):
page = Page.objects.get(product__id=product_id)
...
context = {
'page_data': page
}
return render(request, 'page_template.html', context)
And in your urls.py for the custom product pages:
...
path('/products/pages/<int:product_id>/<str:product_slug>/', product_page, name="product_page")
...
So the url would look like:
http://example.com/products/pages/3/my-amazing-widget/
If you created the link dynamically in a template with something like:
{% for product in products %}
<p><a href='{% url 'product_page' product.id product.slug %}'>{{ product.name }}</a></p>
{% endfor %}
And there are other ways as well. In the model example above, I included a SlugField you can use to generate the custom URL as well. If you elaborate on your question I can update my answer.

How to properly use filter method with foreign key in django details view with multiple models

I have two model one model is being used for storing the blog posts and another model is being used for taking the ratings and comments. Below are two my models
# Models Code
class Products(models.Model):
name = models.CharField(max_length=50)
img = models.ImageField(upload_to='productImage')
CATEGORY = (
('Snacks','Snacks'),
('Juice','Juice'),
)
category = models.CharField(max_length=50, choices=CATEGORY)
description = models.TextField()
price = models.FloatField()
review = models.TextField()
# Rating Model
class Rating(models.Model):
product = models.ForeignKey(Products, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
stars = models.IntegerField(validators=[MinValueValidator(1),MaxValueValidator(5)])
comment = models.TextField()
#Views Code
class ProductListView(ListView):
model = Products
template_name = 'products.html'
context_object_name ='Products'
class ProductDetailView(DetailView):
model = Products
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
context['Rating'] = Rating.objects.filter(self.product_id) # How can i get the comments only for that specific product?
return context
In details-view how should I filter to fetch the comments for that specific product only ?
no need to write separate context for that in ProductDetailView, you can do it as follows in templates
{% for rate in object.rating_set.all %}
{{ rate.comment }}
{% endfor %}

How to query in Django with best efficiency?

I recently found that too much SQL query optimization issue. django-debug-tool reported hundreds of similar and duplicate queries. So, I'm trying to figure out the best efficiency of Django ORM to avoid unnecessary Queryset evaluation.
As you see the below Store model, a Store model has many Foreign key and ManyToManyFields. Due to that structure, there are many code snippets doing the blow on HTML template files such as store.image_set.all or store.top_keywords.all. Everything starts with store. In each store detail page, I simply pass a cached store object with prefetch_related or select_related. Is this a bad approach? Should I cache and prefetch_related or select_related each Foreign key or ManyToManyField separately on views.py?
HTML templates
{% for img in store.image_set.all %}
{{ img }}
{% endfor %}
{% for top_keyword in store.top_keywords.all %}
{{ top_keyword }}
{% endfor %}
{% for sub_keyword in store.sub_keywords.all %}
{{ sub_keyword }}
{% endfor %}
views.py
class StoreDetailView(View):
def get(self, request, *args, **kwargs):
cache_name_store = 'store-{0}'.format(store_domainKey)
store = cache.get(cache_name_store, None)
if not store:
# query = get_object_or_404(Store, domainKey=store_domainKey)
query = Store.objects.all().prefetch_related('image_set').get(domainKey=store_domainKey)
cache.set(cache_name_store, query)
store = cache.get(cache_name_store)
context = {
'store': store,
}
return render(request, template, context)
models.py
class Store(TimeStampedModel):
categories = models.ManyToManyField(Category, blank=True)
price_range = models.ManyToManyField(Price, blank=True)
businessName = models.CharField(unique=True, max_length=40,
verbose_name='Business Name')
origin = models.ForeignKey(Origin, null=True, on_delete=models.CASCADE, blank=True)
ship_to = models.ManyToManyField(ShipTo, blank=True)
top_keywords = models.ManyToManyField(Keyword, blank=True, related_name='store_top_keywords')
sub_keywords = models.ManyToManyField(SubKeyword, blank=True, related_name='store_sub_keywords')
sponsored_stores = models.ManyToManyField(
'self', through='Sponsorship', symmetrical=False, related_name='sponsored_store_of_store')
similar_stores = models.ManyToManyField(
'self', through='Similarity', symmetrical=False, related_name='similar_store_of_store')
shortDesc = models.TextField(blank=True, verbose_name='Short Description')
longDesc = models.TextField(blank=True, verbose_name='Long Description')
returnPol = models.TextField(verbose_name='Return Policy', blank=True)
returnUrl = models.CharField(max_length=255, null=True, blank=True, verbose_name='Return Policy URL')
likes = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, editable=False)
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, editable=False, on_delete=models.CASCADE,
related_name='stores_of_created_by', null=True, blank=True)
updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, editable=False, on_delete=models.CASCADE,
related_name='stores_of_updated_by', null=True, blank=True)
I really wouldn't advise custom caching/performance optimisation, unless it's a very last resort. Django has great docs on querysets and optimisation - if you follow those, it should be rare for you to experience major performance issues that require custom workarounds.
I think the issue here is that you're printing your objects in a template and hence calling their str() method. There's nothing wrong with this, but I'd check what variables you're using in your str() methods. I suspect you're referencing other models? I.e. the str() method in your image model (or whatever) is doing something like image.field.other_field. In this case, your query should look like:
queryset = Store.objects.prefetch_related('image_set__field')
Your final queryset may look like:
queryset = Store.objects.prefetch_related('image_set__field1', 'image_set__field2', 'top_keywords__field3', ...)
Note that you can still pass this into get_object_or_404 like so:
get_object_or_404(queryset, pk=<your_stores_id>)
Hope this helps.

Django filter foreign key model in views.py

I have two models, one is called Books and BookInstance, one Book has many BookInstances,
class Books(models.Model):
.........
def get_absolute_url(self):
"""
Returns the url to access a detail record for this book.
"""
return reverse('book-detail', args=[str(self.id)])
def __str__(self):
"""
String for representing the Model object.
"""
return '{0}'.format(self.book_name)
class BookInstance(models.Model):
books = models.ForeignKey('Books',verbose_name="Books", on_delete=models.SET_NULL, null=True)
keyrequest = models.OneToOneField('BookRequest', verbose_name='Book requests', on_delete=models.SET_NULL, null=True, blank=True,)
LOAN_STATUS = (
('a', 'Available'),
('o', 'On loan'),
('r', 'Reserved'),
)
status = models.CharField(max_length=1, choices=LOAN_STATUS, help_text='Key availability', verbose_name="Key status", blank=True)
date_out = models.DateField(null=True, blank=True, verbose_name="Date Issued")
due_back = models.DateField(null=True, blank=True, verbose_name="Date to be returned")
......
id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text="Unique ID for this particular book")
I have a class based view in views.py that uses the Book model to show the total number of BookInstances for that book, Here's my views.py:
class KeyListView(generic.ListView):
model = RoomKey
fields = '__all__'
template_name = 'catalog/roomkey_list.html'
And I have a template that shows the number of all BookInstances for a Book,as shown below:
{{ books.bookinstance_set.all.count }}
But I would like to filter it out and show the number of available BookInstance of that Book, I tried to use add a Query manager in the BookInstance class but that didn't work , django never threw any error it just didn't show anything. Can someone please tell me the correct way to implement something like this?
You can override view's get_queryset() method, and annotate count:
from django.db.models import Count, Case, When, CharField
def get_queryset(self):
return Books.objects.annotate(
available_books_count=Count(Case(
When(bookinstance__status='a', then=1),
output_field=CharField(),
))
Now in template you can do
{{ books.available_books_count }}

Try to join a OneToOne relationship in Django

I need some help doing a join using Django, which seems like it should be easy. I have looked at the documentation but it seems like it won't join for some reason.
I am trying to get in my view, the model.Photo and model.PhotoExtended with both joined and then displayed in the view. Currently I am just trying to get the model.Photo displayed but with a join which finds the request.user and filters it based on that.
They are in different apps.
models.py for model.Photo
class Photo(ImageModel):
title = models.CharField(_('title'),
max_length=60,
unique=True)
slug = models.SlugField(_('slug'),
unique=True,
help_text=_('A "slug" is a unique URL-friendly title for an object.'))
models.py for model.PhotoExtended
class PhotoExtended(models.Model):
Photo = models.OneToOneField(Photo, related_name='extended', help_text='Photo required', null=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, help_text='User that uploaded the photo')
views.py
class PhotoExtendedUserView(ListView):
template_name = 'photo_user_list.html'
def get_queryset(self):
user = get_object_or_404(User, username=self.request.user)
return Photo.objects.filter(photoextended__user=user)
You set the related_name on Photo (which shouldn't be capitalized by the way) to extended so you need to filter like so:
class PhotoExtendedUserView(ListView):
template_name = 'photo_user_list.html'
def get_queryset(self):
user = get_object_or_404(User, username=self.request.user)
# 'extended' vs. 'photoextended'
return Photo.objects.filter(extended__user=user)