django model use database server time - python

I have 2 separate django server and a mysql server at difference location
Using django model, when try create/update object (as code below). Result is both 3 column with the same django server local time value. How to get db server local time when do that.
my model:
class Test(models.Model):
id = models.AutoField(primary_key = True, null = False)
create = models.DateTimeField(auto_now_add=True)
update = models.DateTimeField(auto_now=True)
test = models.DateTimeField(null=True)
My code:
y = Test()
y.test = datetime.now()
y.save()
result
id create update test
------ ------------------- ------------------- -------------------
6 2013-10-07 06:57:04 2013-10-07 06:57:04 2013-10-07 06:57:04

If you handle this in Django, django will set these fields before saving the data to database. In that case, Django will set the time according to your applicaiton server (server which django runs).
You must make some settings to your MySql installation (if you have not set them already). Then you must re-create your tables or alter them in your database (I guess Django model definition can not handle this). This is a how-to doc explaning these settings
An example sql CREATE TABLE statement will look like that
CREATE TABLE test_test (
create DATETIME DEFAULT CURRENT_TIMESTAMP,
update DATETIME DEFAULT 0 ON UPDATE CURRENT_TIMESTAMP
)
If you already have data in yotr table, then you may execute ALTERs' instead of re-creating tables.
Then you must edit yor models and set related datetime fields to null, so MySql can set them.
class Test(models.Model):
id = models.AutoField(primary_key = True, null = False)
create = models.DateTimeField(null=True)
update = models.DateTimeField(null=True)
test = models.DateTimeField(null=True)

I think you can't do what you want without setting up the database timezone. Let's see what means each of your dates:
create = models.DateTimeField(auto_now_add=True)
It sets automatically the date to the time when you add it.
update = models.DateTimeField(auto_now=True)
It sets automatically the date to the time when you start the process (either web process or command).
test = models.DateTimeField(null=True)
...
y.test = datetime.now()
It sets the date to the time of the process (where Django process run).
To get the database time, you could store the timezone of database in settings, and use it whenever neccesary. You can't use any automatically generated field for this.
Another solution could be to grab local time manually (through a raw query) on database.

Related

Django Execute Function When DateTimeField Equals Current Date and Time

