how can I summarize checked column with django-tables2 - django

I have a table with some columns. One column is a checkboxcolumn.
So, how can I summarize the "quantity" columns that checkboxcolumn is checked?
I appreciate if someone can help me.

The django-tables2 docs on CheckBoxColumn mention:
You might expect that you could select multiple checkboxes in the rendered table and then do something with that. This functionality is not implemented. If you want something to actually happen, you will need to implement that yourself.
So django-tables2 doesn't have something build in, we have to write something ourselves. That makes this more of a JavaScript/HTML question, but anyway, let's see if we can create a table for this model:
class Country(models.Model):
name = models.CharField(max_length=100)
population = models.PositiveIntegerField()
This is a basic table, adding an empty footer to the table by adding a footer argument to one of the columns. We will use this footer later to put the calculated population sum in.
class CheckboxTable(tables.Table):
select = tables.CheckBoxColumn(empty_values=(), footer='')
population = tables.Column(attrs={'cell': {'class': 'population'}})
class Meta:
model = Country
template_name = 'django_tables2/bootstrap.html'
fields = ('select', 'name', 'population')
Now, the template. I used jQuery, to quickly create a function to calculate the sum. This function is executed every time the change event is fired on one of the checkboxes, and on page load.
{% load django_tables2 %}
{% render_table table %}
<script src="//code.jquery.com/jquery.min.js"></script>
<script>
$(function () {
// calculate the sum of the population column for the selected rows.
function update_sum() {
var sum = 0;
// loop over each checkbox within <tbody>
$('tbody input[type=checkbox]').each(function () {
// if the checkbox is checked
if ($(this).is(':checked')) {
// find the contents of the population column, convert
// it to integer and add it to the total.
sum += parseInt($(this).parents('tr').find('.population').html());
}
});
// add the sum to the footer cell in the population column.
$('tfoot .population').html(sum);
}
// update the sum after each change
$('tbody input[type=checkbox]').on('change', update_sum);
// update the sum initially.
update_sum();
});
</script>
I added code similar to this to the django-tables2 example application.

Related

Django-tables2 Row Selection

How do I select a row from a table generated from django-tables2? I'd prefer not to have to select a row by checking a checkbox cell (selection = tables.CheckBoxColumn(accessor='pk')) in the row.
Depending on what you want to do, you can format the cell as an href, so that the user clicks on it to get the appropriate action- or detail-view. You can accomplish this easily by defining a method on the model to return the appropriate text, including self.id or other row-identification from the instance.
Example:
class Customer( models.Model):
...
#property
def td_inspect(self):
dest = reverse('customers:detail', args=(self.id,) )
return format_html('<td><a class="NoUnderline AddWayBack style-inspect" href="{dest}">🔎 </a></td>', dest=dest ) # 1f50e is magnify glass
You can also work client-side using JQuery or suchlike, passing the necessary id information via something like <td data_id={id}...

How can I display a view based on a database value

