Annotate method in django - python

I have a little problem with annotate. I want to display records from my class Kategorie in the main html file. I used the annotate method to take the query from db. I used in the second class Firmy the ForeignKey to class Kategorie. Now I dont know how to display for example how many websites added in the class Firmy are in the for example in category Business. Now I have something like: "Business (2)(3)(4)" when I used annotate with count by id. This is my models.py
from django.db import models
from django.utils import timezone
class Kategorie(models.Model):
glowna = models.CharField(max_length=150, verbose_name='Kategoria')
class Meta:
verbose_name='Kategoria'
verbose_name_plural='Kategorie'
def __str__(self):
return self.glowna
class Witryna(models.Model):
nazwa = models.CharField(default="", max_length=150, verbose_name = 'Nazwa strony')
adres_www = models.CharField(max_length=70, verbose_name='Adres www')
slug = models.SlugField(max_length=250, verbose_name='Przyjazny adres url')
email = models.CharField(max_length=100, verbose_name='Adres e-mail')
text = models.TextField(max_length=3000, verbose_name='Opis strony')
kategoria = models.ForeignKey(Kategorie, verbose_name='Kategoria')
data_publikacji = models.DateTimeField(blank=True, null=True, verbose_name='Data publikacji')
class Meta:
verbose_name='Strona www'
verbose_name_plural = 'Strony www'
def publikacja(self):
self.data_publikacji=timezone.now()
self.save()
def __str__(self):
return self.nazwa
And some part from views.py
from django.db.models import Count
wpisy_kat = Kategorie.objects.annotate(cnt_witryna=Count('Witryna'))
So what kind of method or tags I have to use to display for example:
Business(34)
Industry(21)
Health Care(11)
where the name od category is field from class Kategorie and integer is a result from query to database how many websites are in for example Business category?
My html file is:
{% for kategoria in kategorie %}
<table>
<tr>
<td>
<li>{{ kategoria.glowna|linebreaksbr }} </li>
{% for wpis in wpisy_kat %}
{{ wpis }} ({{ cat.cnt_witryna }})
{% endfor %}
</td>
</tr>
</table>
{% endfor %}
and the main html file:
{% include 'firmy/header.html' %}
<html>
<body>
<p>
<center>
<ul id="menu">
<li>Strona główna</li>
<li>Jak dodać stronę</li>
<li>Regulamin</li>
<li>Kontakt</li>
</ul>
</center>
<div class="glowna">
<div class="lewe_menu">
<h3><center>Ostatnio dodane</center></h3>
{%include 'firmy/widok_strony.html'%}
</div>
<div class="srodek">
<h3><center>Kategorie</center></h3>
<center>{%include 'firmy/widok_kategorii.html'%} </center>
</div>
<div class="prawe_menu">
<h3><center>Reklama</center></h3>
<center>Tutaj wpisz kod reklamy </center>
</div>
{% include 'firmy/footer.html' %}
</div>
</body>
</html>
view.py file
from django.shortcuts import render, get_object_or_404
from .models import Witryna, Kategorie
from django.utils import timezone
from django.db.models import Count
def widok_strony(request):
firmy = Witryna.objects.filter(data_publikacji__lte=timezone.now()).order_by('data_publikacji')
return render(request, 'firmy/widok_strony.html', {'firmy': firmy})
def widok_kategorii(request):
kategorie = Kategorie.objects.all()
wpisy_kat = Witryna.objects.annotate(cnt_kategoria=Count('kategoria'))
return render(request, 'firmy/widok_kategorii.html', {'kategorie': kategorie, 'wpisy_kat': wpisy_kat,})
def index(request):
firmy = Witryna.objects.filter(data_publikacji__lte=timezone.now()).order_by('data_publikacji')
kategorie = Kategorie.objects.order_by('glowna')
wpisy_kat = Witryna.objects.annotate(cnt_witryna=Count('kategoria'))
return render(request, 'firmy/index.html', {'kategorie': kategorie, 'wpisy_kat': wpisy_kat, 'firmy': firmy})
def detale_strony(request, slug):
det_wpisu = get_object_or_404(Witryna, slug=slug)
firmy = Witryna.objects.filter(data_publikacji__lte=timezone.now()).order_by('data_publikacji')
return render(request, 'firmy/detale_strony.html', {'det_wpisu': det_wpisu, 'firmy': firmy})
def detale_kat(request, slug_kat):
det_kategorii = get_object_or_404(Kategorie, slug_kat=slug_kat)
firmy = Witryna.objects.filter(data_publikacji__lte=timezone.now()).order_by('data_publikacji')
return render(request, 'firmy/detale_kat.html', {'det_kategorii': det_kategorii, 'firmy': firmy})

