How to calculate the difference between two context variables in Django template - django

Consider a model:
class TempReport(models.Model):
id = models.AutoField(primary_key=True)
cost = models.FloatField()
revenue = models.FloatField()
# Some other fields not relevant to topic
class Meta:
managed = False
db_table = 'temp_report'
unique_together = (('sale_point', 'date'), ('id', 'sale_point'),)
#property
def net_income(self):
return self.revenue - self.cost
My goal is to calculate net income = revenue - cost
The code for the template:
<tbody>
{% for repdata in reporttable %}
<tr>
<td> {{ repdata.revenue }}</td>
<td> {{ repdata.cost }}</td>
<td> {{ repdata.net_income}}</td>
</tr>
{% endfor %}
</tbody>
...and the view
def tempreport(request):
reporttable = TempReport.objects.values('id','cost','revenue')
return render_to_response('report.html',
{'reporttable': reporttable},
context_instance = RequestContext(request))
I end up with an empty net_income even if no error message is present. Any ideas why this might be caused by ?

Creating a property on the model should work. The indentation on your code is incorrect. The property should be a method of the model class, not the Meta class.
class TempReport(models.Model):
id = models.AutoField(primary_key=True)
cost = models.FloatField()
revenue = models.FloatField()
# Some other fields not relevant to topic
class Meta:
managed = False
db_table = 'temp_report'
unique_together = (('sale_point', 'date'), ('id', 'sale_point'),)
#property
def net_income(self):
return self.revenue - self.cost
In your view, don't use values(), because that will return dictionaries rather than model instances, and you won't be able to access the property.
from django.shortcuts import render
def tempreport(request):
reporttable = TempReport.objects.all()
for r in reporttable:
r.net_income = r.revenue - r.cost
return render(request, 'report.html', {'reporttable': reporttable})
Note I've also updated the view to use render instead of the obsolete render_to_response.

Related

Displaying data from the model Django