I want to use one view (URL) for 2 conditions.
In my application, "owners" create daily plans for routes they manage, and update the plans/results throughout the day.
class DailyRoute(models.Model):
owner = models.ForeignKey(Owner)
route = models.ForeignKey(Route)
driver = models.ForeignKey(Driver)
stops = models.PositiveSmallIntegerField(default=0, ...
on_duty_hours = models.TimeField(default=datetime.time...
date = models.Datefield(...
finalized = models.Boolean(...
Essentially, there are 3 database conditions, 2 of which I'm trying to use for a view:
No database row for the date/route. This means that the owner hasn't created a plan for the day. (e.g. if DailyRoute.objects.filter(stage = 0).count() == 0:...)
A database row for the date/route with finalized = False. This means that a plan has been created for the day for the date/route pair and can be edited.
(e.g. DailyRoute.objects.filter(stage = 0).count() >0 )
A database row for the date/route with finalized = True. This means that a plan/updates for the day are complete and no further edits are allowed. (e.g. if DailyRoute.objects.filter(stage = 1).count() > 0:...)
As a daily activity, I want the user to go to a URL (e.g. /account/daily) and be presented with one of these views:
a) If no owner routes (point 1 above), provide an "Add" button to add a row for each owned route for a specific date (i.e. next business day). On success, display the DailyRoute List as per b) below.
b) If owner routes exist in the 0 state, display the list (i.e. don't display the "Add" button.)
Note that an owner can never have more than one date / route pair in the 0 state. They must complete the day's activities on 0 state routes before they can plan the next days routes.
Alternatively, it would be acceptable to have View with a disabled "Add" button and a list of routes, if routes are in the 0 state. Conversely, the "Add" button would be enabled and an empty list of routes, if no routes are found.
Right now I can do this with separate URLs, one for add daily plan, the other to display a list of routes in stage=0, but it would be much better to do this all on the same URL.
I can't figure out how/where to add a filter to a view to do this.
Thanks in advance for guidance.
Well, sounds like a simple if/else statement would do the trick:
class YourView(View):
if first_condition:
return render('first_template')
else if second_condition:
return render('second_template')
else:
return render('third_template')
Or, if you want to use one template, you can do this if/else inside your template. I also suggest that you send that flag from your view:
class YourView(View):
...
routes_exist = DailyRoute.objects.filter(stage = 0).exists()
return render('template.html', {'routes_exist': routes_exist})
Then in your template:
{% if routes_exsist %}
<button> Add </button>
{% endif %]
Hope it helps!

Is it possible to apply the primary key of the row as an id attribute to each <td> when using django-tables2?

With django-tables2, i am trying to set the id of each element of my model as an "attribute" of every single <td> corresponding to a specific <tr>.
I'm using a dict in the Column Attributes definition like so:
class MainTable(tables.Table):
id = tables.Column()
Client = tables.Column(attrs={'td': {
'data-name': 'Client',
'data-type': 'text',
'data-pk': lambda record: record.pk,
'data-url': 'path/to/url',
'data-placeholder': 'New Client',
'data-title': 'New Client' }})
Every attribute is applying correctly except the 'data-pk'. Is there a way to get the primary key inside the dict? Or any other way to set this attribute using django-tables2 ?
Currently, you cannot do that on columns, but you can on rows:
class Table(tables.Table):
class Meta:
row_attrs = {
'data-pk': lambda record: record.pk
}
Which might make more sense anyway, since a record is mapped to a row, not to a single table cell in a row.
I can see the use for computable attrs with arguments on table columns, so if you decide you need it, you are welcome to open a Pull request.

Doctrine QueryBuilder summary functions in Symfony entity repositories?

Is there a way I can use min() (or equivalent) in Doctrine's QueryBuilder?
The page I'm working on shows a table of invoices for the current (or impersonated) user. It has a row for each of their invoices and has nested collapsible rows of any associated payments (see below image)
My issue is that in my twig template I need to be able to be identify at the Invoice-level if any of it's Payments have not been approved. If any payment is not approved then I need to flag both the payment and the Invoice row it relates to. For example, in the below example, "Invoice #1" is yellow because it has a nested "Payment #2" record that is yellow (not approved):
To get access to this at the Invoice-level I have been trying to do it through my InvoiceRepository but I'm unclear if/how this can be done using QueryBuilder. I somehow need anyToBeApproved (which isn't an attribute of my Invoice entity) to be available, and still have the Payment information also included:
select a.*, min(b.approved) as anyToBeApproved
from Invoice a
left join Payment b
on a.invoice_id=b.id
where a.member_id=$Uid
group by a.id
My controller:
$invoices = $em->getRepository('AppBundle:Invoice')->OutstandingForUser($this->getUser()->getId());
My QueryBuilder:
public function OutstandingForUser($Uid)
{
return $this->createQueryBuilder('i')
->select('i')
->leftJoin('i.member_id','m')
->leftJoin('i.payment_ids','p')
->andwhere('m.member_id = :MemberId' )
->setParameter('MemberId',$Uid)
->getQuery()
->getResult();
}
I don't know why this hasn't been answered yet, but you should be able to use those aggregate functions just fine.
The following aggregate functions are allowed in SELECT and GROUP BY
clauses: AVG, COUNT, MIN, MAX, SUM
See the documentation: http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#aggregate-functions
You can use aggregate functions inside of querybuilder ->select() OK.
I'd rewrite your function as something like:
public function OutstandingForUser($Uid)
{
return $this->createQueryBuilder('i')
->select('i, min(p.approved) as anyToBeApproved')
->leftJoin('i.member','m', 'WITH', 'm.member_id = :MemberId') // this works better with not actually constraining your left join, otherwise you might as well just use innerJoin
->leftJoin('i.payment','p')
->groupBy('i.id')
->setParameter('MemberId',$Uid)
->getQuery()
->getResult();
}
Here's an alternative approach, given you seem to want all the invoice rows anyway.
Change your fetch function to:
public function OutstandingForUser($Uid)
{
return $this->createQueryBuilder('i')
->select('i, p, m') // may as well pre-fetch all entities
->join('i.member','m') // member is surely mandatory? so no left join required
->leftJoin('i.payment','p') // payments may not be present, so left join
->where('m.member_id = :MemberId')
->setParameter('MemberId',$Uid)
->getQuery()
->getResult();
}
Then in your invoice entity do something like
public function hasUnapprovedPayments () {
foreach ($this->getPayments() as $payment) {
if (!$payment->approved()) {
return true;
}
}
return false;
}
public function hasPayments () {
return !$this->getPayments()->isEmpty(); // assuming doctrine collection
}
These functions will be pretty fast as you have already hydrated the entities in the function above.
You could also add a similar isFullyPaid method that loops through payments, but I would personally make that a bool or datetime on Invoice that is set when the invoice is completely paid.
Then in twig you can do stuff like:
{% for invoice in invoices %}
{% if invoice.hasUnapprovedPayments %} do something {% endif %}
{% if invoice.hasPayments %} do something {% endif %}
{# loop through all your payments etc #}
{% endfor %}
Which should make it pretty easy to get the layout you want.

Django - show sums of a table of records

I have a table which has a list of invoices and their details:
class Invoice(models.Model):
invoiceNum = models.CharField(etc...)
invoiceDate = models.DateField(etc...)
customerID = models.ForeignKey(etc...)
isPaid = models.CharField(etc...)
The Invoice records do not hold the actual invoice total. Instead, an invoice's total is made up of several Invoice_Line_Item records, held in another table:
class Invoice_Line_Item(models.Model):
invNum = models.ForeignKey(Invoice)
itemName = models.CharField(etc...)
itemPrice = models.DecimalField(etc...)
I have a webapp that shows all the invoices in a big HTML table, along with all the details of that invoice on the table's tr row. Details such as, Invoice Date, Invoice Number, Customer ID, all come from that Invoice table. There are hundreds of invoices to display in this HTML table.
What I would like to do is also show each invoice's total value - which is the sum of all the line items. However, I can't think of a simple way to acomplish this since the invoice details and the line items that make up the invoice's total are in two different tables.
One way I thought is to pass the entire Invoice_Line_Item querySet into the HTML template, then for each invoice displayed in a table tr, I could iterate over the entire Invoice_Line_Item querySet, adding up all the line items that match the current invoice. This, however, seems hugely inefficient.
Any better ideas on how to do this?
Thanks!
One word: Aggregation
Invoice_Line_Item.objects.filter(invNum=invoice).aggregate(Sum('itemPrice'))
https://docs.djangoproject.com/en/dev/topics/db/aggregation/
Another way is to store the total in Invoice and update it whenever you change a related Invoice_Line_Item
One more word: annotate.
from django.models import Sum
Invoice.objects.filter( .. ).annotate( InvTotal=Sum( 'invoice_line_number__itemPrice' ) )
InvTolal becomes a new attribute of Invoice object, you can use it in template the same way as invoiceNum or invoiceDate.
With this approach you do not have to pass any additional data structures to your template, only a QuerySet of Invoices.
Please note:
Argument of Sum is a string, which is a concatenation of the name of related model converted to lowercase, than double '_', and than the name of a field in related model.