Your view needs to return something like :
wpisy_kat = Kategorie.objects.annotate(cnt_witryna=Count('witryna'))
return render(request, 'app/template.html', {'wpisy_kat': wpisy_kat})
template.html :
<ul>
{% for cat in wpisy_kat %}
<li>{{ cat }} ({{ cat.cnt_witryna }})</li>
{% endfor %}
</ul>
EDIT :
You can add sorting or filtering to the annotate query, no need to pass 2 parameters from the view, and no need to perform to for/loops :
replace the query in the view :
wpisy_kat = Kategorie.objects.annotate(cnt_witryna=Count('witryna')).order_by('glowna')
Then in the HTML:
<ul>
{% for cat in wpisy_kat %}
<li>{{ cat }} ({{ cat.cnt_witryna }}) </li>
{% endfor %}
</ul>

Related

Django add to wishlist

I have to do wishlist, I have done wishlist page, model and html.bBut when I click on the button bellow my post, I'm redirected to wishlist page and post didnt saved in my wishlist.So thats my code:
models.py
class Wishlist(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
wished_item = models.ForeignKey(Posts, on_delete=models.CASCADE)
def __str__(self):
return self.wished_item.title
class Posts(models.Model):
TYPE = Choices(
('private', _('private')),
('business', _('business')),
)
STATUS = Choices(
('active', _('active')),
('deactivated', _('deactivated'))
)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name='posts',
on_delete=models.CASCADE, verbose_name='owner')
phone_number = PhoneNumberField(verbose_name=_('Phone_number'), null=False, blank=False, unique=True)
title = models.CharField(verbose_name=_('Title'), max_length=100)
text = RichTextField(verbose_name=_('Text'))
image = models.ImageField(upload_to='images/%Y/%m/%d/', null=True, blank=True, validators=[file_size])
price = models.DecimalField(verbose_name=_('Price'), decimal_places=2, max_digits=9)
status = models.CharField(choices=STATUS, max_length=50)
created = models.DateTimeField(auto_now=True)
type = models.CharField(choices=TYPE, max_length=50)
def __str__(self):
return self.title
views.py
class WishListView(generic.View):
def get(self, *args, **kwargs):
wish_items = Wishlist.objects.filter(user=self.request.user)
context = {
'wish_items': wish_items
}
return render(self.request, 'wishlist/wishlist.html', context=context)
def addToWishList(request):
if request.method == 'POST':
post_var_id = request.POST.get('object-id')
post_var = Posts.objects.get(id=post_var_id)
print(post_var)
try:
wish_item = Wishlist.objects.get(user=request.user, post=post_var)
if wish_item:
wish_item.save()
except:
Wishlist.objects.create(user=request.user, post=post_var)
finally:
return HttpResponseRedirect(reverse('wishlist'))
wishlist.html
{% extends 'posts/base.html' %}
{% load thumbnail %}
{% block content %}
<div>
{% for item in wish_items %}
{% if item.wished_item.image1 %}
<img src="{{item.wished_item.image.url}}" alt="">
{% endif %}
</div>
<div>
<li>{{item.wished_item.title}}</li>
<li>{{item.wished_item.text}}</li>
<li>{{item.wished_item.price}}</li>
<li>{{item.wished_item.phone_number}}</li>
{% if item.wished_item.image %}
<img src="{% thumbnail item.wished_item.image 200x200 crop %}" alt="" />
<p></p>
{% endif %}
</div>
{% endfor %}
{% endblock %}
urls.py
urlpatterns = [
path("wishlist/", WishListView.as_view(), name='wishlist'),
path("add-to-wishlist", addToWishList, name='add-to-wishlist'),
]
and all posts template with add to wishlist button.
<ul>
{% for object in object_list %}
<li>Owner: {{ object.owner }}</li>
<li>Phone: {{ object.phone_number }}</li>
<li>Title: {{ object.title }}</li>
<li>Text: {{ object.text }}</li>
<li>Type: {{ object.type }}</li>
<li>Price: {{ object.price }}</li>
<li>Date: {{ object.created }}</li>
<p>
{% if object.image %}
<img src="{% thumbnail object.image 200x200 crop %}" alt="" />
{% endif %}
</p>
<form action="{% url 'add-to-wishlist' %}" method="POST">
{%csrf_token%}
<input type="hidden" name="object-id" value="{{object.id}}">
<input type="submit" value="Add to Wishlist">
</form>
<hr/>
Probably problem with posts Id, but I'm not sure in that.
You can use get_or_create instead of the if else statement for if exist or not. And use get_object_or_404 to make code more clear.
from django.shortcuts import get_object_or_404
def addToWishList(request):
if request.method == 'POST':
post_obj = get_object_or_404(Post, pk=request.POST.get('object-id'))
Wishlist.objects.get_or_create(user=request.user, post=post_obj)
return HttpResponseRedirect(reverse('wishlist'))
In your views.py try to replace these lines:
try:
wish_item = Wishlist.objects.get(user=request.user, post=post_var)
if wish_item:
wish_item.save()
except:
Wishlist.objects.create(user=request.user, post=post_var)
with
wish_item, was_created = Wishlist.objects.get_or_create(user=request.user, post=post_var)
# for debugging
if was_created:
print(f"{wish_item} was created")
else:
print(f"{wish_item} already exists")
what is the output?
The code looks OK for me. You might add a trailing / in your urls.py after the path("add-to-wishlist/" ... but I can't spot anything wrong in the first place.

Paginate detailview django

i need to paginate my category_detail page,but this view doesnt have list_objects.I have class for DetailView,where i need to add Paginator.Or maybe i can do it only in html template,and thats all?
class Category(models.Model):
name = models.CharField(_('Name'), max_length=200)
slug = models.SlugField(_('Slug'), unique=True)
PARAMS = Choices(
('following', 'following'),
('price_to', 'price_to'),
('price_from', 'price_from'),
)
def count_of_products(self):
return self.pk.count()
def __str__(self):
return self.slug
def get_absolute_url(self, **kwargs):
return reverse('products:category_detail', kwargs={'category_slug': self.slug})
this is my views.py
class CategoryListView(ActiveTabMixin, ListView):
model = Category
active_tab = 'category_list'
template_name = 'products/category_list.html'
def get_ordered_grade_info(self):
return []
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['grade_info'] = self.get_ordered_grade_info()
return context
class CategoryDetailView(DetailView):
model = Category
slug_url_kwarg = 'category_slug'
PARAM_FOLLOWING = 'following'
PARAM_PRICE_FROM = 'price_from'
PARAM_PRICE_TO = 'price_to'
slug_field = 'slug'
In my opinion,i need to do paginate def in CategoryDetailView, isnt it?
template
{% extends "base.html" %}
{% block content %}
<h2>{{ category.name }}</h2>
<div class="list-group">
{% for product in category.products.all %}
<a href="{{ product.get_absolute_url }}" class="list-group-item">
<h4 class="list-group-item-heading">{{ product.name }}</h4>
<p class="list-group-item-text">{{ product.price }}$</p>
<p class="list-group-item-text">Likes {{ product.likes.count }}</p>
<p>
<img class='img-article' src="{{product.image.url}}">
</p>
</a>
{% endfor %}
</div>
{% endblock content %}

Django Model and Form for Comment system

I have made a comment system under my books where only the authenticated user can comment. When I use the form to add a comment, it doesn't work! why ?
here is my models
models.py
class Books(models.Model):
author = models.ManyToManyField(Authors)
title = models.CharField(max_length=250)
number_of_pages = models.PositiveIntegerField(validators=[MaxValueValidator(99999999999)])
date_added = models.DateField(auto_now_add=True)
updated = models.DateField(auto_now=True)
publication_date = models.PositiveIntegerField(default=current_year(), validators=[MinValueValidator(300),
max_value_current_year])
cover = models.ImageField(upload_to='pics/covers/', default='pics/default-cover.jpg')
pdf_file = models.FileField(upload_to='pdfs/books/', default='pdfs/default-pdf.pdf')
category = models.ForeignKey(Categories, on_delete=models.CASCADE)
def __str__(self):
return self.title
class Comments(models.Model):
book = models.ForeignKey(Books, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
body = models.TextField()
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '{} - {}'.format(self.livre.title, self.user)
here is my forms
forms.py
class BookForm(ModelForm):
class Meta:
model = Books
fields = '__all__'
class CommentForm(ModelForm):
class Meta:
model = Comments
fields = ['body']
here is my views
views.py
#login_required(login_url='login')
def book_detail_view(request, book_id):
books = get_object_or_404(Books, pk=book_id)
context = {'books': books,}
return render(request, 'book_detail.html', context)
#login_required(login_url='login')
def add_comment(request, comment_id):
form = CommentForm()
books = get_object_or_404(Books, pk=comment_id)
user = request.user
if request.method == "POST":
form = CommentForm(request.POST, instance=books)
if form.is_valid():
comment = form.save(commit=False)
comment.user = user
comment.books = books
comment.save()
return redirect('book_detail', books.id)
context = {'form': form}
return render(request, 'comment_form.html', context)
here is my book detail page
book_detail.html
{% extends 'base.html' %}
{% block title %} {{ books.title }} {% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-4">
<p><img src="{{ books.cover.url }}"></p>
</div>
<div class="col-lg-8">
<h2>{{ books.title }}</h2>
<b>Author : </b>
{% for author in books.author.all %}
{{ author.name }}
{% if not forloop.last %},{% endif %}
{% endfor %}<br/>
<b>Catégory : </b>{{ books.category }}<br/>
<b>Pages : </b>{{ books.number_of_pages }}<br/>
<b>Publication : </b>{{ books.publication_date }}<br/>
<b>Date added : </b>{{ books.date_added }}<br/>
<b>Updated : </b>{{ books.updated }}<br/>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<p><button class="btn btn-outline-dark btn-sm"><i class="far fa-eye"></i> Read</button></p>
</div>
</div>
<hr/>
<div class="container-fluid">
<h2>Comments</h2>
</div>
<div class="container-fluid">
{% if not books.comments.all %}
<p>No comments yet ! <a class="text-primary" href="{% url 'add_comment' books.id %}">Add comment...</a></p>
{% else %}
<a class="text-primary" href="{% url 'add_comment' books.id %}">Add comment !</a><br/><br/>
{% for comment in books.comments.all%}
<b>{{ comment.user }}</b> - <span class="text-muted" style="font-size: 13px;">{{ comment.date }}</span>
<p>{{ comment.body }}</p>
{% endfor %}
{% endif %}
</div>
{% endblock %}
here is my form for comment model
comment_form.html
{% extends 'base.html' %}
{% block title %} Add a comment {% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Add this comment</button>
</form>
{% endblock %}
here is my urls
urls.py
urlpatterns = [
# BOOKS
path('book/<int:book_id>/', views.book_detail_view, name='book_detail'),
# COMMENTS
path('book/<int:comment_id>/comment/', views.add_comment, name='add_comment'),
]
Your form is currently set to edit the book, not the comment, you should remove the instance=books:
if request.method == "POST":
# no instance=books &downarrow;
form = CommentForm(request.POST)
If you use instance=books, the form will set attributes to the Books object, and then the comment = form.save(commit=False) will result in the fact that comment is a Books object that is already saved and thus updated in the database.
You also made a typo when setting the book of a Comments object: it is book, not books:
if form.is_valid():
comment = form.save(commit=False)
comment.user = user
comment.book = books # &leftarrow; .book, not .books
comment.save()
Note: normally a Django model is given a singular name, so Book instead of Books.

NoReverseMatch at /availability/1/edit/ Reverse for 'availability_list' with no arguments not found. Tried: ['availability\\/(?P<pk>[0-9]+)\\/list$']

I'm getting this error using Django 3 when trying to either 1) create a new object (CreateView), or 2) edit an existing one (UpdateView). Using CBVs and ModelForms.
I explored similar questions on Stackoverflow. Very often, it's about the template missing pk reference, but I triple-checked that and it doesn't seem to be the case.
Any idea?
Here are my codes:
URLs
path('availability/<int:pk>/list', views.AvailabilityListView.as_view(), name='availability_list'),
path('availability/new/', views.AvailabilityCreateView.as_view(), name='availability_new'),
path('availability/<int:pk>/edit/', views.AvailabilityUpdateView.as_view(), name='availability_edit'),
Views
class AvailabilityUpdateView(UpdateView):
template_name = 'crm/availability_form.html'
form_class = AvailabilityForm
model = Availability
class AvailabilityUpdateView(UpdateView):
template_name = 'crm/availability_form.html'
form_class = AvailabilityForm
model = Availability
class AvailabilityListView(TemplateView):
template_name = 'crm/availability_list.html'
def get_context_data(self, **kwargs):
user = self.request.user
kwargs['availabilities'] = Availability.objects.filter(staff__manager=Manager.objects.get(user=user)).filter(staff=self.kwargs['pk'])
return super().get_context_data(**kwargs)
Forms
class AvailabilityForm(forms.ModelForm):
class Meta():
model = Availability
exclude = []
Models
class Availability(models.Model):
staff = models.ForeignKey(Staff, on_delete=models.CASCADE)
start = models.DateTimeField()
end = models.DateTimeField()
class Meta:
verbose_name_plural = "Availabilities"
def get_absolute_url(self):
return reverse('staff_list')
Template
{% block body_block %}
<div class="container">
<div class="heading">
<h1>Availability</h1>
<a class = 'btn btn-primary' href="{% url 'availability_new' %}">+Create new availability</a>
</div>
<hr/>
{% if availabilities %}
<table class="table">
<tbody>
{% for availability in availabilities %}
<tr>
<td>{{availability.pk}}</td>
<td>{{availability.staff}}</td>
<td>{{availability.start}}</td>
<td>{{availability.end}}</td>
<td>
Edit
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class='empty'>Staff does not have any availability</p>
{% endif %}
</div>
{% endblock %}

Blank link with get_absolute_url

I try to create a link between lista_libri.html and lista_generi.html, using get_absolute_url. I've create already a link between autore.html and lista_libri.html and it runs well.
But if I active a link between lista_libri.html and lista_generi.html, it results blank.
Below I share the code strings that define model, view and templates.
models.py
from django.db import models
from django.urls import reverse
class Genere(models.Model):
nome = models.CharField(max_length=20)
def __str__(self):
return self.nome
def get_absolute_url(self):
return reverse("libri_genere", kwargs={"pk": self.pk})
class Meta:
verbose_name = "Genere"
verbose_name_plural = "Generi"
class Autore(models.Model):
nome = models.CharField(max_length=20)
cognome = models.CharField(max_length=20)
nazione = models.CharField(max_length=20)
def __str__(self):
return self.nome + " " + self.cognome
def get_absolute_url(self):
return reverse("profilo_autore", kwargs={"pk": self.pk})
class Meta:
verbose_name = "Autore"
verbose_name_plural = "Autori"
class Libro(models.Model):
titolo = models.CharField(max_length=100)
isbn = models.CharField(max_length=13)
autore = models.ForeignKey(Autore, on_delete=models.CASCADE, related_name="libri")
genere = models.ManyToManyField(Genere, related_name="generi")
def __str__(self):
return self.titolo
class Meta:
verbose_name = "Libro"
verbose_name_plural = "Libri"
views.py
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from .models import Autore, Libro, Genere
class AutoreDCBV(DetailView):
model = Autore
template_name = "autore.html"
class LibroLCBV(ListView):
model = Libro
template_name = "lista_libri.html"
class GenereDCBV(DetailView):
model = Genere
template_name = "lista_generi.html"
urls.py
from django.urls import path
from .views import LibroLCBV, AutoreDCBV, GenereDCBV
urlpatterns = [
path('', LibroLCBV.as_view(), name='lista_libri'),
path('autore/<int:pk>/', AutoreDCBV.as_view(), name='profilo_autore'),
path('genere/<int:pk>/', GenereDCBV.as_view(), name='libri_genere'),
]
lista_generi.html
{% extends 'base.html' %}
{% block head_title %}{{ block.super }} | {{ genere }}{% endblock head_title %}
{% block content %}
<h1>Genere: {{ nome }}</h1>
<br>
{% for gen in genere.generi.all %}
<h4><strong>Titolo: </strong> {{ gen.titolo }} </h4>
<h5><strong>Autore: </strong> {{ gen.autore }} </h5>
<p><strong>ISBN: </strong> {{ gen.isbn }} </p>
<hr>
{% endfor %}
{% endblock content %}
lista_libri.html
{% extends 'base.html' %}
{% block head_title %}{{ block.super }} | Libreria{% endblock head_title %}
{% block content %}
<h1>La nostra libreria:</h1>
<br>
{% for libro in object_list %}
<h4><strong>Titolo: </strong> {{ libro.titolo }} </h4>
<h5><strong>Autore: </strong> {{ libro.autore }} </h5>
<h6><strong>Genere </strong></h6>
{% for genere in libro.genere.all %}
{{ genere }}
{% endfor %}
<p><strong>ISBN: </strong> {{ libro.isbn }} </p>
<hr>
{% endfor %}
{% endblock content %}
What is wrong?
{{ libro.genere.get_absolute_url }}
libro.genere is not a Genere instance. It is a ManyRelatedManager which you can use to access related instance. Since libro.genere does not have a get_absolute_url method, it is evaluated as the empty string '' in the rendered template.
You are already looping through {% for genere in libro.genere.all %}, therefore you should use {{ genere.get_absolute_url }}.
{% for genere in libro.genere.all %}
{{ genere }}
{% endfor %}

Categories

Resources