I'm new in Python and hope that someone can help me. I realise that this is probably not a unique question, but please be sympathetic.
I'm working on web-application (it's a bookstore). I make a cart and the proccess of forming an order. Now I'm trying to make a usr profile but unfortunately, I don't know how to display all user orders and make it possible to cahnge orders (change quantity of books for exmpl.) and how to make changable user profile information.
I realised the following logic:
User creates the cart and then create the order. After order creation cart also is in database.
To summarise the above, main questions are:
How to add all information from user cart (that was formed into the order) to user's profile?
How to make user's data and order's/cart's data possible to change in the user's profile?
How to display several user's orders in separate rows in HTML table (cause in my template all orders are in one row)?
Cart models:
User = get_user_model()
class Cart(models.Model):
customer = models.ForeignKey(
User, null=True, blank=True,
related_name="Customer",
verbose_name="Customer",
on_delete=models.PROTECT
)
#property
def total_price_cart(self):
goods = self.goods.all()
total_price_cart = 0
for good in goods:
total_price_cart += good.total_price
return total_price_cart
def __str__(self):
return str(self.pk)
class BooksInCart(models.Model):
cart = models.ForeignKey(
Cart,
related_name="goods",
on_delete=models.CASCADE,
verbose_name="Cart"
)
book = models.ForeignKey(
Book,
on_delete=models.PROTECT,
verbose_name='Book',
)
quantity = models.IntegerField(
verbose_name="Quantity",
default=1
)
price = models.DecimalField(
verbose_name='Price',
max_digits=5,
decimal_places=2,
)
#property
def total_price(self):
return self.price * self.quantity
Cart views:
class CartUpdate(View):
def post(self, request):
action = request.POST.get('submit')
if action == "save_cart":
cart_id = self.request.session.get('cart_id')
cart, created = models.Cart.objects.get_or_create(
pk=cart_id,
defaults={},
)
if created:
self.request.session['cart_id'] = cart.pk
goods = cart.goods.all()
if goods:
for key, value in request.POST.items():
if "quantityforgood_" in key:
pk = int(key.split('_')[1])
good = goods.get(pk=pk)
good.quantity = int(value)
good.save()
return HttpResponseRedirect(reverse_lazy("carts:cart_edit"))
elif action == "create_order":
return HttpResponseRedirect(reverse_lazy('order:create_order'))
else:
return HttpResponseRedirect(reverse_lazy("carts:cart_edit"))
class CartView(generic.DetailView):
template_name = 'carts/cart_edit.html'
model = models.Cart
def get_object(self, queryset=None):
cart_id = self.request.session.get('cart_id')
cart, created = models.Cart.objects.get_or_create(
pk=cart_id,
defaults={},
)
if created:
self.request.session['cart_id'] = cart.pk
book_id = self.request.GET.get('book_pk')
if book_id:
book = Book.objects.get(pk=int(book_id))
book_in_cart, flat_created = models.BooksInCart.objects.update_or_create(
cart=cart,
book=book,
defaults={
'price': book.price
}
)
if not flat_created:
q = book_in_cart.quantity + 1
book_in_cart.quantity = q
book_in_cart.price = book_in_cart.book.price * q
else:
book_in_cart.price = book.price
book_in_cart.save()
return cart
class DeleteGoodInCartView(generic.DeleteView):
model = models.BooksInCart
template_name = 'carts/delete_book_in_cart.html'
success_url = reverse_lazy("carts:cart_edit")
Order models:
User = get_user_model()
class CustomSession(Session):
cart = models.ForeignKey(
Cart,
on_delete=models.CASCADE
)
class Meta:
app_label = 'cart_id'
class Status(models.Model):
name = models.CharField(max_length=30)
def __str__(self):
return self.name
class Meta:
verbose_name = "Status"
verbose_name_plural = "Statuses"
class Order(models.Model):
user = models.ForeignKey(
User,
on_delete=models.PROTECT,
related_name='orders'
)
cart = models.OneToOneField(
Cart,
on_delete=models.PROTECT,
verbose_name="Cart"
)
status = models.ForeignKey(
Status,
on_delete=models.PROTECT
)
contact_info = models.TextField(
verbose_name="Contact info",
)
created = models.DateTimeField(
verbose_name="Created",
auto_now=False,
auto_now_add=True
)
updated = models.DateTimeField(
verbose_name="Updated",
auto_now=True,
auto_now_add=False
)
def __str__(self):
return self.contact_info
class Meta:
verbose_name = "Order"
verbose_name_plural = "Orders"
Cart views:
class CreateOrderView(generic.FormView):
form_class = forms.OrderCreateForm
template_name = 'order/create_order.html'
success_url = reverse_lazy("order:success")
def form_valid(self, form):
cart_id = self.request.session.get('cart_id')
cart, created = carts_models.Cart.objects.get_or_create(
pk=cart_id,
defaults={},
)
if created:
return HttpResponseRedirect(reverse_lazy('carts:cart_edit'))
info = form.cleaned_data.get('contact_info')
status = models.Status.objects.get(pk=1)
user = self.request.user
order = models.Order.objects.update_or_create(
cart=cart,
contact_info=info,
status=status,
user=user,
)
self.request.session.delete('cart_id')
if self.request.user.is_authenticated:
cart_id = self.request.session.get('cart_id')
customer1 = carts_models.Cart.objects.get(pk=cart_id)
customer1.customer = self.request.user
customer1.save()
return super().form_valid(form)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
cart_id = self.request.session.get('cart_id')
cart, created = carts_models.Cart.objects.get_or_create(
pk=cart_id,
defaults={},
)
context['object'] = cart
return context
def get_success_url(self) -> str:
del self.request.session['cart_id']
return super().get_success_url()
def success(requsest):
return render(requsest, 'order/success.html')
User profile views:
class ProfileView(generic.DetailView):
def get(self, request, *args, **kwargs):
user = get_object_or_404(AppUser, pk=kwargs['pk'])
return render(request, 'app_profiles/profile_view.html', context={
'user': user,
'order': Order.objects.filter(user=request.user),
})
User profile template:
<thead>
<tr>
<th scope="col">Goods</th>
<th scope="col">Quantity</th>
<th scope="col">Total price</th>
<th scope="col">Status</th>
<th scope="col">Created</th>
<th scope="col">Updated</th>
</tr>
</thead>
<tbody>
<tr>
{% for order in order.all %}
<td>{{ order.created }}</td>
<td>{{ order.updated }}</td>
<td>{{ order.status }}</td>
{% endfor %}
I'll try to answer your questions best i can based on what i think you're asking.
Your questions:
How to add all information from user cart (that was formed into the
order) to user's profile?
How to make user's data and order's/cart's data possible to change
in the user's profile?
How to display several user's orders in separate rows in HTML table
(cause in my template all orders are in one row)?
1: Make a foreign key relation from the cart to the user using Django's ORM.
Example:
# Create your models here.
class Someclass(models.Model):
user = models.CharField(max_length=255, default='none')
subject = models.CharField(max_length=255, default='none')
date = models.DateField(auto_now_add=True)
def __str__(self):
return self.subject
class Someotherclass(models.Model):
relation = models.ForeignKey(
"Someclass", on_delete=models.CASCADE, null=True, related_name='myFkRelation')
detail = models.TextField()
date = models.DateField(auto_now_add=True, null=True)
This way you can connect the a cart with a user.
2: Create a (or 2) function(s) in your views.py where you can edit the data.
Example (using 2 separate functions):
# views.py
def change(request, id):
something = Someclass.objects.get(id=id)
context = {
'something': something,
}
return render(request, 'change.html', context)
def addchange(request, id):
change = request.POST['change']
something = Someclass.objects.get(id=id)
something.subject = change
something.date = datetime.now()
something.save()
return redirect('index')
----------
# urls.py
path('view/<int:id>/change/', views.change, name='change'),
path('view/<int:id>/change/addchange/', views.addchange, name='addchange'),
# of course you can change the urls to what you want as long as you give the id with it as a parameter to the views function.
----------
# change.html
<form action="addchange/" method="post">
{% csrf_token %}
<div class="form-group">
<label for="change">Change: *</label>
<textarea rows="4" cols="100" type="text" class="form-control" id="change" name="change" required></textarea>
</div>
<br>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
3: Create a for loop within a HTML table (given the context in views, where a user has a relation with the cart)
Example:
# views.py
def index(request):
User = get_user_model()
users = User.objects.all()
context = {
'users': users
}
return render(request, "index.html", context)
----------
# index.html
<table class="table table-hover table-sm">
<thead>
<tr>
<th scope="col">Users</th>
<th scope="col">Items</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{user.name}}</td>
<td>{{user.item}}</td>
# or whatever you want
</tr>
{% endfor %}
</tbody>
</table>
Hopefully this answers some (maybe all?) of your questions.

