How do I change how a CharField is displayed? - python

I have a model defined as such in my models.py file:
class Tutor(models.Model):
FirstName = models.CharField(max_length=50)
LastName = models.CharField(max_length=50)
Email = models.EmailField(max_length=100)
PhoneNumber = models.CharField(max_length=10)
RequestedHours = models.DecimalField(max_digits=3, decimal_places=1)
def __str__(self):
return str(self.FirstName + " " + self.LastName)
I want to create a page that displays all of this information in a table that is easy to read quickly. I have managed to do so for the most part, but I have always found 10-digit phone numbers to be more difficult to read without separators. Currently, the display output looks like this:
And the code for the table looks like this:
<div class="table-background">
<table class="table table-striped table-bordered table-hover">
<thead>
<tr class="bg-info">
<th colspan="2">Tutor</th>
</tr>
</thead>
<tbody>
<tr>
<th>First Name</th>
<th>Last Name</th>
</tr>
<tr>
<td>{{ tutor.FirstName }}</td>
<td>{{ tutor.LastName }}</td>
</tr>
<tr>
<th>Email</th>
<th> Phone Number</th>
</tr>
<tr>
<td>{{ tutor.Email }}</td>
<td>{{ tutor.PhoneNumber }}</td>
</tr>
<tr>
<th colspan="2">Requested Hours</th>
</tr>
<tr>
<td colspan="2">{{ tutor.RequestedHours }}</td>
</tr>
</tbody>
</table>
Is there any way for me to modify the output of the CharField PhoneNumber so that I can get it to display as 000-000-0000?

You should really format it in your view, but on the template side:
{% with pn=tutor.PhoneNumber|to_list %}
{{ pn|slice:":3"|join:'' }}-{{ pn|slice:"3:6"|join:'' }}-{{ pn|slice:"6:"|join:''}}
{% endwith %}
Your could also modify your model:
class Tutor(models.Model):
FirstName = models.CharField(max_length=50)
LastName = models.CharField(max_length=50)
Email = models.EmailField(max_length=100)
PhoneNumber = models.CharField(max_length=10)
RequestedHours = models.DecimalField(max_digits=3, decimal_places=1)
def __str__(self):
return str(self.FirstName + " " + self.LastName)
def get_formatted_phone(self):
return '{}-{}-{}'.format(self.PhoneNumber[:3],
self.PhoneNumber[3:6],self.PhoneNumber[6:])
Then in your template:
{{ tutor.get_formatted_phone }}
But this sort of stuff really belongs in the view.

Related

Two separate query lists in the same view in Django

I want to have 2 separate query lists on the same page but with different filters. First list must be played and the second one paused. This is what I came up with in my views.py file but it throws an error like this: UnboundLocalError at /
local variable 'formset_paused' referenced before assignment
def home_view(request):
#campaigns in progress
queryset = Campaign.objects.filter(is_active=True, completion_percent__lt=100)
if request.method == "POST":
form_type = request.POST.get('id')
if form_type == 'campaign_status':
formset = CampaignStatusFormSet(
request.POST, request.FILES,
queryset=queryset,
)
formset.save()
else:
formset = CampaignStatusFormSet(queryset=queryset)
campaigns_and_forms = list(zip(queryset, formset))
#paused campaigns
queryset_paused = Campaign.objects.filter(is_active=False, completion_percent__lt=100)
if request.method == "POST":
form_type_paused = request.POST.get('id_paused')
if form_type_paused == 'campaign_status_paused':
formset_paused = CampaignStatusFormSet(
request.POST, request.FILES,
queryset=queryset_paused,
)
formset_paused.save()
else:
formset_paused = CampaignStatusFormSet(queryset=queryset_paused)
paused_campaigns_and_forms = list(zip(queryset_paused, formset_paused))
context = {
'formset': formset,
'formset_paused': formset_paused,
'campaigns_and_forms': campaigns_and_forms,
'paused_campaigns_and_forms': paused_campaigns_and_forms,
}
return render(request, 'campaigns_in_progress.html', context)
Here are the tables in my template where I make those 2 lists.
<table class="table table-striped table-hover table-bright table-bordered align-middle">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Nazwa</th>
<th scope="col">Temat</th>
<th scope="col">Nadawca</th>
<th scope="col">Procent Realizacji</th>
<th scope="col">Start Kampani</th>
<th scope="col">Stan</th>
</tr>
</thead>
<tbody>
<form method="post" id="campaign_status"> {% csrf_token %}
<input type='hidden' value='campaign_status' name='id'>
{{ formset.management_form }}
{% for campaign, form in campaigns_and_forms %}
<tr>
<td>{{ campaign.campaign_id }}</td>
<td>{{ campaign.name }}</td>
<td>{{ campaign.topic }}</td>
<td>{{ campaign.sender }}</td>
<td>
<div class="progress">
<div class="progress-bar" role="progressbar" style="width: {{campaign.completion_percent}}%;" aria-valuenow="{{campaign.completion_percent}}" aria-valuemin="0" aria-valuemax="100">{{campaign.completion_percent}}%</div>
</div>
</td>
<td>{{ campaign.start_date }}</td>
<td>{{ form.as_p }}</td>
</tr>
{% endfor %}
</form>
</tbody>
</table>
<h4>Kampanie zatrzymane</h4>
<table class="table table-striped table-hover table-bright table-bordered align-middle">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Nazwa</th>
<th scope="col">Temat</th>
<th scope="col">Nadawca</th>
<th scope="col">Procent Realizacji</th>
<th scope="col">Start Kampani</th>
<th scope="col">Stan</th>
</tr>
</thead>
<tbody>
<form method="post" id="campaign_status_paused"> {% csrf_token %}
<input type='hidden' value='campaign_status_paused' name='id_paused'>
{{ formset_paused.management_form }}
{% for campaign, form in paused_campaigns_and_forms %}
<tr>
<td>{{ campaign.campaign_id }}</td>
<td>{{ campaign.name }}</td>
<td>{{ campaign.topic }}</td>
<td>{{ campaign.sender }}</td>
<td>
<div class="progress">
<div class="progress-bar" role="progressbar" style="width: {{campaign.completion_percent}}%;" aria-valuenow="{{campaign.completion_percent}}" aria-valuemin="0" aria-valuemax="100">{{campaign.completion_percent}}%</div>
</div>
</td>
<td>{{ campaign.start_date }}</td>
<td>{{ form.as_p }}</td>
</tr>
{% endfor %}
</form>
</tbody>
</table>
Before adding the second query and the second table everything worked.
So basically what I want is when the is_active filter is equal to True the campaign should be in the first list. If it's False then it should jump to the other. Can anyone help me?
If request.method == "POST" but form_type_paused != 'campaign_status_paused', the formset_paused variable is never assigned. But you try to access it here:
...
paused_campaigns_and_forms = list(zip(queryset_paused, formset_paused))
...
That is where the error comes from.

