getting number of days between twoo dates - django

I don't figure out how to get the number of days betwen twoo dates:
this is the models I have :
class Reservation(models.Model):
"""A typical class defining a reservation model."""
# Fields
id_reservation = models.BigAutoField(primary_key=True)
start_date = models.DateField(auto_now=False, auto_now_add=False)
end_date = models.DateField(auto_now=False, auto_now_add=False)
client_id = models.ForeignKey(Client, on_delete=models.CASCADE)
chambre_id = models.ForeignKey(Chamber, on_delete=models.CASCADE)
# Metadata
class Meta:
ordering = ['-end_date']
# Methods
def get_absolute_url(self):
"""Returns the url to access a particular instance of MyModelName."""
return reverse('model-detail-view', args=[str(self.id_reservation)])
def __str__(self):
return self.id_reservation
and try to calculate the days between the end_date and the start_date in my generic view:
class ReservationsListView(generic.ListView):
"""ReservationsListView: produce a list of all Reservations """
model = Reservation
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['periode'] = Reservation.end_date-Reservation.start_date
return context
can please help doing this.

You can subtract the two date objects, and then obtain the .days attribute, so:
class Reservation(models.Model):
# …
#property
def days(self):
return (self.end_date - self.start_date).days
This will thus return the difference in days, so that means that if we calculate the difference between today and yesterday, this will result in one day:
>>> (date(2021, 4, 28) - date(2021, 4, 27)).days
1
If you want to take both days into account, you increment the result with 1:
class Reservation(models.Model):
# …
#property
def days(self):
return (self.end_date - self.start_date).days + 1
You can thus retrieve the number of days from a Reservation object with:
my_reservation.days

Related

unsupported operand type(s) for -: 'DateField' and 'DateField'

I am working on creating a contract model with Django and I came cross on how to get the time duration from the start_date to the end_date??
class Contract(models.Model):
name = models.CharField(max_length=100)
price = models.IntegerField(max_length=10)
start_date = models.DateField(auto_now_add=True)
end_date = models.DateField(auto_now_add=True)
duration = models.IntegerField(end_date - start_date) # how get the duration by hours
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
If you want to calculate a field in your model one good approach is to do that in an overridden save method.
Substracting one datetime from another results in a timedelta object. I am converting this here into seconds, assuming that is what you wanted.
class Contract(models.Model):
...
duration = models.IntegerField()
....
def save(self, *args, **kwargs):
self.duration = (self.end_date - self.start_date).total_seconds()
super().save(*args, **kwargs)
I would advise not to store this in a field: several ORM queries like for example .update(…) [Django-doc] circumvent the .save(…) method [Django-doc], so that means that there are ways to update the start_date, or end_date without the duration being updated properly.
You can add a property that will simply determine the duration when necessary, indeed:
from datetime import timedelta
class Contract(models.Model):
name = models.CharField(max_length=100)
price = models.IntegerField(max_length=10)
start_date = models.DateField(auto_now_add=True)
end_date = models.DateField(auto_now_add=True)
created_at = models.DateTimeField(auto_now_add=True)
#property
def duration(self):
return (self.end_date - self.start_date) // timedelta(hours=1)
def __str__(self):
return self.name

Querying other Model in class-based view produces error

I have two tables, in one of which the possible items with their properties are recorded, in the other the stock levels of these respective items are recorded.
class itemtype(models.Model):
item_id = models.IntegerField(primary_key=True)
item_name = models.CharField(max_length=100)
group_name = models.CharField(max_length=100)
category_name = models.CharField(max_length=100)
mass = models.FloatField()
volume = models.FloatField()
packaged_volume = models.FloatField(null=True)
used_in_storage = models.BooleanField(default=False, null=True)
class Meta:
indexes = [
models.Index(fields=['item_id'])
]
def __str__(self):
return '{}, {}'.format(self.item_id, self.item_name)
class material_storage(models.Model):
storage_id = models.AutoField(primary_key=True)
material = models.ForeignKey(itemtype, on_delete=models.PROTECT)
amount_total = models.IntegerField(null=True)
price_avg = models.FloatField(null=True)
amount = models.IntegerField(null=True)
price = models.FloatField(null=True)
timestamp = models.DateTimeField(default=timezone.now)
def __str__(self):
return '{}, {} avg.: {} ISK'.format(self.material, self.amount, self.price)
I have a ModelForm based on the table material_storage, in which a checkbox indicates whether transport costs should be included or not.
In the form_valid() method of this ModelForm class the calculations are performed. To do so, I have to retrieve the volume per unit of the given item to use it for my transport cost calculations. Trying to geht that value the way shown below leads to an error I don't really understand.
class MaterialChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return obj.item_name
class NewAssetForm(forms.ModelForm):
material = MaterialChoiceField(models.itemtype.objects.filter(used_in_storage= True))
needs_transport = forms.BooleanField(required=False)
def __init__(self, *args, **kwargs):
super(NewAssetForm, self).__init__(*args, **kwargs)
self.fields['amount'].widget.attrs['min'] = 1
self.fields['price'].widget.attrs['min'] = 0.00
class Meta:
model = models.material_storage
fields = (
'material',
'amount',
'price',
)
widgets = {
'material': forms.Select(),
}
class NewItemView(FormView):
template_name = 'assetmanager/newasset.html'
form_class = forms.NewAssetForm
success_url = '/storage/current'
def form_valid(self, form):
unit_volume = itemtype.objects.values('packaged_volume').filter(item_id=form.cleaned_data['material'])[0]['packaged_volume']
I believe that this has something to do with querying a different model than specified in the form, but I don't understand what exactly is the problem. Especially the fact, that running the exact same query in the django shell returns the correct value does not really help to understand what is going wrong here. Could somebody please tell me how to get the desired value the correct way?
Change last line from:
unit_volume = itemtype.objects.values('packaged_volume').filter(item_id=form.cleaned_data['material'])[0]['packaged_volume']
to:
unit_volume = itemtype.objects.values('packaged_volume').filter(item_id=form.cleaned_data['material'].item_id)[0]['packaged_volume']
The error says, you are giving Item instance to the query, where is item_id asked.