Django: how to show related model fields in template

I'm new in Django 3.0 and I'm lost in this easy question, please help me.
I have 2 models:
class Product(models.Model):
fields...
class ProductType(models.Model):
product = models.ForeignKey(Product, related_name='product_types', on_delete=models.CASCADE)
color = models.Charfield(...)
In my template, I would like to show all the related product types and their fields to a specific product:
...
{% for product in products %}
{{ product.??? }}
{% endfor %}
Here is my view:
class ProductsView(ListView):
collection = None
model = Product
paginate_by = 6
template_name = 'shop/product/list.html'
context_object_name = 'products'
def get_queryset(self):
products = Product.objects.filter(available=True)
collection_name = self.kwargs['collection_name'] if 'collection_name' in self.kwargs else None
if collection_name:
collection = get_object_or_404(Collection, name=collection_name)
products = products.filter(collection=collection)
return products
def get_context_data(self):
context = super().get_context_data(**self.kwargs)
context['notification'] = Notification.objects.all()
if 'collection_name' in self.kwargs:
context['collection'] = get_object_or_404(Collection, name=self.kwargs['collection_name'])
context['collection_name'] = self.kwargs['collection_name']
context['collections'] = Collection.objects.all()
return context
Thank you
You access the related ProductTypes through a manager that has as name the value you specify as related_name=… [Django-doc], so in this case:
{% for product in products %}
{% for type in product.product_types.all %}
{{ type.color }}
{% endfor %}
{% endfor %}
To boost efficiency, you can fetch all related ProductTypes for the elements in the queryset with .prefetch_related(…) [Django-doc]:
class ProductsView(ListView):
# …
def get_queryset(self):
products = Product.objects.prefetch_related('product_types').filter(available=True)
collection_name = self.kwargs['collection_name'] if 'collection_name' in self.kwargs else None
if collection_name:
collection = get_object_or_404(Collection, name=collection_name)
products = products.filter(collection=collection)
return products