group articles by day in django

I'm trying to group articles in each day, image blow will show you how it looks like now
I have 3 records that have their date on top right of them, but as you can see each one of them is separated, I want to show record on this date in one group and tomorrow if I added more records shows on that date group,
my codes:
Models.py:
class Form(models.Model):
food_type = models.ForeignKey(FoodTypes, blank=False, null=True, on_delete=CASCADE)
calorie = models.CharField(max_length=50)
protein = models.ForeignKey(Protein, blank=False, null=True, on_delete=CASCADE)
carbohydrate = models.ForeignKey(Carbohydrate, blank=False, null=True, on_delete=CASCADE)
drinks = models.ForeignKey(Drinks, blank=False, null=True, on_delete=CASCADE)
fruits = models.CharField(max_length=50)
note = models.CharField(max_length=200, blank=True)
date = models.DateField(default=datetime.date.today)
views.py:
def requests(request):
lists = Form.objects.all().order_by('-date')
context = {
'lists': lists
}
return render(request, 'form/requests_list.html', context)
template file:
{% for lists in lists %}
<div class="req-list">
<h6>{{ lists.date }}</h6>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">نوع غذا</th>
<th scope="col">کالری</th>
<th scope="col">پروتئین</th>
<th scope="col">کربوهیدرات</th>
<th scope="col">نوشیدنی</th>
<th scope="col">میوه</th>
<th scope="col">توضیحات</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">{{ lists.id }}</th>
<td>{{ lists.food_type }}</td>
<td>{{ lists.calorie }}</td>
<td>#{{ lists.protein }}</td>
<td>#{{ lists.carbohydrate }}</td>
<td>#{{ lists.drinks }}</td>
<td>#{{ lists.fruits }}</td>
<td>#{{ lists.note }}</td>
</tr>
</tbody>
</table>
</div>
{% endfor %}
You can try using the {% ifchanged %} tag as mentioned in this answer: https://stackoverflow.com/a/3986324/4151233
Following code is not tested:
{% for lists in lists %}
{% ifchanged lists.date %}
<div class="req-list">
<h6>{{ lists.date }}</h6>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">نوع غذا</th>
<th scope="col">کالری</th>
<th scope="col">پروتئین</th>
<th scope="col">کربوهیدرات</th>
<th scope="col">نوشیدنی</th>
<th scope="col">میوه</th>
<th scope="col">توضیحات</th>
</tr>
</thead>
<tbody>
{% endifchanged %}
<tr>
<th scope="row">{{ lists.id }}</th>
<td>{{ lists.food_type }}</td>
<td>{{ lists.calorie }}</td>
<td>#{{ lists.protein }}</td>
<td>#{{ lists.carbohydrate }}</td>
<td>#{{ lists.drinks }}</td>
<td>#{{ lists.fruits }}</td>
<td>#{{ lists.note }}</td>
</tr>
{% ifchanged lists.date %}
</tbody>
</table>
</div>
{% endifchanged %}
{% endfor %}

Trying to set non-model field in model form to some other field that exists in the model

