How to Model a TimeField in Django? - django

I have modeled a class called ConversationHistory. Whenever an instance is created I wish to set the current date and current time.
class ConversationHistory(models.Model):
contact_date = models.DateField(_(u"Conversation Date"), blank=True)
contact_time = models.DateTimeField(_(u"Conversation Time"), blank=True)
def __init__(self, *args, **kwargs):
super(ConversationHistory, self).__init__(*args, **kwargs)
self.contact_date = datetime.datetime.now()
self.contact_time = datetime.datetime.now()
The idea is that the user can later still adjust the date and time as two different fields.
I am now a bit lost how to make the time field only to show and accept time, rather than date and time. I recon it is not possible to have a time field without datetime, but then how would I show only the time in the form?

If you want only time, TimeField is what you need:
class ConversationHistory(models.Model):
contact_date = models.DateField(_(u"Conversation Date"), blank=True)
contact_time = models.TimeField(_(u"Conversation Time"), blank=True)
You can take advantage of the auto_now_add option:
class TimeField([auto_now=False, auto_now_add=False, **options])
A time, represented in Python by a datetime.time instance. Accepts the
same auto-population options as DateField.
If you use the auto_now_add, it will automatically set the field to now when the object is first created.
class ConversationHistory(models.Model):
contact_date = models.DateField(_(u"Conversation Date"), auto_now_add=True, blank=True)
contact_time = models.TimeField(_(u"Conversation Time"), auto_now_add=True, blank=True)

Related

Creating an "incomplete" Django class for a user to fill in

I have a database representing financial transactions. Columns representing payee and category are non-optional.
However, part of my app's functionality will be to ingest external spreadsheets of transactions which do not already have payee and category information. I would then populate a form where the user will select correct payees and categories through drop-down menus, and then save the completed information to the database.
Is the correct approach to simply create two separate but equivalent classes (see below)? Or is there some way to make one a sub-class to another, despite the fact that one is connected to a database and the other is not.
# An initial class representing a transaction read from an Excel sheet
# Payee and category information are missing at this stage, but will be filled
# in by the user later on
class TransactionFromSpreadsheet:
def __init__(self, date, amount):
self.date = date
self.amount = amount
self.payee = None
self.category = None
# The Django class that will be instantiated once all the user has completed all
# necessary information
class Transaction(models.Model):
date = models.DateField()
amount = models.DecimalField(max_digits=14, decimal_places=2)
category = models.ForeignKey('Category', on_delete=models.CASCADE)
payee = models.ForeignKey('Payee', on_delete=models.CASCADE)
One could use optional foreign keys and a custom manager to provide an easy way to query the "incomplete" or "complete" transactions.
class TransactionQueryset(models.query.QuerySet):
def complete(self):
return self.filter(category__isnull=False,
payee__isnull=False)
def incomplete(self):
return self.filter(category__isnull=True,
payee__isnull=True)
class TransactionManager(models.Manager):
def get_queryset(self):
return TransactionQueryset(self.model, using=self._db)
def complete(self):
return self.get_queryset().complete()
def incomplete(self):
return self.get_queryset().incomplete()
class Transaction(models.Model):
date = models.DateField()
amount = models.DecimalField(max_digits=14, decimal_places=2)
category = models.ForeignKey('Category', on_delete=models.CASCADE,
blank=True, null=True)
payee = models.ForeignKey('Payee', on_delete=models.CASCADE,
blank=True, null=True)
objects = TransactionManager()
And if you now need an incomplete transaction you could easily get these in a view:
def view_incomplete(request):
incompletes = Transaction.objects.incomplete()
return render(request, 'incomplete_template.html',
{'incompletes': incompletes})
It is now very comfortable to gather all heavily used filter conditions in the queryset and manager class.
And if you have non complementary filter conditions you could even chain the manager functions.

Django auto_now count results in a certain date

I want filter of all request done is done in a certain date
class Log(models.Model):
request = models.CharField(max_length=255)
created_at = models.DateField(default=now, blank=True)
def __str__(self):
return self.request
But I don't how to do it correctly compare both the created_at and the date today
quantity_of_requests = Log.objects.filter(created_at=now()).count()
The objective is to take the current of date and count how many logs was added until now. But I also want to keep track of the time the request made.
I tried with this function but it takes of the time and put to 0:
datetime.today().strftime('%Y-%m-%d')
Edit:
I did by doing this:
class Log(models.Model):
request = models.CharField(max_length=255)
created_at = models.DateTimeField(blank=True, auto_now=True)
def __str__(self):
return self.request
quantity_of_requests = Log.objects.filter(created_at__contains=datetime.today().strftime('%Y-%m-%d')).count()
I would like to know a better way to write this code
Your now gets initated when your django process runs. To correctly get local time every time, either override save() to set it or use auto_now=True on DateField.
Ref: https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.DateField.auto_now

Setting queryset within forms.ModelChoiceField()