Many DB queries for string representation of model object

I have this models:
class Country(models.Model):
name = models.CharField(max_length=250)
def __str__(self):
return str(self.name)
class City(models.Model):
name = models.CharField(max_length=250)
country = models.ForeignKey(Country, default=None, blank=True)
def __str__(self):
return str(self.name)
class Airport(models.Model):
name = models.CharField(max_length=250)
city = models.ForeignKey(City, default=None, blank=True)
def __str__(self):
return "{0} - {1} - {2}".format(self.city, self.city.country, self.name)
class Tour(models.Model):
title = models.CharField(max_length=200)
tour_from = models.ForeignKey(Airport)
tour_to = models.ForeignKey(Airport)
def __str__(self):
return str(self.title)
For string representation of Airport Django sends many requests to DB:
302.06 ms (591 queries including 586 similar and 586 duplicates )
Queries screenshot:
At tour/create page I have a ModelForm for creating a tour and Django sends these queries for displaying form.
forms.py:
class TourCreateForm(forms.ModelForm):
class Meta:
model = Tour
fields = ['title', 'tour_from', 'tour_to']
views.py:
class DashboardTourCreate(CreateView):
model = Tour
template_name = "dashboard/tour/create.html"
form_class = TourCreateForm
def get_context_data(self, **kwargs):
context = super(DashboardTourCreate, self).get_context_data(**kwargs)
context['page_name'] = ['tour', 'tour-index']
context['page_title'] = "Create Tour"
return context
How I can reduce queries count?
Root Cause
def __str__(self):
return "{0} - {1} - {2}".format(self.city, self.city.country, self.name)
When the tour_to and tour_from fields are rendered as <option> in the <select> widget the Airport.__str__ method is called. Because Airport.__str__ has self.city.county and both of these are ForeignKey's, the Django ORM issues a query to grab the airports city and the citys country.
And it does this for every single Airport that is an <option> which means the problem will get progressively worse the more Airport's that are added.
Solution
Leverage select_related[1]. select_related will tell the Django ORM to pull in the related fields ('city', 'county') whenever it grabs an Airport.
class TourCreateForm(forms.ModelForm):
class Meta:
model = Tour
fields = ['title', 'tour_from', 'tour_to']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['tour_from'].queryset = Airport.objects.select_related(
'city__country',
)
self.fields['tour_to'].queryset = Airport.objects.select_related(
'city__country',
)
[1] https://docs.djangoproject.com/en/2.1/ref/models/querysets/#select-related
As f-string is a string literal expressions evaluated at run time link, this might be faster that other string format but i am not fully sure. I am expecting following modification may reduce the over all time.
class Airport(models.Model):
name = models.CharField(max_length=250)
city = models.ForeignKey(City, default=None, blank=True)
def __str__(self):
return f"{self.city} - {self.city.country} - {self.name}"
I fix this issue by adding Queryset to forms.py:
class TourCreateForm(BaseForm):
airports = Airport.objects.select_related('city', 'city__country').all()
tour_from = forms.ModelChoiceField(queryset=airports)
tour_to = forms.ModelChoiceField(queryset=airports)
But I think this is not correct!

optimise django sql query