I have a form that I need to add an extra field to - and it needs to have an initial value of the data model's time field (called seg_length_time).
I didn't expect the code to work because I know I need to convert the time into a string from the model, but I can't seem to figure out how to set the field to begin with. The current code returns an error 'SegmentForm' object has no attribute 'pk'. I have tried id , and a couple of other things but haven't correctly referenced the specific object. For instance - I tried Segemt.object.values('seg_length_time') but then I get all of the seg_length_time key pairs.
See forms.py code below:
import re
from datetime import datetime,timedelta
class SegmentForm(forms.ModelForm):
str_seg_length_time = forms.CharField(max_length=8)
class Meta:
model = Segment
exclude = ('seg_length_time_delta',
#'seg_length_time',
'seg_run_time',
'seg_run_time_delta' ,
'seg_remaining_time',
'seg_remaining_time_delta',
'program_id',
'audit_user',
)
def __init__(self, *args, **kwargs):
obj = Segment.objects.get(id=self.instance.pk)
self.fields['str_seg_length_time'] = obj.seg_length_time
super().__init__(*args, **kwargs)
def clean_str_seg_length_time(self):
str_seg_time = self.cleaned_data['str_seg_length_time']
if (re.search("[^\d:]", str_seg_time) ): # check that only digits and : are in our time string
raise ValidationError("Segment Time: There are illegal characters in segment time. Valid format:[HH:MM:SS].")
if (re.search(".*:.*:.*:.*", str_seg_time) ): # check for more than two : signs
raise ValidationError("Segment Time: There are too many colons. Valid format:[HH:MM:SS].")
if ( re.search("^\d+$", str_seg_time) ): # time defaults to minutes if not specified
str_seg_time = "00:" + str_seg_time + ":00"
if ( re.search("^\d*:\d*$", str_seg_time) ): # time defaults to minutes/seconds if not specified
str_seg_time = "00:" + str_seg_time
try:
t = datetime.strptime(str_seg_time,"%H:%M:%S")
except:
raise ValidationError('Segment Time : Could not convert time to HH:MM:SS - Invalid Time')
self.seg_length_time = t
return str_seg_time
def form_valid(self, form):
form.instance.audit_user = self.request.user
return super().form_valid(form)
The model.py is below:
from django.db import models
from django.urls import reverse
from datetime import datetime, timedelta
class Program(models.Model):
air_date = models.DateField(default="0000-00-00")
air_time = models.TimeField(default="00:00:00")
service = models.CharField(max_length=10)
block_time = models.TimeField(default="00:00:00")
block_time_delta = models.DurationField(default=timedelta)
running_time = models.TimeField(default="00:00:00",blank=True)
running_time_delta = models.DurationField(default=timedelta)
remaining_time = models.TimeField(default="00:00:00",blank=True)
remaining_time_delta = models.DurationField(default=timedelta)
title = models.CharField(max_length=190)
locked_flag = models.BooleanField(default=False)
deleted_flag = models.BooleanField(default=False)
library = models.CharField(null=True,max_length=190,blank=True)
mc = models.CharField(null=True,max_length=64)
producer = models.CharField(null=True,max_length=64)
editor = models.CharField(null=True,max_length=64)
remarks = models.TextField(null=True,blank=True)
audit_time = models.DateTimeField(auto_now=True)
audit_user = models.CharField(null=True,max_length=32)
def convert_to_delta(self,time_in):
#print("Lengthof time_in = %s" % len(time_in))
hold_time = time_in.strftime("%H:%M:%S")
t = datetime.strptime(hold_time,"%H:%M:%S")
return(timedelta(hours=t.hour, minutes=t.minute, seconds=t.second))
def calculate_time(self):
block_time_delta = self.convert_to_delta(self.block_time)
total_run_time_delta = timedelta(minutes=0)
for segs in self.segments.all():
total_run_time_delta += segs.seg_length_time_delta
segs.seg_run_time_delta = total_run_time_delta
segs.seg_run_time = f"{segs.seg_run_time_delta}"
segs.seg_remaining_time_delta = block_time_delta - total_run_time_delta
segs.seg_remaining_time = f"{abs(segs.seg_remaining_time_delta)}"
super(Segment,segs).save()
self.running_time_delta = total_run_time_delta
self.running_time = f"{self.running_time_delta}"
self.remaining_time_delta = self.block_time_delta - total_run_time_delta
self.remaining_time = f"{abs(self.remaining_time_delta)}"
def calculate_sequence(self):
for index,segs in enumerate(self.segments.all(),1):
segs.sequence_number = index
super(Segment,segs).save()
def save(self, *args, **kwargs):
self.calculate_sequence()
self.calculate_time()
super().save(*args,**kwargs)
def __str__(self):
return f"{self.pk} : {self.title}"
def get_absolute_url(self):
#return reverse('program_detail', args=[str(self.id)])
return reverse('program-detail', kwargs={'pk': self.pk})
class Segment(models.Model):
class Meta:
ordering = ['sequence_number']
program_id = models.ForeignKey(Program,
on_delete=models.CASCADE,
related_name='segments', # link to Program
)
sequence_number = models.DecimalField(decimal_places=2,max_digits=6,default="0.00")
title = models.CharField(max_length=190)
bridge_flag = models.BooleanField(default=False)
seg_length_time = models.TimeField(null=True,default=None, blank=True)
seg_length_time_delta = models.DurationField(default=timedelta)
seg_run_time = models.TimeField(default="00:00:00",blank=True)
seg_run_time_delta = models.DurationField(default=timedelta)
seg_remaining_time = models.TimeField(default="00:00:00",blank=True)
seg_remaining_time_delta = models.DurationField(default=timedelta)
author = models.CharField(max_length=64,null=True,default=None,blank=True)
voice = models.CharField(max_length=64,null=True,default=None,blank=True)
library = models.CharField(max_length=190,null=True,default=None,blank=True)
summary = models.TextField()
audit_time = models.DateTimeField(auto_now=True)
audit_user = models.CharField(null=True,max_length=32)
def save(self, *args, **kwargs):
self.seg_length_time_delta =
self.program_id.convert_to_delta(self.seg_length_time)
super().save(*args,**kwargs)
self.program_id.save()
def get_absolute_url(self):
return reverse('program_detail', kwargs={'pk': self.program_id.pk} )
def save( self, *args, **kwargs):
super().save(*args,**kwargs)
self.program_id.save()
def __str__(self):
return f"{self.title}"
The View is the following:
class ProgramMixView(LoginRequiredMixin,UpdateView):
model = Segment
template_name = 'program_mix_view.html'
form_class = SegmentForm
context_object_name = 'program_edit'
def get_context_data(self, **kwargs):
context = super(ProgramMixView,self).get_context_data(**kwargs)
cur_segment = Segment.objects.get(id=self.kwargs['pk'])
context['program_info'] = Program.objects.get(id=cur_segment.program_id.pk)
return context
class SegmentCreateView(LoginRequiredMixin,CreateView):
model = Program
template_name = 'segment_create_view.html'
form_class = SegmentForm
context_object_name = 'program_seg_create'
And the actual template program_mix_template.html
{% if program_info %}
<TABLE ID="pro-table" WIDTH="100%">
<!-- <TABLE BORDER="0" TABLE_LAYOUT="fixed" WIDTH="100%"> -->
<TR BGCOLOR="#15C1B5">
<TD ALIGN="Right">Program Title:</TD><TD ALIGN="Left">{{ program_info.title|truncatechars:30 }}</TD>
<TD ALIGN="Right">Library:</TD><TD ALIGN="Left"> {{ program_info.library }}</TD>
<TD ALIGN="Right">Service Bureau:</TD><TD ALIGN="Left"> {{ program_info.service }}</TD>
</TR>
<TR BGCOLOR="#15C1B5">
<TD ALIGN="Right">Program Id:</TD><TD ALIGN="Left"> {{ program_info.pk }}</TD>
<TD ALIGN="Right">Air Date:</TD><TD ALIGN="Left"> {{ program_info.air_date }}</TD>
<TD ALIGN="Right">Air Time</TD><TD ALIGN="Left"> {{ program_info.air_time|time:"H:i:s" }}</TD>
</TR>
<TR BGCOLOR="#15C1B5">
<TD ALIGN="Right">Producer:</TD><TD ALIGN="Left"> {{ program_info.producer }}</TD>
<TD ALIGN="Right">Editor:</TD><TD ALIGN="Left"> {{ program_info.editor }}</TD>
<TD ALIGN="Right">MC:</TD><TD ALIGN="Left"> {{ program_info.mc }}</TD>
</TR>
<TR BGCOLOR="#15C1B5">
<TD BGCOLOR="#99CCFF" ALIGN="Right">Duration:</TD>
<TD BGCOLOR="#99CCFF" ALIGN="Left"> {{ program_info.block_time|time:"H:i:s" }}</TD>
<TD BGCOLOR="#8DF1BF" ALIGN="Right">Rem. Time:</TD>
<TD BGCOLOR="#8DF1BF" ALIGN="Left"> {{ program_info.remaining_time|time:"H:i:s" }}</TD>
<TD BGCOLOR="#CC99CC" ALIGN="Right">Run Time:</TD>
<TD BGCOLOR="#CC99CC" ALIGN="Left"> {{ program_info.running_time|time:"H:i:s" }}</TD>
</TR>
<TR BGCOLOR="#15C1B5">
<TD ALIGN="Right">Remarks:</TD><TD ALIGN="Left" COLSPAN="5"><PRE>{{ program_info.remarks|truncatechars:180 }}</TD>
</PRE></TD>
</TR>
</TABLE>
{% if program_info.segments.all %}
<TABLE BORDER="0" TABLE_LAYOUT="fixed" WIDTH="100%">
<TR BGCOLOR="#B0B0FF">
<TD ALIGN="Center"> #</TD>
<TD ALIGN="Center">Segment Title</TD>
<TD ALIGN="Center">Summary</TD>
<TD ALIGN="Center">Library</TD>
<TD ALIGN="Center">Author</TD>
<TD ALIGN="Center">Voice</TD>
<TD ALIGN="Center">Segment time</TD>
<TD BGCOLOR="#CC99CC" ALIGN="Center">Run time</TD>
<TD BGCOLOR="#8DF1BF" ALIGN="Center">Rem. time</TD>
</TR>
{% for segments in program_info.segments.all %}
{% if program_edit.pk == segments.id %}
<form method="post" action="">
{% csrf_token %}
<TR BGCOLOR="#B0B0FF">
<TD ALIGN="Left" VALIGN="Top" WIDTH="35">{{ form.sequence_number }}</TD>
<TD ALIGN="Left" VALIGN="Top">{{ form.title }}</TD>
<TD ALIGN="Left" VALIGN="Top"><PRE>{{ form.summary }}</PRE></TD>
<TD ALIGN="Left" VALIGN="Top">{{ form.library }}</TD>
<TD ALIGN="Left" VALIGN="Top">{{ form.author }}</TD>
<TD ALIGN="Left" VALIGN="Top">{{ form.voice }}</TD>
<TD ALIGN="CENTER" VALIGN="Top" WIDTH="10">{{ form.str_seg_length_time }}</TD>
</TR>
<TR>
<td><input type="submit" Value="Update"></td>
</tr>
</form>
{% if form.errors %}
<!-- Error messaging -->
<div id="errors">
<div class="inner">
<p>There were some errors in the information you entered. Please correct the following:</p>
<ul>
{% for field in form %}
{% if field.errors %}
<li> {{ field.errors|striptags }}</li><br>
{% endif %}
{% endfor %}
</ul>
</div>
</div>
{% endif %}
{% else %}
<tr BGCOLOR="#B0B0FF">
<TD ALIGN="Left" VALIGN="Top" WIDTH="35">{{ segments.sequence_number }}</TD>
<TD ALIGN="Left" VALIGN="Top">{{ segments.title }}</TD>
<TD ALIGN="Left" VALIGN="Top"><PRE>{{ segments.summary|truncatechars:40 }}</PRE></TD>
<TD ALIGN="Left" VALIGN="Top">{{ segments.library }}</TD>
<TD ALIGN="Left" VALIGN="Top">{{ segments.author }}</TD>
<TD ALIGN="Left" VALIGN="Top">{{ segments.voice }}</TD>
<TD ALIGN="CENTER" VALIGN="Top" WIDTH="10">{{ segments.seg_length_time|time:"H:i:s" }}</TD>
<TD BGCOLOR="#CC99CC" ALIGN="CENTER" VALIGN="Top" WIDTH="10">{{ segments.seg_run_time|time:"H:i:s" }}</TD>
<TD BGCOLOR="#8DF1BF" ALIGN="CENTER" VALIGN="Top" WIDTH="10">{{ segments.seg_remaining_time|time:"H:i:s" }}</TD>
</TR>
{% endif %}
{% endfor %}
</TABLE>
{% else %}
No Segments
{% endif %}
{% endif %}
The field (str_seg_length_time)shows up on the form as nothing (Blanks), if I don't do some sort of initial code to set it from the model. Can I get the object by id, or dont I already have the object in the form at initialization time? I assume I will need to do some sort of strfdatetime() call to turn it into a string, but I don't know how to get the seg_length_time from the model to begin with...
Thanks.
(P.S. I don't just put the field from the model in the form because I am trying to change the way the default time entered is handled (HH:MM is the default - I need mm:ss or Minutes by default) i.e. 05:00 should be 5 minutes, NOT 5 hours)...
Your forms __init__ method is written as:
def __init__(self, *args, **kwargs):
obj = Segment.objects.get(id=self.instance.pk)
self.fields['str_seg_length_time'] = obj.seg_length_time
super().__init__(*args, **kwargs)
Let's note the problems here. Firstly obj = Segment.objects.get(id=self.instance.pk), I can see that you use this same form in a CreateView, obviously when an instance is not yet created it will not have a primary key, giving you an error. Secondly self.fields['str_seg_length_time'] this will give you an error because you are calling this before even calling super().__init__ which is what initializes self.fields. Thirdly = obj.seg_length_time what even are you trying to do here? Do you want to set the initial value of the field? Because currently you replace the field you declare with this value instead.
What is the solution? It appears you just want to provide an initial value for the field, you don't need to override __init__ for this simple purpose. Going even further you just want the input in a custom format? No need to do all this for that, you can simply pass the input_formats [Django docs] kwarg for that to the field. The formats in this list will be tried in order and the first matching one will be used:
class SegmentForm(forms.ModelForm):
seg_length_time = forms.TimeField(input_formats=['%H:%M:%S', '%M:%S', '%M']) # Set custom formats here
class Meta:
model = Segment
exclude = ('seg_length_time_delta',
# 'seg_length_time',
'seg_run_time',
'seg_run_time_delta' ,
'seg_remaining_time',
'seg_remaining_time_delta',
'program_id',
'audit_user',
)
What if you want to use some custom formats for all time fields by default? Simply set the TIME_INPUT_FORMATS setting [Django docs] in settings.py:
TIME_INPUT_FORMATS = [
'%H:%M:%S',
'%M:%S',
'%M'
]
In your initialization of the SegmentForm the keyword self will refer to the SegmentForm class, not the model instance. That's why you get the error that the form does not have pk. To refer to the model instance use self.instance, which will refer to the instance of the underlying model.
def __init__(self, *args, **kwargs):
obj = Segment.objects.get(id=self.instance.pk)
self.fields['str_seg_length_time'] = obj.seg_length_time
super().__init__(*args, **kwargs)

How to display related objects through DetailView

I'm trying to display a product, and available brands(has product as ForeignKey) for that product through DetailView. Based on Django documentation and similar answers on stackoverflow, I tried below code but it doesn't work. Product details are rendering but names of brands are not. I've checked through django-admin, that the brands the products are present in the database.
Could someone please help.
Models.py
class Product(models.Model):
name = models.CharField(max_length=256)
price = models.IntegerField()
class Brand(models.Model):
name = models.CharField(max_length=256)
product = models.ForeignKey(Product,on_delete=models.PROTECT,related_name='Brands')
Views.py
class ProductDetailView(DetailView):
model = Product
Urls.py
path('detail/<int:pk>/',views.ProductDetailView.as_view(),name='product_detail'),
product_detail.html
<table class="table table-bordered table-hover table-secondary">
<tr>
<th class="bg-secondary th-customer-detail">Name</th>
<td>{{ product.name }}</td>
</tr>
<tr>
<th class="bg-secondary th-customer-detail">Price</th>
<td>{{ product.price }}</td>
</tr>
</table>
<br>
<ul>
{% for brand in product.brand_set.all %}
<li>{{ brand.name }}</li>
{% endfor %}
</ul>
You can do it this way
class ProductDetailView(DetailView):
model = Product
context_object_name = 'product'
def get_context_data(self,**kwargs):
context = super(ProductDetailView,self).get_context_data(**kwargs) #returns a dictionary of context
primary_key = self.kwargs.get('pk')
brands = Brand.objects.filter(product = primary_key)
new_context_objects = {'brands':brands}
context.update(new_context_objects)
return context
<table class="table table-bordered table-hover table-secondary">
<tr>
<th class="bg-secondary th-customer-detail">Name</th>
<td>{{ product.name }}</td>
</tr>
<tr>
<th class="bg-secondary th-customer-detail">Price</th>
<td>{{ product.price }}</td>
</tr>
</table>
<br>
<ul>
{% for brand in brands %}
<li>{{brand.name}}</li>
{% endfor %}
</ul>

Django Sum of Multiplied Values

I am trying to show the total weight of assets I have presented on a table.
Each Asset has a weight and a quantity and I can present the total weight of each asset as below:
def by_item_weight(self):
"""This Method gives the total value of the asset. Weight * Qnty"""
total = self.asset_quantity * self.asset_weight
return total
What I would like to do is take the total item weight of each asset (result of qnty * weight) and present the over all weight of all assets.
I have tried to make various combinations of the below to return the sum of the by_item_weight 'column':
Asset.objects.all().annotate(total=Sum('by_item_weight')
But I understand this will not work as by-item-weight is not defined in the model Asset.
I am presenting the table via a for loop as below on my assetslist.html template:
{% block content %}
<table class="well table table-striped text-center">
<thead>
<tr class="text-center">
<th class="text-center">Asset ID:</th>
<th class="text-center">Asset Name:</th>
<th class="text-center">Asset Quantity:</th>
<th class="text-center">Asset Weight / kg:</th>
<th class="text-center">Total Weight / kg:</th>
<th class="text-center">Asset Owner:</th>
</tr>
</thead>
<tbody>
<tr class="text-center">
{% for asset in object_list %}
<td>{{ asset.id }}</td>
<td>{{ asset.asset_name }}</td>
<td>{{ asset.asset_quantity }}</td>
<td>{{ asset.asset_weight }}</td>
<td>{{ asset.by_item_weight }}</td>
<td>{{ asset.asset_owner }}</td>
</tr>
{% endfor %}
I am new to Python and am having trouble finding a way to capture the value of each asset.by_item_weight which If I could capture in a list I presume I could them sum the list and show the result.
My Models
class Asset(models.Model):
asset_name = models.CharField(max_length=30)
asset_quantity = models.IntegerField(default=0)
asset_weight = models.IntegerField(default=0)
asset_owner = models.ForeignKey(
'AssetOwner',
on_delete=models.CASCADE,
) # This should be a Foreign Key Drop down of AssetOwners owner_name.
def by_item_weight(self):
"""This Method gives the total value of the asset. Weight * Qnty"""
total = self.asset_quantity * self.asset_weight
return total
def __str__(self):
return '{}'.format(self.asset_name)
return 'Asset Quantity: {}'.format(self.asset_quantity)
return 'Asset Weight: {}'.format(self.asset_weight)
return 'Asset Owner: {}'.format(self.asset_owner)
return 'Asset Owner: {}'.format(self.asset_owner)
Any help would be much appreciated.
UPDATED:
No Errors now, but still not able to show/display the value of sum_total
New Template
{% extends "personal/header.html" %}
{% block content %}
<h1 class='text-center'>This is the full asset list not split by owner</h1></br>
<table class="well table table-striped text-center">
<thead>
<tr class="text-center">
<th class="text-center">Asset ID:</th>
<th class="text-center">Asset Name:</th>
<th class="text-center">Asset Quantity:</th>
<th class="text-center">Asset Weight / kg:</th>
<th class="text-center">Total Weight / kg:</th>
<th class="text-center">Asset Owner:</th>
</tr>
</thead>
<tbody>
<tr class="text-center">
{% for asset in object_list %}
<td>{{ asset.id }}</td>
<td>{{ asset.asset_name }}</td>
<td>{{ asset.asset_quantity }}</td>
<td>{{ asset.asset_weight }}</td>
<td>{{ asset.by_item_weight }}</td>
<td>{{ asset.asset_owner }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p class="" style="">Total Weight In Stock : {{ asset.sum_total }}</p>
<p class="text-center">{% include "sam/includes/backtosam.html" %}</p>
{% endblock %}
New Models
from __future__ import unicode_literals
from django.db import models
from django.db.models import Sum, F, Count
from django.db.models import Max
from django.db.models import ExpressionWrapper
from django.db.models import Aggregate
class Asset(models.Model):
asset_name = models.CharField(max_length=30)
asset_quantity = models.IntegerField(default=0)
asset_weight = models.IntegerField(default=0)
asset_owner = models.ForeignKey(
'AssetOwner',
on_delete=models.CASCADE,
) # This should be a Foreign Key Drop down of AssetOwners owner_name.
def by_item_weight(self):
"""This Method gives the total value of the asset. Weight * Qnty"""
total = self.asset_quantity * self.asset_weight
return total
def sum_total(self):
assets = Asset.objects.all().annotate(
total_weight=ExpressionWrapper(F('asset_quantity') * F('asset_weight'),output_field=IntegerField))
the_sum = assets.aggregate(total=Sum('total_weight'))
return the_sum
def __str__(self):
return '{}'.format(self.asset_name)
return 'Asset Quantity: {}'.format(self.asset_quantity)
return 'Asset Weight: {}'.format(self.asset_weight)
return 'Asset Owner: {}'.format(self.asset_owner)
return 'Asset Owner: {}'.format(self.asset_owner)
return 'Total Weight of Assets: {}'.format(self.assets)
Updated view
from django.shortcuts import render
from django.http import HttpResponse
from django.core.cache import cache
from django.db.models import Sum, F
def get_total_weight(self):
total_weight = cache.get('total_weight',-1)
if total_weight == -1:
total_weight = Asset.objects.annotate(total_weight=F('asset_quantity')*F('asset_weight')).aggregate(total=Sum('total_weight'))
# this is tested
cache.set('total_weight',total_weight)
return total_weight
def index(request):
return render(request, 'sam/index.html')
def assetslist(request):
return render(request,'sam/assetslist.html',{'total_weight': get_total_weight}, assets = Asset.objects.all())
I suspect there is an issue with the above assetslist method which I am not apparently calling.
Template
{% extends "personal/header.html" %}
{% block content %}
<h1 class='text-center'>This is the full asset list not split by owner</h1></br>
<table class="well table table-striped text-center">
<thead>
<tr class="text-center">
<th class="text-center">Asset ID:</th>
<th class="text-center">Asset Name:</th>
<th class="text-center">Asset Quantity:</th>
<th class="text-center">Asset Weight / kg:</th>
<th class="text-center">Total Weight / kg:</th>
<th class="text-center">Asset Owner:</th>
</tr>
</thead>
<tbody>
<tr class="text-center">
{% for asset in object_list %}
<td>{{ asset.id }}</td>
<td>{{ asset.asset_name }}</td>
<td>{{ asset.asset_quantity }}</td>
<td>{{ asset.asset_weight }}</td>
<td>{{ asset.by_item_weight }}</td>
<td>{{ asset.asset_owner }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p class="" style="">Total Weight In Stock : {{ get_total_weight }}</p>
<p class="" style="">Total Weight In Stock : {{ assetslist }}</p>
<!-- <table class="well table table-striped text-center">
<thead>
<tr class="text-center">
<th class="text-center"></th>
<th class="text-center"></th>
<th class="text-center"></th>
<th class="text-center"></th>
<th class="text-center">Total Weight / kg:</th>
</tr>
</thead>
<tbody>
<tr class="text-center">
{% for sum_weight in object_list %}
<td></td>
<td></td>
<td></td>
<td></td>
<td>{{ asset.sum_total }}</td>
</tr>
{% endfor %}
</tbody>
</table> -->
<p class="text-center">{% include "sam/includes/backtosam.html" %}</p>
{% endblock %}
Thanks for any input/suggestions.
Further UPDATE:
I have adjusted the view to the below:
from django.core.cache import cache
from django.db.models import Sum, F
def get_total_weight(self):
total_weight = cache.get('total_weight',-1)
if total_weight == -1:
total_weight = Asset.objects.annotate(total_weight=F('asset_quantity')*F('asset_weight')).aggregate(total=Sum('total_weight'))
# this is tested
cache.set('total_weight',total_weight)
return total_weight
render(request,'template_name',{'total_weight': get_total_weight, assets = Asset.objects.all() } )
I am getting errors on the assets = Asset.objects.all() } ) = sign. SyntaxError: invalid syntax
I presume that render needs to be in its own function?
UPDATE:
I have updated my views and moved the def from the models.py
my views.py file is as below
def total_weight(request):
assets = Asset.objects.all().annotate(
total_weight=ExpressionWrapper(F('asset_quantity') * F('asset_weight'),
output_field=IntegerField() ))
return render(request, 'sam/index.html')
def sum_total(request):
the_total = assets.aggregate(total=Sum('total_weight'))
return render(request, 'sam/assetlist.html')
def index(request):
return render(request, 'sam/index.html')
def by_item_weight(self):
"""This Method gives the total value of the asset. Weight * Qnty"""
total = self.asset_quantity * self.asset_weight
return total
def get_total_weight(self):
total_weight = Asset.objects.filter(by_item_weight__isnull=True).aggregate(Sum('by_item_weight'))
Asset.objects.annotate(total_weight=F('asset_quantity')*F('asset_weight')).aggregate(total=Sum('total_weight'))
def __str__(self):
return '{}'.format(self.asset_name)
return '{}'.format(self.total_weight)
assetlist.html
There is JS below that I tried to implement to resolve this issue which did not work either.
I feel like I am missing something on the assetlist.html in calling the values of the def in the views.py.
{% extends "personal/header.html" %}
{% block content %}
<h1 class='text-center'>This is the full asset list not split by owner</h1></br>
<table id="sum_table" class="well table table-striped text-center">
<thead>
<tr class="text-center titlerow">
<td class="text-center">Asset ID:</td>
<td class="text-center">Asset Name:</td>
<td class="text-center">Asset Quantity:</td>
<td class="text-center">Asset Weight / kg:</td>
<td class="text-center">Total Weight / kg:</td>
<td class="text-center">Asset Owner:</td>
</tr>
</thead>
<tbody>
<tr class="text-center">
{% for asset in object_list %}
<td><a href="/sam/assets/{{ asset.id }}">{{ asset.id }}</></td>
<td>{{ asset.asset_name }}</td>
<td>{{ asset.asset_quantity }}</td>
<td>{{ asset.asset_weight }}</td>
<td class="rowDataSd">{{ asset.by_item_weight }}</td>
<td><a href="/sam/owners/">{{ asset.asset_owner }}</></td>
</tr>
{% endfor %}
<tr class="totalColumn">
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class="totalCol">Total: {{ asset.get_total_weight }}</td>
<td class=""></td>
</tr>
</tbody>
</table>
<p>Hope this is full ( {{ this_view }} )?</p>
<p class="text-center">{% include "sam/includes/backtosam.html" %}</p>
<!--
<script>
var total = 0;
$('#sum_table tr td.rowDataSd').each(function() {
total += parseInt($(this).text());
});
$('#sum_table td.totalCol').text("total: " + total);
</script>
-->
{% endblock %}
UPDATE - 3rd July 2016
from __future__ import unicode_literals
from django.db import models
from django.db.models import Sum, F, Count
from django.db.models import Max
from django.db.models import ExpressionWrapper
from django.db.models import Aggregate
from django.contrib.auth.models import User
class Asset(models.Model):
asset_name = models.CharField(max_length=30)
asset_quantity = models.IntegerField(default=0)
asset_weight = models.IntegerField(default=0)
total_assets_weight = models.IntegerField(default=0)
asset_owner = models.ForeignKey(
'AssetOwner',
on_delete=models.CASCADE,
) # This should be a Foreign Key Drop down of AssetOwners owner_name.
def by_item_weight(self):
"""This Method gives the total value of the asset. Weight * Qnty"""
total = self.asset_quantity * self.asset_weight
return total
def total_weight(self):
assets = Asset.objects.all().annotate(
total_weight=ExpressionWrapper(F('asset_quantity') * F('asset_weight'),
output_field=IntegerField()))
the_total = assets.aggregate(total=Sum('total_weight'))
return the_total
template
{% extends "personal/header.html" %}
{% block content %}
<h1 class='text-center'>This is the full asset list not split by owner</h1></br>
<table id="sum_table" class="well table table-striped text-center">
<thead>
<tr class="text-center titlerow">
<td class="text-center">Asset ID:</td>
<td class="text-center">Asset Name:</td>
<td class="text-center">Asset Quantity:</td>
<td class="text-center">Asset Weight / kg:</td>
<td class="text-center">Total Weight / kg:</td>
<td class="text-center">Asset Owner:</td>
</tr>
</thead>
<tbody>
<tr class="text-center">
{% for asset in object_list %}
<td><a href="/sam/assets/{{ asset.id }}">{{ asset.id }}</td>
<td>{{ asset.asset_name }}</td>
<td>{{ asset.asset_quantity }}</td>
<td>{{ asset.asset_weight }}</td>
<td class="rowDataSd">{{ asset.by_item_weight}}</td>
<td><a href="/sam/owners/">{{ asset.asset_owner }}</td>
</tr>
{% endfor %}
{% for total in object_list %}
<tr class="totalColumn">
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class=""></td>
<td class="totalCol">Total: {{ total.total_weight }}</td>
<td class=""></td>
</tr>
</tbody>
</table>
{% endfor %}
<p class="text-center">{% include "sam/includes/backtosam.html" %}</p>
{% endblock %}
You could use the ExpressionWrapper() (mostly Django 1.8+)
assets = Asset.objects.all().annotate(
total_weight=ExpressionWrapper(F('asset_quantity') * F('asset_weight'),
output_field=IntegerField() ))
That should give you the total weight for each object, i.e. quantity times weight.
Now, you should be able to get a sum from all the total_weights.
Edit: Now you can use Aggregate to get the total
assets.aggregate(total=Sum('total_weight'))
{'total': 1234.5678}

Categories

Resources