So I have implemented a subscription product to my website.
When they start the subscription, the current date and time is stored into the database. When they cancel the subscription, a period of time is added onto the start date so I know when to cancel the subscription and change some values elsewhere.
This is fine, I know how to use Django and Python, however, I am stuck on the last bit of, when the cancel date comes around in the future.
Question: How do I execute a function when the cancel date (in the db) is equal to current date and time.
Below is an simple example of the model I will be using:
models.py
class Subscriptions(models.Model):
subscription_id = models.AutoField(primary_key=True)
start_date = model.DateTimeField(auto_now_add=True)
cancel_date = model.DateTimeField(auto_now_add=False)
person_id = model.ForeignKey('Persons')
class Persons(models.Model):
person_id = models.AutoField(primary_key=True)
value_to_change = models.BooleanField()
Before you ask I have not attempted any code as I couldn't find a solution for this problem. Thanks <3
Without Celery, installed on UNIX system providing CRON (cron doku: e.g. https://www.computerhope.com/unix/ucrontab.htm):
write a Command https://docs.djangoproject.com/en/2.1/howto/custom-management-commands/ that fetches the objects for which cancel_date is in the past and that have not been cancelled yet. (If you do the lookup with datetime.now() (a very precise lookup with the finest granularity), you would have to be more than lucky to find anything.)
You should add another date field that tells you when the system actually ran the cancellation, and you should allow both the cancel_date and cancelled_date to be null.
# models.py
class Subscriptions(models.Model):
subscription_id = models.AutoField(primary_key=True)
start_date = model.DateTimeField(auto_now_add=True)
cancel_date = model.DateTimeField(auto_now_add=False, null=True)
cancelled_date = model.DateTimeField(null=True)
person_id = model.ForeignKey('Persons')
# under myapp/management/command/cancellation.py
class CancellationCommand(BaseCommand):
def handle(self, *args, **options):
now = datetime.now()
to_cancel_qs = Subscriptions.objects.exclude(
cancelled_date__isnull=False).filter(
cancel_date__lte=now)
for sub in to_cancel_qs.all():
# do your cancelling
sub.cancelled_date = now
sub.save()
# or: to_cancel_qs.update(cancelled_date=now)
install a cron job that runs this command via ./manage.py your_command at a regular time

Django Form Does Not Find Right ID int Increment on Production vs LH

For some reason on Localhost (LH) everything works fine but on my Production server, my form does not add a new user submission properly. The error I am getting is:
duplicate key value violates unique constraint "..."
DETAIL: Key (id)=(8) already exists.
Is there some sort of production "sudo systemctl restart gunicorn" I need to run (I have already tried the example above)? Maybe it's only working on LH because there I have tested there more and the increment naturally fell on the same level as the total amount of users? I really am out of ideas here.
models.py
class Lead(models.Model):
username = models.CharField(max_length=15, blank=True, null=True)
email = models.CharField(unique=True, max_length=150, validators=[validate_email])
created = models.DateTimeField(auto_now_add=True)
...
forms.py
class LeadCaptureForm1(forms.ModelForm):
birth_date = forms.DateField(widget=SelectDateWidget(years=range(1999, 1910, -1)))
class Meta:
model = Lead
widgets = {
'email': forms.TextInput(attrs={'class': 'form-control'}),
}
fields = ('email', 'birth_date',)
views.py
def iframe1(request):
ip = get_real_ip(request)
created = timezone.now()
if request.method == 'POST':
form = LeadCaptureForm1(request.POST)
if form.is_valid():
# Save lead
lead = form.save()
# attempt at fixing it
#lead.id = Lead.objects.get(all).count()
#print(lead.id)
lead.created = created
lead.birth_date = form.cleaned_data.get('birth_date')
lead.ipaddress = get_real_ip(request)
lead.joinmethod = "Iframe1"
lead.save()
print(lead)
I'm not sure why you are setting the ID manually, and especially why you are setting it to the count of items. You should always let the database manage the primary key itself - it is an autoincrement field, and is completely opaque to your data.
The reason why you are getting this conflict is that items can be deleted, so that there can be 8 entries in the database but ID 8 already exists. But as I say, don't do this at all.
Also, don't set created manually, as that will be done automatically as well because you have auto_now_add=True in the model field.. And birth_date is set by the form save already. Finally, you should call save with commit=False if you want to set some other fields manually.
So just do:
lead = form.save(commit=False)
lead.ipaddress = get_real_ip(request)
lead.joinmethod = "Iframe1"
lead.save()
This is due to the fact that we uploaded leads manually and Django uses PostgreSQL’s SERIAL data type to store auto-incrementing primary keys.
"A SERIAL column is populated with values from a sequence that keeps track of the next available value. Manually assigning a value to an auto-incrementing field doesn’t update the field’s sequence, which might later cause a conflict."
https://docs.djangoproject.com/en/2.0/ref/databases/#manually-specifying-values-of-auto-incrementing-primary-keys
To solve this we can either force a new serial number or build an exception to fix the serial. The latter option would be the ideal since we may upload users manually in the future. However, for now, we'll try and force serial number.
Run code: python manage.py sqlsequencereset [app_name]
For some reason, this did NOT work, so I was about to try and figure out how to build some sort of "python if exemption" but instead found this post first (IntegrityError duplicate key value violates unique constraint - django/postgres) that helped me directly update the "setval":
SELECT setval('tablename_id_seq', (SELECT MAX(id) FROM tablename)+1)

MySQL On Update not triggering for Django/TastyPie REST API

We have a resource table which has a field last_updated which we setup with mysql-workbench to have the following properties:
Datatype: TIMESTAMP
NN (NotNull) is checked
Default: CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
When I modify a row through the workbench and apply it, the last_updated field properly updates.
When I use the REST api we've setup, and issue a put:
update = requests.put('http://127.0.0.1:8000/api/resources/16',
data=json.dumps(dict(status="/api/status/4", timeout=timeout_time)),
headers=HEADER)
I can properly change any of the values (including status and timeout, and receive a 204 response), but last_updated does not update.
Django's model documentation says in this case it should be sending an UPDATE.
Anyone have and ideas on why it's missing these updates?
I can provide further details regarding our specific Django/tastypie setup, but as long as they are issuing an UPDATE, they should be triggering the databases ON UPDATE.
I suspect that the UPDATE statement issued by Django may be including an assignment to the last_updated column. This is just a guess, there's not enough information provided.
But if the Django model contains the last_updated column, and that column is fetched from the database into the model, I believe a save() will assign a value to the last_updated column, in the UPDATE statement.
https://docs.djangoproject.com/en/1.9/ref/models/instances/#specifying-which-fields-to-save
Consider the behavior when we issue an UPDATE statement like this:
UPDATE mytable
SET last_updated = last_updated
, some_col = 'some_value'
WHERE id = 42
Because the UPDATE statement is assigning a value to the last_updated column, the automatic assignment to the timestamp column won't happen. The value assigned in the statement takes precedence.
To get the automatic assignment to last_updated, that column has to be omitted from the SET clause, e.g.
UPDATE mytable
SET some_col = 'some_value'
WHERE id = 42
To debug this, you'd want to inspect the actual SQL statement.
With the added information from spencer7593's answer, I was able to track down how to do this through tastypie:
The BaseModelResource.save() (from tastypie/resources.py):
def save(self, bundle, skip_errors=False):
if bundle.via_uri:
return bundle
self.is_valid(bundle)
if bundle.errors and not skip_errors:
raise ImmediateHttpResponse(response=self.error_response(bundle.request, bundle.errors))
# Check if they're authorized.
if bundle.obj.pk:
self.authorized_update_detail(self.get_object_list(bundle.request), bundle)
else:
self.authorized_create_detail(self.get_object_list(bundle.request), bundle)
# Save FKs just in case.
self.save_related(bundle)
# Save the main object.
obj_id = self.create_identifier(bundle.obj)
if obj_id not in bundle.objects_saved or bundle.obj._state.adding:
bundle.obj.save()
bundle.objects_saved.add(obj_id)
# Now pick up the M2M bits.
m2m_bundle = self.hydrate_m2m(bundle)
self.save_m2m(m2m_bundle)
return bundle
Needs to be overridden in your class, so that you can change the Django save(), which has the update_fields parameter we want to modify:
if obj_id not in bundle.objects_saved or bundle.obj._state.adding:
bundle.obj.save()
To, for example:
class ResourcesResource(ModelResource):
# ...
def save(self, bundle, skip_errors=False):
# ...
if obj_id not in bundle.objects_saved or bundle.obj._state.adding:
resource_fields = [field.name for field in Resources._meta.get_fields()
if not field.name in ['id', 'last_updated']]
bundle.obj.save(update_fields=resource_fields)
# ...
This properly excludes the last_updated column from the sql UPDATE.

Using the SQL initialization hook with ManytoManyField

I'm fairly new to Django and I'm trying to add some 'host' data to 'record' using django's hook for using SQL to initialise (a SQL file in lowercase in the app folder & sql subfolder)
Here's the models:
class Record(models.Model):
species = models.TextField(max_length = 80)
data=models.TextField(max_length = 700)
hosts = models.ManyToManyField('Host')
class Host(models.Model):
hostname = models.TextField()
I've used a ManyToManyField as each record should be able to have multiple hosts, and hosts should be 'reusable': ie be able to appear in many records.
When I'm trying to insert via SQL I have
INSERT INTO myapp_record VALUES ('Species name', 'data1', XYZ);
I'm not sure what to put for XYZ (the ManytoMany) if I wanted hosts 1, 2 and 3 for example
Separating them by commas doesn't work obviously, and I tried a tuple and neither did that.
Should I be trying to insert into the intermediary table Django makes? Does that have a similar hook to the one I'm using? If not, how can I execute SQL inserts on this table?
The use of initial SQL data files is deprecated. Instead, you should be using a data migration, which might look something like this:
from django.db import models, migrations
def create_records(apps, schema_editor):
# We can't import the Person model directly as it may be a newer
# version than this migration expects. We use the historical version.
Record = apps.get_model("yourappname", "Record")
Host = apps.get_model("yourappname", "Host")
host1 = Host.objects.get(hostname='host1')
record = Record.objects.create(name='Species name', data='Data')
record.hosts.add(host1)
...etc...
class Migration(migrations.Migration):
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
migrations.RunPython(create_records),
]