The queryset for the 'jurisdiction' field is set below in the initialization. The queryset is dependent on the id that is passed in, which comes from a specific link that a user clicks. As a result, I can't define a singular queryset within the forms.ModelChoiceField(), but it seems that django requires me to do this.
class TaxForm (forms.ModelForm): #Will be used for state tax and other taxes
jurisdiction = forms.ModelChoiceField(queryset=?????)
class Meta:
model = Tax
exclude = ('user', 'taxtype',)
def __init__(self, *args, **kwargs):
self.taxtype = kwargs.pop('taxtype',None)
super(TaxForm, self).__init__(*args, **kwargs)
if int(self.taxtype) == 1:
self.fields['jurisdiction'].choices = [(t.id, t) for t in State.objects.all()]
elif int(self.taxtype) == 2:
self.fields['jurisdiction'].choices = [(t.id, t) for t in Country.objects.all()]
else:
self.fields['jurisdiction'].choices = [(t.id, t) for t in State.objects.none()]
How can I indicate that I want the jurisdiction field to be a dropdown, but not specify one queryset within the forms.ModelChoiceField()? Alternatively, how can I make the queryset that is referenced in forms.ModelChoiceField() refer to the queryset that I initialize based on the taxtype?
Thanks!
Here is my tax model
class Tax(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, default=None)
jurisdiction = models.CharField(max_length=120, null=True, blank=True)
name = models.CharField(max_length=200)
rate = models.DecimalField(max_digits=10, decimal_places=2)
basis = models.CharField(max_length=120, null=True, blank=True)
regnumber = models.CharField(max_length=200, null=True, blank=True) #tax number that will appear on customer invoice
taxtype = models.IntegerField(blank=True, null=True) # 0 is other, 1 is state, 2 is federal
def __str__(self):
return '{} - {}'.format(self.user, self.name)
As I mentioned, ModelChoiceField is not the right thing to do here. That's for allowing the user to choose from related items from a single model that will be saved into a ForeignKey. You don't have a ForeignKey, and what's more you're setting the choices attribute in your init rather than queryset. You should make it a plain ChoiceField with an empty choices parameter:
jurisdiction = forms.ChoiceField(choices=())
(For the sake of completeness: if you did need to use ModelChoiceField you can put anything you like into the queryset parameter when you're overwriting it in __init__, because it will never be evaluated. But managers have a none method which returns an empty queryset, so you could do queryset=State.objects.none().)

Auto increment django model field per user

I have this model:
class Invoice(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
data = models.TextField(default=None, blank=True, null=True)
number = models.PositiveIntegerField(default=0, null=False)
What I need is to auto-increment the field number for each separated user. The rationale is that each user has a list of Invoice, starting from number=1 to number=latest.number+1.
I do known about F() expressions, but can't figure out how to reference the latest/greatest number for each specific user. Maybe Invoice.objects.filter(owner=request.user).aggregate(Max('number')) is the path, but how do I ensure there is no race conditions between Max() and F()?
You can achieve this and similar functions by overriding save method in model and writing your custom logics to it.
class Invoice(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
data = models.TextField(default=None, blank=True, null=True)
number = models.PositiveIntegerField(default=0, null=False)
def save(self, *args, **kwargs):
if self.pk:
self.number += 1
# Write all your logic here, like handeling max value etc
return super(Invoice, self).save(*args, **kwargs)
you can get your first or last object like this:
# For last Object
Model.objects.latest('field') # Field can be id or pk or ...
# For first Object
Model.objects.all().first() # You can also use it on filter
A simple solution is you can make the number field as the primary key since its nature would be similar.
class Invoice(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
data = models.TextField(default=None, blank=True, null=True)
number = models.IntegerField(primary_key=True)
Or, you can make number as AutoField or BigAutoField.
number = models.AutoField()

Django: DateTime not good when saved on my database

It seems stupid but i have hard time since hours and hours about saving my dateTime on db. I'm pretty new in Python and it's not everyday that i'm manipulating datetime.
I have one hour of difference when i'm saving my value. So 18h is now 17h (sorry for my english)
My models is like this:
class Event(models.Model):
title = models.CharField(max_length=245)
description = models.TextField(max_length=750, null=True, blank=True)
start = models.DateTimeField()
end = models.DateTimeField()
created_at = models.DateTimeField(editable=False)
updated_at = models.DateTimeField(editable=False)
slug = AutoSlugField(populate_from='title', unique=True, editable=False)
nb_participant = models.PositiveSmallIntegerField(default=1)
price = models.PositiveSmallIntegerField(default=0)
user = models.ForeignKey(User, editable=False, related_name='author')
address = models.ForeignKey('Address', editable=False, related_name='events')
participants = models.ManyToManyField(User, related_name='participants', blank=True)
class Meta:
db_table = 'event'
def save(self, *args, **kwargs):
if not self.pk:
self.created_at = timezone.localtime(timezone.now())
print self.created_at
self.updated_at = timezone.localtime(timezone.now())
super(Event, self).save(*args, **kwargs)
As you see i have 4 fields with datetime. 2 are actually save automatically save when the model is created.
I resolved the probleme by using timezone.localtime(timezone.now()) instead of timezone.now(). I find that there enter link description here at the bottom of the page. But they said to use timezone.now() in most case. So i don't know why i have this one hour difference.
I have two other fields that are send from my angular frontend to my API( using django rest framework)
I put a screenshot. The first object i send by angular.As you seen the date is well formatted.
The second object is the response from my API and i have lost one hour (so the GMT +1)
Why ? I'm totally block so if someone has a solution, i'll be very happy :)
My settings.py:
LANGUAGE_CODE = 'fr-fr'
TIME_ZONE = 'Europe/Paris'
USE_L10N = True
USE_TZ = True
Thanks.
In settings file try with USE_TZ=False, and use normal datetime.now().