I'm using Django 2.x.
I have two models
class AmountGiven(models.Model):
contact = models.ForeignKey(Contact, on_delete=models.PROTECT)
amount = models.FloatField(help_text='Amount given to the contact')
interest_rate = models.FloatField(blank=True, default=None, null=True)
given_date = models.DateField(default=timezone.now)
total_due = models.FloatField(blank=True, default=0.0, editable=False)
class AmountReturned(models.Model):
amount_given = models.ForeignKey(AmountGiven, on_delete=models.CASCADE, blank=True)
amount = models.FloatField()
return_date = models.DateField(default=date.today)
Use case
There can be multiple records of the amount given to a contact
There can be multiple records of the returned amount for an amount given
Now, I want to get total_due amount for a particular contact. This includes
total_payable = total_given + interest
total_due = total_payable - total_returned
To calculate total_due and interest, I have defined few property methods in the AmountGiven model.
#property
def interest_to_pay(self):
if self.interest_rate:
simple_interest_amount = ...
return simple_interest_amount
return 0
#property
def total_payable(self):
return self.amount + self.interest_to_pay
#property
def amount_due(self):
total_due = self.total_payable - self.total_returned
self.total_due = total_due
self.save()
return total_due
#property
def total_returned(self):
returned_amount = self.amountreturned_set.aggregate(total_returned=Sum('amount'))['total_returned']
if not returned_amount:
returned_amount = 0
return returned_amount
In Contact model, there is a property method to get the total due amount for the contact.
#property
def amount_due(self):
total_due = 0
for due in self.amountgiven_set.all():
total_due += due.amount_due
return total_due
Query
ContactSerializer
class ContactMinSerializer(serializers.ModelSerializer):
class Meta:
model = Contact
fields = (
'id', 'first_name', 'amount_due', 'created', 'modified'
)
Since amount_due property is being used in the ContactSerializer, amount_due property is called everytime a contact is call and thus results in nested DB queries.
How can I optimise the above scenario in the application to reduce the DB queries while getting contact or list of contacts? Specially two properties amount_due and total_returned.
amount_due() updates the total_due field in the table, every time it is called.
Edit 2
class ContactViewSet(LoggingMixin, viewsets.ModelViewSet):
serializer_class = ContactMinSerializer
def get_queryset(self):
return Contact.objects.filter(user=self.request.user).annotate(
total_due=Sum(
F('amountgiven_set__total_payable')
- F('amountgiven_set__total_returned')
)
).order_by('first_name')
You're looking for annotations.
Your viewset should define a queryset as follows:
from django.db.models import F, Sum
Contact.objects.annotate(
total_due=Sum(
F('amountgiven_set__total_payable')
- F('amountgiven_set__total_returned')
)
)
Then define a MethodSerializer field on your serializer to account for it.
class ContactMinSerializer(serializers.ModelSerializer):
total_due = serializers.SerializerMethodField()
def get_total_due(self, obj):
return return self.total_due
class Meta:
model = Contact
fields = (
'id', 'first_name', 'created', 'modified',
'total_due',
)

What model object type do I use to store a value of time in Django Admin?

I'm trying to set up a product that corresponds to a length of time (a newspaper subscription).
I'm having the clients use the Django Admin to add product types (various subscription lengths) but I can't find anything out there on what model object type I should use to store a value of time (like 52 weeks, 26 weeks, 1 week, etc.). I would want to be able to choose a length as either days or weeks, because some papers are weekly, others daily.
Right now my Product Model is:
class Product(models.Model):
product_type = models.CharField(max_length=100)
product_description = models.CharField(max_length=255)
product_cost = models.DecimalField(decimal_places=2, max_digits=4)
product_active = models.BooleanField()
def get_absolute_url(self):
return "/signup/%i/" % self.id
def __unicode__(self):
return self.product_type
Is there any way to make the product_type an object type that would let a user define a value of time?
Thanks,
Anthony
I think it'd be easier to just store two values, one numeric value and a choice for what the number represents.
duration = models.IntegerField()
duration_type = models.CharField(max_length=32, choices=[
("day", "Days"),
("week", "Weeks")])
Assuming you have a separate subscription model, you could then calculate the expiration in that model as a method:
class Subscription(models.Model):
product = models.ForeignKey(Product)
starts = models.DateField()
def expires(self):
from datetime import timedelta
if self.product.duration_type == "day":
days = self.product.duration
elif self.product.duration_type == "week":
days = self.product.duration * 7
return self.starts + timedelta(days=days)
You could add some DateTime model fields describing the start and end of a subscription. you can create a method of your model that returns a timedelta by subtracting one datetime from the other
class Product(models.Model):
product_type = models.CharField(max_length=100)
product_description = models.CharField(max_length=255)
product_cost = models.DecimalField(decimal_places=2, max_digits=4)
product_active = models.BooleanField()
subscription_start = models.DateTimeField()
subscription_end = models.DateTimeField()
def get_duration(self):
# returns a timedelta
return self.subscription_end - self.subscription_start
def get_absolute_url(self):
return "/signup/%i/" % self.id
def __unicode__(self):
return self.product_type
eg:
>> import datetime
>> d1 = datetime.datetime(1986,14,05,0,0,0)
>> d2 = datetime.datetime.now()
>> print d2 - d1
9471 days, 17:24:31
you can use that timedelta to do calculations in your views