How to convert Django's TimeField to Postgres' timestamp with time zone - python

In my Django app, the program gets the time of an event from the user, say user inputs 10:30 for the time of an event. The program uses modelformset_factory to create a whole bunch of forms:
#forms.py
class EventForm(ModelForm):
class Meta:
model = Event
fields = ['entry', 'time', 'work']
localized_fields = ('time', )
widgets = {
'work': Textarea(attrs = {'cols': 40, 'rows': 8}),
}
#models.py
class Event(models.Model):
'''
Each day there are many events, e.g. at 10 am, the framer orders material, etc.
'''
entry = models.ForeignKey(Entry)
time = models.TimeField()
work = models.TextField()
#views.py:
EventFormSet = modelformset_factory( Event, form = EventForm, exclude = ('entry',),extra = 5)
eventset = self.EventFormSet(request.POST)
all_errors = eventset.errors # All errors returns [{}, {}, {}, {}, {}], so the data is free of error.
try:
eventset.is_valid()
except ValidationError:
return render(request, self.template_name, {'form': self.day_log(initial = self.initial_values), 'eventforms': self.event_formset})
events_instances = eventset.save(commit = False)
for instance in events_instances:
if instance.time:
event_date = datetime.date(the_year, the_month, the_day) # Naive date
event_time = instance.time # Naive time
naive_event_time = timezone.is_naive(event_time) # Checking naivaty
event_datetime = datetime.datetime.combine(event_date, event_time) # Naive datetime object
is_aware_event_datetime = timezone.is_aware(event_datetime) # Checking to see if the datetime object is aware or naive
event_aware_datetime = datetime.datetime(the_year, the_month, the_day, instance.time.hour, instance.time.minute, instance.time.second, tzinfo = pytz.utc) # Making an aware time
is_aware_event_aware_datetime = timezone.is_aware(event_aware_datetime) # Making sure the event_aware_datetime is indeed aware
instance.time = event_aware_datetime
awareness_test = timezone.is_aware(instance.time) # Making sure instance.time is aware
eventset.save() # Here is where the exception is thrown.
The Django app fails to save time data to Postgres. The exception type is DataError with the exception value:
invalid input syntax for type timestamp with time zone:
LINE 1: ...y_event" ("entry_id", "time", "work") VALUES (14, '18:43:04....
with ^ pointing at ' of '18:
Edit:
Here is my database:
CREATE TABLE site_activity_event
(
id serial NOT NULL,
entry_id integer NOT NULL,
"time" time without time zone NOT NULL,
work text NOT NULL,
CONSTRAINT site_activity_event_pkey PRIMARY KEY (id),
CONSTRAINT site_activity_event_entry_id_fkey FOREIGN KEY (entry_id)
REFERENCES site_activity_entry (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED
)
I did sqlall my app to ensure that the data type of model field correspond to that of the app and all is well there. This seems to be a postgres issue, but Django is supposed to take care of all those pesky interface issues with Postgres (https://docs.djangoproject.com/en/1.8/topics/i18n/timezones/#migration-guide), so that makes me think I am missing something. Any help is much appreciated.

Well, is "18:43:04" a valid timestamp with a timezone? Do you think it specifies a unique point in time? If you do then you have uncovered a major bug in PostgreSQL's date/time handling. If not, then you should either supply a valid timestamp or use the correct type in your database - I can't say which without knowing more about these events.

Your Event has a TimeField but it looks like the database was created with a DateTimeField.

Related

Odoo _check_concurrency never triggered?

With our team, we would like to implement a feature where the user is warned whenever the record he/she is updating have also been updated by one of his collegue since he/she opened the record.
We dug into the source code because we did not find any official documentation, only some module that did not fit our Odoo version (11).
We found in the file /odoo/odoo/models.py the method def _check_concurrency(self): with the following code:
#api.multi
def _check_concurrency(self):
if not (self._log_access and self._context.get(self.CONCURRENCY_CHECK_FIELD)):
return
check_clause = "(id = %s AND %s < COALESCE(write_date, create_date, (now() at time zone 'UTC'))::timestamp)"
for sub_ids in self._cr.split_for_in_conditions(self.ids):
nclauses = 0
params = []
for id in sub_ids:
id_ref = "%s,%s" % (self._name, id)
update_date = self._context[self.CONCURRENCY_CHECK_FIELD].pop(id_ref, None)
if update_date:
nclauses += 1
params.extend([id, update_date])
if not nclauses:
continue
query = "SELECT id FROM %s WHERE %s" % (self._table, " OR ".join([check_clause] * nclauses))
self._cr.execute(query, tuple(params))
res = self._cr.fetchone()
if res:
# mention the first one only to keep the error message readable
raise ValidationError(_('A document was modified since you last viewed it (%s:%d)') % (self._description, res[0]))
=> This method is called before any "write". It compares :
the __last_update value of the record at the time of write
with a value of __last_update get from the context, which therefore should have been set in the context beforehand
PROBLEM
We did not find anywhere in the code (python or javascript) the value set in the context => NOTHING HAPPENS ! THe function returns from the beginning.
When we tried to hardcode it in the context, the function check_concurrency seems to work properly.
QUESTION
Does anyone one know where the __last_update is set or SHOULD BE set in the context ? And how ?
I would e.g. imagine setting it somehow when clicking on edit button of a record ??? Or at read time ??
CONCURRENCY_CHECK_FIELD = '__last_update'
concurrency field is a dynamic field which computing method defined dynamically and also you can see this is updated by
last_modified_name = 'compute_concurrency_field_with_access' or last_modified_name = 'compute_concurrency_field' according to access and later added to the class. Following functions will take part in the workaround.
#api.model
def _add_magic_fields(self):
""" Introduce magic fields on the current class
* id is a "normal" field (with a specific getter)
* create_uid, create_date, write_uid and write_date have become
"normal" fields
* $CONCURRENCY_CHECK_FIELD is a computed field with its computing
method defined dynamically. Uses ``str(datetime.datetime.utcnow())``
to get the same structure as the previous
``(now() at time zone 'UTC')::timestamp``::
# select (now() at time zone 'UTC')::timestamp;
timezone
----------------------------
2013-06-18 08:30:37.292809
>>> str(datetime.datetime.utcnow())
'2013-06-18 08:31:32.821177'
"""
def add(name, field):
""" add ``field`` with the given ``name`` if it does not exist yet """
if name not in self._fields:
self._add_field(name, field)
# cyclic import
from . import fields
# this field 'id' must override any other column or field
self._add_field('id', fields.Id(automatic=True))
add('display_name', fields.Char(string='Display Name', automatic=True,
compute='_compute_display_name'))
if self._log_access:
add('create_uid', fields.Many2one('res.users', string='Created by', automatic=True))
add('create_date', fields.Datetime(string='Created on', automatic=True))
add('write_uid', fields.Many2one('res.users', string='Last Updated by', automatic=True))
add('write_date', fields.Datetime(string='Last Updated on', automatic=True))
last_modified_name = 'compute_concurrency_field_with_access'
else:
last_modified_name = 'compute_concurrency_field'
# this field must override any other column or field
self._add_field(self.CONCURRENCY_CHECK_FIELD, fields.Datetime(
string='Last Modified on', compute=last_modified_name, automatic=True))
def compute_concurrency_field(self):
for record in self:
record[self.CONCURRENCY_CHECK_FIELD] = odoo.fields.Datetime.now()
#api.depends('create_date', 'write_date')
def compute_concurrency_field_with_access(self):
for record in self:
record[self.CONCURRENCY_CHECK_FIELD] = \
record.write_date or record.create_date or odoo.fields.Datetime.now()

Django view objects filter with timezone.now().date or timezone.now().time-> expected string or bytes-like object

Hi i have some Django 11 project, my model look like
class Event(models.Model):
name = models.CharField(max_length=100, unique=True)
title = models.CharField(max_length=100)
info = models.CharField(max_length=100)
image = models.ImageField(upload_to='events/%Y/%m/%d')
start_date = models.DateField(default=timezone.now)
start_time = models.TimeField(default=timezone.now)
stop_date = models.DateField(default=timezone.now)
stop_time = models.TimeField(default=timezone.now)
place = models.ForeignKey('places.Place', on_delete=models.CASCADE)
company = models.ForeignKey('companies.Company', on_delete=models.CASCADE)
and my view look like
def place_website(request, place_id):
place_template = get_template('room.html')
place_obj = Place.objects.filter(id=place_id)
# filter for event obejts only for requested place, filtered for now and next events
place_event_now = Event.objects.filter(place=place_id, start_date=timezone.now().date, stop_date__gte=timezone.now().date)
place_events_next = Event.objects.filter(place=place_id, start_date=timezone.now(), stop_date__gte=timezone.now()).order_by('start_time')
place_context = {
'place_obj': place_obj,
'place_event_now': place_event_now,
'place_events_next': place_events_next,
}
return HttpResponse(place_template.render(place_context))
the thing i want to manage is to pass to template the list of filtered Event objects based on time.
Lets pick this line
place_event_now = Event.objects.filter(place=place_id, start_date=timezone.now().date, stop_date__gte=timezone.now().date)
it couse error "expected string or bytes-like object" but when i remove ".date" from "timezone.now()" error disappear (then filter do nothing) but i want to compare date to date and time to time.
How to do this properly ?
This approach to filter objects in view rather than in template is proper?
###### UPDATE ########
Its werid because after correction now i have no error but queryset is not filtered properly, look like only two first parameter is filtering ok and the another two is causing empty queryset.
place_event_now = Event.objects.filter(place=place_id, start_date=timezone.now().strftime('%Y-%m-%d'), start_time__lte=timezone.now().strftime('%H:%M:%S'), stop_time__gte=timezone.now().strftime('%H:%M:%S'))
I my database time is saved in format H:M:S and timezone.now().time() has different format so i modified filter with .strftime this didnt help, what i wont is to limit "place_event_now" queryset to particular object/objects that come true with condition start_time < currenttime < stop_time.
Another case is with place_event_next
place_events_next = Event.objects.filter(place=place_id, start_date=timezone.now().strftime('%Y-%m-%d'), stop_date__gte=timezone.now().strftime('%Y-%m-%d'), start_time__gt=timezone.now().strftime('%H:%M:%S')).order_by('start_time')
Event when i filter objects that start_time is greater than timezone.now() they still are in queryset.
Am I doing something wrong ?
I figured it out that timezone.now() return time not in my current timezone, i change it to timezone.localtime() and everything working perfect !!!
May be you need call date to date()
replace
filter(place=place_id, start_date=timezone.now().date, stop_date__gte=timezone.now().date)
# ^^^ ^^^
to
filter(place=place_id, start_date=timezone.now().date(), stop_date__gte=timezone.now().date())
# ^^^ ^^^

Map multiple WTForm fields to one attribute using Flask-WTForm

I have a form that maps correctly to my model. What I want to do is have users input their date of birth using three SelectField (day, month, year) and write the combination of those three values to my model.dateOfBirth. I know of the existing DateField and DateTimeField options, but neither of them are suitable.
I've attempted this using a FormField but it fails when I call populate_obj on the whole Form 'str' object has no attribute 'day', assuming that it is trying to set model.dateOfBirth.day which of course, doesn't exist.
I can pull all the relevant data from form.data and write to model outside of populate_obj, but I can't seem to remove the dateOfBirth data to stop populate_obj from failing. There are of course other questions about how I could prefill the form data when passing obj on form creation, but I'm willing to sacrifice that for now.
I've looked at using a FieldList, but the docs say that it gives all its data back in a list, instead of a dict, which will still give me the same issues
Forms
class DOBForm(Form):
day = SelectField(u'Please enter your date of birth',
choices=days,
validators=[InputRequired(message=u' ')]
)
month = SelectField(u' ',
choices=months,
validators=[InputRequired(message=u' ')]
)
year = SelectField(u' ',
choices=years,
validators=[InputRequired(message=u' ')]
)
and
class MainForm(Form):
dateOfBirth = FormField(DOBForm)
Model
class Model
dateOfBirth = db.Column('dateOfBirth', Date)
You can write a function for this.
from datetime import datetime
def date_of_birth(**kwargs):
day = kwargs['day']
month = kwargs['month']
year = kwargs['year']
strip="-"
seq = (day,month,year)
date = datetime.strptime(strip.join(seq) , '%d-%m-%Y')
birthday = stringDate.strftime('%d-%m-%Y')
dob = Model()
dob.dateOfBirth = birthday
session = Session()
session.add(dob)
session.commit()
retval = row2dict(dob)
session.close()
return retval
Now you can call this function.
I don't test this function yet. If you get any error or have any query , let me know..

How to filter in many-to-many relationship in Django

Since I could not find the exact answer for me, I decided to ask.
I have the following two models:
class Schedule(models.Model):
     transport = models.ForeignKey(Transport)
     stop = models.ForeignKey(Stop)
     from_to_stop = models.ForeignKey(Direction)
     time = models.ManyToManyField(TimeTable)
     type_day = models.ForeignKey(TypeDay)
     created = models.DateTimeField(_('Created at'), auto_now_add = True)
     updated = models.DateTimeField(_('Modified at'), auto_now = True)
class TimeTable(models.Model):
     time_arrival = models.TimeField()
     created = models.DateTimeField(_('Created at'), auto_now_add = True)
     updated = models.DateTimeField(_('Modified at'), auto_now = True)
A script that must be dynamically added things in 'time' field on the 'Schedule'. Here's what I do:
time = self.get_or_create_time(time)
count = Schedule.objects.filter(time__in = [time]).count()
if not count:
schedule.time.add(time)
def get_or_create_time(self, time):
obj, created = TimeTable.objects.get_or_create(time_arrival=time)
return obj
Here I'm not getting the required result for me, because it searches the entire table if there is such a connection somewhere. I want to see if there is such a link only for the current object ' schedule ' is the current object. All I want is for him to see whether there is a connection with the ' TimeTable '. How to do this in Django?
For a start, there is no point in using __in with a single element which you then wrap in a list. The fact that you had to do that should have given you a hint that you are using the wrong predicate: just use the default (which is __eq, but since it's the default you can leave it out altogether).
Also, if you only want to know if an object exists, use .exists() rather than .count(), as the latter is a more expensive query if there can be multiple objects.
But basically your problem is simple. You want to filter the times belonging to a schedule: so, start with that schedule, not the whole Schedule model.
exists = self.times.filter(time_arrival=time.time_arrival).exists()

Django Unit testing, unknown column error

I'm a Django beginner and am getting acquainted to using it, and I'm also a big believer in unit testing.
Given a sample database table contracts with the fields
parent_id int
contract_num varchar
start_date date
end_date date
org_name varchar
I defined a model class by using django_admin.py inspectdb > models.py
class Contracts(models.Model):
parent_id = models.IntegerField()
contract_num = models.CharField(max_length=10L, db_column='contract_num')
start_date = models.DateField()
end_date = models.DateField(null=True, blank=True)
org_name = models.CharField(max_length=100L, db_column='org_name')
class Meta:
db_table = 'contracts'
Within the test class, I defined
def setUp(self):
self.contracts = Contracts(parent_id = 300, contract_num = "1234", start_date = timezone.now(), end_date = None, org_name = "TestContractName")
def test_contracts_access(self):
self.contracts.save()
getContracts = Contracts.objects.get(parent_id = 300)
self.assertEqual(getContracts.org_name, "TestContractName")
self.assertEqual(getContracts.contract_num, "1234")
self.assertEquals(getContracts.contract_num, "12")
getContracts.org_name = "TestContractNameUpdate"
getContracts.save()
updateContract = Contracts.objects.get(contract_num = "1234")
self.assertEqual(updateContract.org_name, "TestContractNameUpdate")
When I run this test, I get a database error 1054: "Unknown column contracts.id in field list". What exactly does that mean? The first error in the stack trace is the get call right after the first save.
Thing is, I have an exact same test set up for another model object and that one passes.
Make sure to check the primary key that is set in the database. If you have a primary key that is on a field not labeled "id" (I'm looking at you parent_id...), then you need to set this in your model. You can do this with :
field_name = models.AutoField(primary_key=True)
If you do not do this, then Django will look for the field "id", assuming that you have one and that it is your primary key. I think you are getting this error because it is looking for contracts.id which does not exist!
Check out the Django docs on primary-key-fields and legacy databses.

Categories

Resources