Flask Jinja2 templating when dealing with one - to- many relationship - flask

so I have created a flask app with three database models one for the users and the other two are parent and child models. The thing is when I am using jinja2 templating the parent table returns on one column none but the other show info. it happens that the last column is related to the child table and is supposed to return info from there. this is the code:
#parent
class Tenant(db.Model):
id = db.Column(db.Integer(), primary_key=True, nullable=False, autoincrement=True, unique=True)
name = db.Column(db.String(length=50), nullable=False)
phone_no = db.Column(db.String(length=50), nullable=False, unique=True)
house_no = db.Column(db.String(length=50), nullable=False, unique=True)
rent = db.relationship('Rent', backref='rent')
def __repr__(self):
return f'Tenant{self.id}'
#child
class Rent(db.Model):
id = db.Column(db.Integer(), primary_key=True, nullable=False, unique=True)
house_no = db.Column(db.String(length=30), nullable=False)
message = db.Column(db.Text(length=256))
payment = db.Column(db.Integer(), nullable=False)
date = db.Column(db.Date, nullable=False)
rent_id = db.Column(db.Integer(), db.ForeignKey('tenant.id'))
def __repr__(self):
return f'Rent{self.id}'
//Rent routes
#app.route('/', methods=['GET', 'POST'])
#login_required
def index():
form = RentForm()
if form.validate_on_submit():
user_create = Rent(house_no=form.house_no.data,
payment=form.payment.data,
message=form.message.data,
date=form.date.data,
)
db.session.add(user_create)
db.session.commit()
return redirect(url_for('rent'))
else:
return render_template('index.html', form=form)
//query- rent
#app.route('/rent_paid', methods=['GET', 'POST'])
#login_required
def rent():
rent = Rent.query.all()
return render_template('rent.html', rent=rent)
//Tenant route
#app.route('/new', methods=['GET', 'POST'])
#login_required
def new():
form = TenantsForm()
if form.validate_on_submit():
user_create = Tenant(name=form.name.data,
phone_no=form.phone_no.data,
house_no=form.house_no.data,
)
db.session.add(user_create)
db.session.commit()
return redirect(url_for('tenants'))
else:
return render_template('tenant.html', form=form)
//query Tenants
#app.route('/tenants', methods=['GET', 'POST'])
#login_required
def tenants():
tenant = Tenant.query.all()
return render_template('table.html', tenant=tenant)
HTML template
<tbody>
{% for tenant in tenant %}
<tr>
<td scope='row'>{{tenant.id}}</td>
<td>{{tenant.name}}</td>
<td>{{tenant.phone_no}}</td>
<td>{{tenant.house_no}}</td>
<td>Ksh {{tenant.rent.}}</td>
so in the HTML template, I want the last column {{ tenant.rent }} to return the actual amount e.g(500USD) but it either returns the rent id or none. how do I code to give the exact amount in the column row.

Ok so in your model every Tenant can have many Rents. Every Rent has a payment amount associated with it.
When you say you want to show the total amount I am not sure whether you want to show the total payment amount collected from this Tenant up to this point or if just want to show the amount of the last payment the Tenant has made.
You have a lot of options on how to send the information to the template, here I just sent the template a dict where the key is the tenant.id and the value is the total rent up to this point.
#app.route('/tenants', methods=['GET', 'POST'])
#login_required
def tenants():
tenants = Tenant.query.all()
rent = {}
for tenant in tenants:
rents = Tenants.query.filter_by(id=tenant.id).first().rent.all()
total = 0
for rent in rents:
total += rent.payment
rent[tenant.id] = total
return render_template('table.html', tenants=tenants, rent=rent)
And in the template you would do something like this.
{ for tenant in tenants }
<tr>
<td scope='row'>{{ tenant.id }}</td>
<td>{{ tenant.name }}</td>
<td>{{ tenant.phone_no }}</td>
<td>{{ tenant.house_no }}</td>
<td>{{ rent[tenant.id] }}</td>
</tr>
{ endfor }
I am not sure if this is the exact correct syntax since it is hard for me to try it out, but you get the idea. You would also do the same thing if you just want to display the latest payment and not the total, you just need to order the rent by date.

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.

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')

How to get the total items in Context to reflect in Template in Django List View

