I want to disable django fields for 6 months after the date of update. I have saved update_time to a table.
updated_time = a.update_time
disabled_time = a.update_time + timedelta(180)
I want to diable field that updated:
self.fields['first_name'].widget.attrs['readonly'] = True
How can I disable self.fields['first_name'].widget.attrs['readonly'] = True for disabled_time?
Thanks in advance
You can compare, and substract basic datetime objects and make some check at form initialization time:
from datetime import timedelta, datetime
...
class FooForm(ModelForm):
def __init__(self, *args, **kwargs):
super(FooForm, self).__init__(*args, **kwargs)
# check if we already have a saved object and it's not older than 180 days
if self.instance.pk and
(datetime.now() - self.instance.update_time) < timedelta(180):
self.fields['first_name'].widget.attrs['readonly'] = True
class Meta:
model = Foo
(Not really tested but should work as it is.)
Also note, that it is often convenient to keep update_time with auto_now set to True.
Related
I have a model in Django with a DateTimeField attribute. I want to forbid insertion of new data in the database if the duration between the datetime field of the new data and the latest datetime field in the database is less than some duration threshold.
class MyModel(models.Model):
time_stamp = models.DateTimeField(default=timezone.now, null=True)
When I want to insert a datapoint say today, and the latest time stamp in my database is yesterday, and the duration threshold is one month (this operation should not be possible).
You can define this logic in your views like so:
from django.shortcuts import get_object_or_404
import datetime
def CreateNew(request, id):
obj = get_object_or_404(MyModel, id = id) #Get the object from your database
form = YourForm(request.POST or None, instance = obj) #create form instance to be rendered inside template
diff = (timezone.now() - obj.time_stamp).total_seconds()
threshold = datetime.timedelta(days=30).total_seconds()
if diff < threshold: # Compare dates to check condition
return HttpResponse('<h1>Not Allowed</h1>')
elif form.is_valid(): # If condition is passed save form as you normally would
form.instance.time_stamp = timezone.now() # Update time_stamp to current time
form.save()
return HttpResponseRedirect("/")
context = {
'form': form
}
return render(request, "Your_template", context)
If you are determined that this is prevented in a more hard manner than putting protection logic in view(s), then instead of checking in the view you can check in the model's save method.
def save( self, *args, **kwargs):
diff = (timezone.now() - self.time_stamp).total_seconds()
threshold = datetime.timedelta(days=30).total_seconds()
if diff < threshold: # Compare dates to check condition
# not certain ValueError is the best choice of exception
raise ValueError(
f"Can't save because {diff} seconds since the previous save, the minimum is {theshold}"
)
super().save( *args, **kwargs)
This check can still be bypassed, by Django bulk_update for example, and by raw SQL. Some databases may let you put the check into the database itself.
The downside is that fixing mistakes using (for example) the Django Admin may become difficult. In this case you can programmatically bypass the check by resetting the timestamp first.
I have the following (simplified) model and factory:
models.py
class Event():
duration = FloatField()
start_time = TimeField()
finish_time = DateTimeField()
def save(self, *args, **kwargs):
self.finish_time = self.start_time + timedelta(hours=self.duration)
event_factory.py
from factory import Faker
class EventFactory:
date = Faker(
"date_time_this_month",
before_now=False,
after_now=True,
tzinfo=timezone.get_current_timezone(),
)
start_time = Faker("time_object")
duration = Faker("random_int")
However, my save method raises Warning: DateTimeField Event.finish_time received a naive datetime (2022-03-28 12:43:38) while time zone support is active.
date is aware due to tzinfo argument, but start_time is naive (I checked with django's timezone.is_aware()), because time providers in Faker do not seem to allow any timezone parameters.
Any suggestion in getting a fake aware time object in factory-boy/Faker?
Try to use FuzzyDateTime objects, they return a timezone aware object: https://factoryboy.readthedocs.io/en/stable/fuzzy.html?highlight=timezone
I have a dateTime field in a model. The dateTime field named breakfast_start_time takes an input.
I have to save another variable or timefield(whichever is better) named breakfast_attendence_start_time whose value should be automatically saved 15 minutes less than the breakfast_start_time.
For this we use
def save(self, *args, **kwargs):
#do something
super().save(*args, *kwargs)
I am trying to do
breakfast_attendence_start_time = breakfast_start_time - time(15,0)
but it is giving error that
class TimeField does not define '_sub_', so the '-' operator cannot be used on its instances
Edited:
Here is the full code
class Hostel(models.Model):
name = models.CharField(max_length=15)
breakfast_start_time = models.TimeField()
lunch_start_time = models.TimeField()
snacks_start_time = models.TimeField()
dinner_start_time = models.TimeField()
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
I am taking start time from admin panel and I want to add 4 more variable/field like breakfast_attendence_start_time whose value should be saved automatically 15 minutes earlier than breakfast_start_time how can I achive that.
You should use timedelta to sub specific time with DateTime field. Such as
import datetime
time_before_15_minute = datetime.datetime.now() - datetime.timedelta(minutes=15)
Use DateTimeField instead of TimeField and use timedelta to make substractions
from datetime import datetime, timedelta
n = datetime(2019, 10, 4, 12, 30)
m = n - timedelta(minutes = 15) # m is now datetime(2019, 10, 4, 12, 15)
You can play with the DateTimeField but this will return time of when this function was called or used. Hope it helps
from django.utils import timezone
class AKA(models.Model):
create_time = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.create_time
I have a model and I want to know if it is possible to set a condition that triggers a change in the model field. For example, I have a model
class BillboardTracker(models.Model):
client_name = models.CharField(max_length=400)
entry_date = models.DateTimeField(default=datetime.now)
duration = models.PositiveIntegerField()
expiry_date = models.DateField()
is_expired = models.BooleanField(default=False)
I want to know if it is possible to have a function in the model that makes is_expired equals to True when the expiry date is up. I tried this
def expire(self):
if datetime.now == self.expiry_date:
self.is_expired = True
but it's not working. Is it possible to implement this?
Use a #property
The simplest thing here is not to have an is expired field at all! It's not needed. What you need is a property.
class BillboardTracker(models.Model):
client_name = models.CharField(max_length=400)
entry_date = models.DateTimeField(default=datetime.now)
duration = models.PositiveIntegerField()
expiry_date = models.DateField()
#property
def is_expired(self):
if datetime.now > self.expiry_date:
return True
return False
Remember, you don't have a field in a database, if that field is the same as another field with a simple calculation. This automatically eliminates your head ache of having to flag items as expired.
If you want to find out if an object has expired.
if instance.is_expired == True:
print 'yes, that ones gone'
Filtering
If you wanted to retrieve a whole set of objects that have expired
BillboardTracker.objects.filter(expiry_date__le=datetime.now())
This is why I mentioned that you don't need to store a field that can be easily calculated.
Index advantage
In most RDBMS a boolean field (such as your is_expired column) cannot be indexed effectively. So that actually means the above query will be faster than a query on that boolean field provided you create an index on the expiry_date field.
You need to make two changes in this function,
Firstly use datetime.now() and secondly,
You might want to update your logic like this :
def expire(self):
if datetime.now() >= self.expiry_date:
self.is_expired = True
return True
else:
return False
Because sometimes both the values might not be exactly same but still BillboardTracker need is_expired = True for all previous dates.
And in your views :
def your_view(request):
instance = BillboardTracker.objects.get(id=some_id)
if instance.is_expired() == True:
print 'expired'
else:
print 'fresh'
I have managed to create a very simple model which allows me to subtract 2 DateTime fields, like so:
class Log(models.Model):
date = models.DateField()
take = models.DateTimeField()
land = models.DateTimeField()
tot = models.CharField(max_length=200, blank=True, default='00000')
def __str__(self):
return str(self.date)
def time_delta(self):
tdelta = self.land - self.take
return str(tdelta)
def save(self, *args, **kwargs):
self.tot = self.time_delta()
super(Log, self).save(*args, **kwargs)
My problem is the user would have to specify the date on every field. How could I make the fields take and land refer to date once and for all?
I don’t know how to do that in your Django model. I think you have to calculate the dates and times in your controller instead, and then register the values in your database.
You can do something like that with datetime.datetime.combine() function:
Return a new datetime object whose date components are equal to the given date object’s, and whose time components are equal to the given time object’s.
You have a reference date, for instance: today.
import datetime
date = datetime.date.today()
The user enter the takeoff time, you can combine this time with the reference date.
takeoff_time = datetime.time(8, 12)
takeoff_datetime = datetime.datetime.combine(date, takeoff_time)
print(takeoff_datetime.isoformat(" "))
# -> 2016-12-21 08:12:00
If the landing date is the same as the takeoff date, you can calculate the landing date/time with the same date reference:
landing_time = datetime.time(12, 37)
landing_datetime = datetime.datetime.combine(date, landing_time)
print(landing_datetime.isoformat(" "))
# -> 2016-12-21 12:37:00
Then, you can register the date, _takeoff_datetime_ and _landing_datetime_ in your database.
Note: you can do the same with the flight duration
I eventually managed to find a solution largely based on Laurent's answer so here it is,if that can ever help someone else:
from datetime import datetime
def calculation(self):
calc_take_off = datetime.combine(self.date, self.take)
calc_land = datetime.combine(self.date, self.land)
result = calc_land - calc_take_off
return str(result)
and then to save this in models:
def save(self, *args, **kwargs):
self.tot = self.calculation()
super(Log, self).save(*args, **kwargs)