How to select and limit the related_name connection in the Peewee ORM?

I'm using Flask with the Peewee ORM in which I have defined two tables like so:
class Ticket(db.Model):
created = DateTimeField(default=datetime.now)
customer_uuid = CharField() # the customer's UUID gotten from App. More info comes from bunq API.
ticket_type = ForeignKeyField(TicketType, related_name='tickets')
active = BooleanField(default=True)
class Assign(db.Model):
created = DateTimeField(default=datetime.now)
ticket = ForeignKeyField(Ticket, related_name='assigned_to')
user = ForeignKeyField(User, related_name='assigned_tickets')
In the Assign table, several users can be assigned to a ticket, but only the last one counts (i.e., if a new user gets assigned, the previous ones should be disregarded). So I select the active tickets using the following:
open_tickets = Ticket.select().where(Ticket.active == True)
I now want to use this loop in my template. With every iteration however, I also want to display the assigned user. But open_ticket[0].assigned_to obviously returns several assignments, and with it several users.
Would anybody know how I can get the latest assigned user for every ticket within a loop?
This worked for me in Sqlite:
q = (Ticket
.select(Ticket, Assign, User)
.join(Assign)
.join(User)
.group_by(Ticket)
.order_by(Ticket.id, Assign.id.desc()))
for ticket in q:
print ticket.id, ticket.assign.user.username

Categories