How do i group all expenses of the same category in django?

I am new to Django and trying to group and retrieve all expenses of the same category together and retrieve them in one "link like" table raw which when clicked can then display all the expenses in that category in another form.
I have these Models:
class Category(models.Model):
name = models.CharField(max_length=50)
class Meta:
verbose_name_plural = 'Categories'
unique_together = ("name",)
def __str__(self):
return self.name
class Expense(models.Model):
amount = models.FloatField()
date = models.DateField(default=now, )
description = models.TextField()
owner = models.ForeignKey(to=User, on_delete=models.CASCADE)
category = models.CharField(max_length=50)
def __str__(self):
return self.category
class Meta:
ordering = ['-date', '-pk']
homeView:
def home(request):
categories = Category.objects.all()
expenses = Expense.objects.filter(owner=request.user)
paginator = Paginator(expenses, 5)
page_number = request.GET.get('page')
page_obj = Paginator.get_page(paginator, page_number)
currency = UserPreference.objects.filter(user=request.user) # .currency
query = Expense.objects.values('category').annotate(total=Sum(
'amount')).order_by('category')
context = {
'expenses': expenses,
'page_obj': page_obj,
'currency': currency,
'query': query
}
return render(request, 'expenses/home.html', context)
mytemplate:
<div class="app-table">
<table class="table table-stripped table-hover">
<thead>
<tr>
<th>Amount</th>
<th>Category</th>
<th>Description</th>
<th>Date</th>
<th></th>
</tr>
</thead>
<tbody>
{% for myquery in query %}
<tr class="clickable-row" data-href="https://www.mavtechplanet.com/">
<td>{{myquery.category }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
I am trying to figure out things but are not coming out clearly. Any help is highly appreciated.
your template :
{% for myquery in query %}
<tr class="clickable-row" data-href="https://www.mavtechplanet.com/">
<td>{{myquery.category}}</td>
</tr>
{% endfor %}
add url to urls.py where in yourapp like this,
from youraapp.views import showCategoryExpenses
urlpatterns=[
.
.
.
path("/categoryExpenses/<pk:categoryPk>", showCategoryExpenses, name = "showCategoryExpenses"),
# if you save categories to expense with their name, you must change the <pk:categoryPk> to <slug:categoryName>
]
and add this view to yourapp.views like this:
def showCategoryExpenses(request, categoryPk): # if you save categories to expense with their name, change categoryPk to categoryName
expenses = Expense.objects.filter(category = categoryPk) # or category = categoryName
context = {
"expenses" : expenses
}
return render(request, "expenses/categoryExpenses.html", context)
and last, add this template to your template folder.. My English skills are not good but i try the solve and explain it. Maybe this is the answer you're looking for.
If you want show sum of all expenses in the category in this template, you can do it:
def showCategoryExpenses(request, categoryPk): # if you save categories to expense with their name, change categoryPk to categoryName
expenses = Expense.objects.filter(category = categoryPk) # or category = categoryName
expensesSum = 0
for expense in expenses:
expensesSum += expense.amount
context = {
"expenses" : expenses,
"expensesSum" : expensesSum
}
return render(request, "expenses/categoryExpenses.html", context)
Once you have categoryPk, you can try this way
from django.db.models import Sum
expenses = Expense.objects.filter(category = categoryPk).annotate(category_sum = Sum('amount')).values('category','category_sum')
# {'category':'ABC','category_sum':3000}
If you don't have any pk and want to annotate all objects then
expenses = Expense.objects.values('category').annotate(category_sum = Sum('amount')).values('category','category_sum')

Django calculate 2 values from model

Im fairly new to Django. Im using Django 2.
My model:
# Create your models here.
class Trade(models.Model):
quantity = models.IntegerField()
open_price = models.FloatField()
commision = models.FloatField()
exchange_rate_usdsek = models.FloatField()
stock = models.ForeignKey(Stock, on_delete=models.CASCADE)
user = models.ForeignKey(User, related_name='trades', on_delete=models.PROTECT)
open_date = models.DateTimeField(auto_now_add=True, null=True)
def get_absolute_url(self):
return reverse('trade:detail',kwargs={'pk': self.pk})
def __str__(self):
return self.stock.name
My view
class IndexView(generic.ListView):
template_name = 'trade/index.html'
context_object_name = 'all_trades'
#
def get_queryset(self):
return Trade.objects.all()
#return Order.objects.all().prefetch_related('items')
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
return context
index.html
<h3>All stocks</h3>
<table class="table">
<thead>
<tr>
<th>Stock</th>
<th>Amount</th>
<th>Open Price</th>
<th>Commision</th>
<th>USD/SEK</th>
<th>Total sek</th>
</tr>
</thead>
{% for trade in all_trades %}
<tr>
<td>{{trade.stock.name}}</td>
<td>{{trade.quantity}}</td>
<td>{{trade.open_price}} USD</td>
<td>{{trade.commision}} SEK</td>
<td>{{trade.exchange_rate_usdsek}}</td>
<td>{{open_price * quantity * exchange_rate_usdsek}}</td>
</tr>
{% endfor %}
</table>
Now on my index.html I want to calculate and display a value.
I make a calculation like this:
total_sek = open_price * quantity * exchange_rate_usdsek
Do I have to calculate this in the views.py or in the index.html?
Also how would I do this? I searched around and found something about filters but im not sure if that is the right way to do it
The easiest way is to just define this calculation as a property on the model:
class Trade(models.Model):
# ...
#property
def total_sek(self):
return self.open_price * self.quantity * self.exchange_rate_usdsek
at which point you can simply
<td>{{ trade.total_sek }}</td>
in the template,
and trade.total_sek in Python code as required too.

Iterate Foregin key options and assign it to each row in inlineformset

I have an inline formset where I need to assign the values of a ForeignKey field sequencially and univocally to each row of the inline formset. With the select default widget of a modelchoicefield everything works but I do not want the user to select any options but to be provided with a list of the options available in the foreignfield.
My inlineformset is the following:
class ResultadoForm(forms.ModelForm):
frequencia = forms.CharField(max_length=50)
tolerancia = forms.CharField(max_length=255)
def __init__(self, *args, **kwargs):
equipamento_id = kwargs.pop('equipamento_id', None)
super (ResultadoForm, self).__init__(*args, **kwargs)
self.fields['teste'].queryset = Teste.objects.filter(equipamento=equipamento_id).order_by('grupoteste', 'numeracao')
class Meta:
model = Resultado
exclude = ['actividade']
My view that renders the form:
#login_required()
def QAView(request, equipamento_id):
form = ActividadeForm()
form1 = ResultadoForm(equipamento_id)
equipamento = Equipamento.objects.get(id=equipamento_id)
testes_list = Teste.objects.filter(equipamento=equipamento_id)
FormSet = inlineformset_factory(Actividade, Resultado, form=ResultadoForm, fields='__all__', extra=len(testes_list))
formset = FormSet(form_kwargs={'equipamento_id': equipamento_id})
context = {'equipamento_id': equipamento_id, 'data':datetime.now(), 'equipamento': equipamento, 'form': form, 'testes_list': testes_list, 'formset': formset}
template = 'SFM/Equipamento/QA.html'
return render(request, template, context)
And in my template:
{% for r in formset %}
{{ r.id }}
{% for t in r.teste.field.queryset %}
<tr>
<td>{{ t}}</td>
<td>{{ t.frequencia }}</td>
<td>{{ t.tolerancia }}</td>
<td>{{ r.conforme }}</td>
</tr>
{% endfor %}
{% endfor %}
My Resultado Model:
class Resultado(models.Model):
CONFORME_CHOICES = (
("Sim", "Sim"),
("Não", "Não"),
)
teste = models.ForeignKey(Teste, null=True, blank=True, on_delete=models.CASCADE)
conforme = models.CharField(max_length=30, choices=CONFORME_CHOICES)
actividade = models.ForeignKey(Actividade, null=True, blank=True, on_delete=models.CASCADE)
def __str__(self):
return str(self.id)
With this template I get 4 rows. I should only get two. But I have two foreign key options so I am getting the two foreign options repeated.
Is it possible to assign one and only one foreign key to each row of this inlineformset? For example for the first row automatically assign the first foreign value and to the second row the second foreign value. Please help.