I am working on an E-commerce project and I got lost in the middle to generate some kind of a report to get the total no. of a sold item after payment is made.
When the order is paid ordered = models.BooleanField(default=False) become True
I have tried to add the context with the total sold but it didn't work so I kept it in the code below but commented it.
Here is the Item models.py
class Item(models.Model):
title = models.CharField(max_length=100)
def __str__(self):
return self.title
Here is the OrderItemmodels.py
class OrderItem(models.Model):
ordered = models.BooleanField(default=False)
item = models.ForeignKey(Item, on_delete=models.CASCADE)
Here is the Order
class Order(models.Model):
items = models.ManyToManyField(OrderItem)
ordered = models.BooleanField(default=False)
Here is the views.py
class DesignerOnlyPostListView(ListView):
model = Item
template_name = "designer_only_posts.html"
context_object_name = 'items'
paginate_by = 6
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Item.objects.filter(designer=user).order_by('-timestamp')
def get_context_data(self, **kwargs):
comments = Comment.objects.all()
# total_sold = Order.objects.all()
context = super().get_context_data(**kwargs)
context["total_comments"] = comments.count()
# context["total_sold"] = total_sold.count()
return context
Here is the template
{% for item in items %}
<tr>
<td>No. of Sold:</td>
<td>{{ item.total_sold.all.count }}</td>
</tr>
{% endfor %}
Seem in your template, you want to display total sold for each "item". In my opinion, i will add a method to Item, this approaching help you add custom filters as you want, something like this:
class Item(models.Model):
# ...
def count_sold(self):
return self.order_set.filter(ordered=True).count()
And in view:
<td>{{ item.count_sold }}</td>
Hope it will help you.

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.

Django: Formset saved data retrieved single raw

I am using Inlineformsets for an app. Data gets saved but when I retrieve data, main form data is correctly retrieved however in case of childmodel, only first raw is retrieved from DB.
Views.py
#login_required
def invoice_detail(request,pk):
invoice_detailmain = serviceinvoice.objects.get(pk=pk)
if invoice_detailmain.user != request.user:
raise PermissionDenied
invoiceitems =invoice_detailmain.serviceitems1.filter(pk=pk,user=request.user)
return render(request,'account/invoicedetail.html',{'invoice_detailmain':invoice_detailmain,
'invoiceitems':invoiceitems})
template.html
{% for items in invoiceitems %}
<tr><td>{{ items.Product }}</td>
<td>{{ items.UOM }}</td>
<td>{{ items.Quantity }}</td>
<td>{{ items.Rate }}</td>
<td>{{ items.Tax_rate }}</td></tr>
{% endfor %}
models.py
class serviceinvoice(models.Model):
user=models.ForeignKey(settings.AUTH_USER_MODEL,related_name='invoice')
invoice_number=models.PositiveIntegerField()
#invoice_no = models.CharField(max_length = 500, default = increment_invoice_number, null = True, blank = True)
invoice_date = models.DateField()
invoice_receivable=models.ForeignKey(Receivables,null=True)
total_amount=models.DecimalField(decimal_places=2,max_digits=20)
total_amountwithtax=models.DecimalField(decimal_places=2,max_digits=20)
company_det=models.ForeignKey(Company,related_name='companydetails')
class Meta:
unique_together = (("user", "invoice_number"),)
ordering=('-invoice_number',)
def __str__(self):
return self.invoice_number
def get_absolute_url(self):
return reverse('invoice:editinvoice', args=[self.invoice_number])
class serviceinvoiceitems(models.Model):
user=models.ForeignKey(settings.AUTH_USER_MODEL,related_name='serviceinvoiceitems')
invoice_number=models.ForeignKey(serviceinvoice,related_name='serviceitems1')
Product=models.CharField(max_length=1000,null=True)
UOM=models.CharField(max_length=100,null=True)
Quantity=models.FloatField(null=True)
Rate=models.FloatField(null=True)
Tax_rate=models.FloatField(null=True)
def __str__(self):
return self.invoice_number
I believe that your filtering is incorrect. Instead of
invoiceitems = invoice_detailmain.serviceitems1.filter(pk=pk,user=request.user)
you should be using
invoiceitems = invoice_detailmain.serviceitems1.filter(user=request.user)
Although I'm baffled to why you have a primary key from both serviceinvoice and serviceinvoiceitems to your user model. It would probably be enough for you to specify
invoiceitems = invoice_detailmain.serviceitems1.all()