Uploaded and Deadline fields in Django models - django

I am making a to do app and in models I have a Task model and dateTimeFields "uploaded", which has auto_now_add=True. Now I want to add deadline field, where users can add the task deadlines. I tried adding second DateTimeField but there's a problem when I make migrations. here's the code and the powershell warning:
models:
class Task(models.Model):
title = models.CharField(max_length=50)
completed = models.BooleanField(default=False)
uploaded = models.DateTimeField(auto_now_add=True)
deadline = models.DateTimeField()
def __str__(self):
return self.title
powershell:
*You are trying to add a non-nullable field 'deadline' to task without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
Provide a one-off default now (will be set on all existing rows with a null value for this column)
Quit, and let me add a default in models.py
Select an option:*

But there's a problem when I make migrations.
The problem is that the database might already have records, so what will you do to the Tasks in the database that now need a value for the existing records.
You can for example decide to work with the current timestamp. You can do this by selecting 1, and then work with timezone.now:
You are trying to add a non-nullable field 'deadline' to task without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
Select an option: 1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>> timezone.now
This will thus set the current timestamp to all the Tasks already in the database.
You can also decide to set it to a datetime in the future, for example 1-1-2100:
You are trying to add a non-nullable field 'deadline' to task without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
Select an option: 1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>> datetime.datetime(2100, 1, 1)
Another alternative is to provide the default in the model itself, but then it also will take that default for new tasks and thus not only the tasks already recorded.
Often a field is made nullable in case one does not have to specify the current timestamp. In that case you thus specify null=True, and by default it will use NULL/None as default for the exiting records:
class Task(models.Model):
# …
deadline = models.DateTimeField(null=True)
# …

If deadline is added afterwards you create the model, you need to tell django that its a null able field, such that deadline can have no value. just add null=True in deadline field
class Task(models.Model):
title = models.CharField(max_length=50)
completed = models.BooleanField(default=False)
uploaded = models.DateTimeField(auto_now_add=True)
deadline = modles.DateTimeField(null=True)
def __str__(self):
return self.title

Related

django update_or_create gets "duplicate key value violates unique constraint "

Maybe I misunderstand the purpose of Django's update_or_create Model method.
Here is my Model:
from django.db import models
import datetime
from vc.models import Cluster
class Vmt(models.Model):
added = models.DateField(default=datetime.date.today, blank=True, null=True)
creation_time = models.TextField(blank=True, null=True)
current_pm_active = models.TextField(blank=True, null=True)
current_pm_total = models.TextField(blank=True, null=True)
... more simple fields ...
cluster = models.ForeignKey(Cluster, null=True)
class Meta:
unique_together = (("cluster", "added"),)
Here is my test:
from django.test import TestCase
from .models import *
from vc.models import Cluster
from django.db import transaction
# Create your tests here.
class VmtModelTests(TestCase):
def test_insert_into_VmtModel(self):
count = Vmt.objects.count()
self.assertEqual(count, 0)
# create a Cluster
c = Cluster.objects.create(name='test-cluster')
Vmt.objects.create(
cluster=c,
creation_time='test creaetion time',
current_pm_active=5,
current_pm_total=5,
... more simple fields ...
)
count = Vmt.objects.count()
self.assertEqual(count, 1)
self.assertEqual('5', c.vmt_set.all()[0].current_pm_active)
# let's test that we cannot add that same record again
try:
with transaction.atomic():
Vmt.objects.create(
cluster=c,
creation_time='test creaetion time',
current_pm_active=5,
current_pm_total=5,
... more simple fields ...
)
self.fail(msg="Should violated integrity constraint!")
except Exception as ex:
template = "An exception of type {0} occurred. Arguments:\n{1!r}"
message = template.format(type(ex).__name__, ex.args)
self.assertEqual("An exception of type IntegrityError occurred.", message[:45])
Vmt.objects.update_or_create(
cluster=c,
creation_time='test creaetion time',
# notice we are updating current_pm_active to 6
current_pm_active=6,
current_pm_total=5,
... more simple fields ...
)
count = Vmt.objects.count()
self.assertEqual(count, 1)
On the last update_or_create call I get this error:
IntegrityError: duplicate key value violates unique constraint "vmt_vmt_cluster_id_added_c2052322_uniq"
DETAIL: Key (cluster_id, added)=(1, 2018-06-18) already exists.
Why didn't wasn't the model updated? Why did Django try to create a new record that violated the unique constraint?
The update_or_create(defaults=None, **kwargs) has basically two parts:
the **kwargs which specify the "filter" criteria to determine if such object is already present; and
the defaults which is a dictionary that contains the fields mapped to values that should be used when we create a new row (in case the filtering fails to find a row), or which values should be updated (in case we find such row).
The problem here is that you make your filters too restrictive: you add several filters, and as a result the database does not find such row. So what happens? The database then aims to create the row with these filter values (and since defaults is missing, no extra values are added). But then it turns out that we create a row, and that the combination of the cluster and added already exists. Hence the database refuses to add this row.
So this line:
Model.objects.update_or_create(field1=val1,
field2=val2,
defaults={
'field3': val3,
'field4': val4
})
Is to semantically approximately equal to:
try:
item = Model.objects.get(field1=val1, field2=val2)
except Model.DoesNotExist:
Model.objects.create(field1=val1, field2=val2, field3=val3, field4=val4)
else:
item = Model.objects.filter(
field1=val1,
field2=val2,
).update(
field3 = val3
field4 = val4
)
(but the original call is typically done in a single query).
You probably thus should write:
Vmt.objects.update_or_create(
cluster=c,
creation_time='test creaetion time',
defaults = {
'current_pm_active': 6,
'current_pm_total': 5,
}
)
(or something similar)
You should separate your field:
Fields that should be searched for
Fields that should be updated
for example:
If I have the model:
class User(models.Model):
username = models.CharField(max_length=200)
nickname = models.CharField(max_length=200)
And I want to search for username = 'Nikolas' and update this instance nickname to 'Nik'(if no User with username 'Nikolas' I need to create it) I should write this code:
User.objects.update_or_create(
username='Nikolas',
defaults={'nickname': 'Nik'},
)
see in https://docs.djangoproject.com/en/3.1/ref/models/querysets/
This is already answered well in the above.
To be more clear the update_or_create() method should have **kwargs as those parameters on which you want to check if that data already exists in DB by filtering.
select some_column from table_name where column1='' and column2='';
Filtering by **kwargs will give you objects. Now if you wish to update any data/column of those filtered objects, you should pass them in defaults param in update_or_create() method.
so lets say you found an object based on a filter now the default param values are expected to be picked and updated.
and if there's no matching object found based on the filter then it goes ahead and creates an entry with filters and the default param passed.

unique_together in Django doesn't work

unique_together doesn't work, it only set the unique constraints on the first field and ignore the second field. Is there any way to enforce unique constraints?
class BaseModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
deleted = models.DateTimeField(db_index=True, null=True, blank=True)
last_modified_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Book(BaseModel):
first_form_number = models.CharField(max_length=8)
class Meta:
unique_together = (("first_form_number", "deleted"),)
Your models work correctly in that extent that the right unique index is created:
$ python manage.py sqlmigrate app 0001_initial
...
CREATE UNIQUE INDEX "app_base_slug_version_a455c5b7_uniq" ON "app_base" ("slug", "version");
...
(expected like the name of your application is "app")
I must roughly agree with user3541631's answer. It depends on the database in general, but all four db engines supported directly by Django are similar. They expect that "nulls are distinct in a UNIQUE column" (see NULL Handling in SQLite Versus Other Database Engines)
I verified your problem with and without null:
class Test(TestCase):
def test_without_null(self):
timestamp = datetime.datetime(2017, 8, 25, tzinfo=pytz.UTC)
book_1 = Book.objects.create(deleted=timestamp, first_form_number='a')
with self.assertRaises(django.db.utils.IntegrityError):
Book.objects.create(deleted=timestamp, first_form_number='a')
def test_with_null(self):
# this test fails !!! (and a duplicate is created)
book_1 = Book.objects.create(first_form_number='a')
with self.assertRaises(django.db.utils.IntegrityError):
Book.objects.create(first_form_number='a')
A solution is possible for PostgreSQL if you are willing to manually write a migration to create two special partial unique indexes:
CREATE UNIQUE INDEX book_2col_uni_idx ON app_book (first_form_number, deleted)
WHERE deleted IS NOT NULL;
CREATE UNIQUE INDEX book_1col_uni_idx ON app_book (first_form_number)
WHERE deleted IS NULL;
See:
Answer for Create unique constraint with null columns
Django docs Writing database migrations
Django docs migrations.RunSQL(sql)
depending on your database, it is possible that NULL isn't equal to any other NULL.
Therefore the rows you create are not the same, if one of the values is NULL, will be unique only by the non null field, in your case 'first_form_number'.
Also take in consideration that is case sensitive so "char" and "Char" are not the same.
I had a similar situation and I did my own check by overriding the save method on the model.
You check if exist in the database, but also exclude the current instance, in case of updating, not to compare with itself..
if not deleted:
exists = model.objects.exclude(pk=instance.pk).filter(first_form_number__iexact=first_form_number).exists()
Make sure you actually extend the inherited Meta class, rather than defining your own Meta class (which is ignored by Django):
class Meta(BaseModel.Meta):
unique_together = (("first_form_number", "deleted"),)

Make a change to date fields automatically in Django

I have 2 date fields and I want to update the date automatically.
The code won't work until I update the updated field by myself.
How can I make it update itself (updated fields) automatically?
STATUS_CHOICES = (
('P', 'Paid'),
('U', 'UNPAID'),
)
status = models.CharField(
max_length=1, choices=STATUS_CHOICES)
updated = models.DateTimeField(default=datetime.now() )
expiry = models.DateTimeField(default=datetime.now() + timedelta(days=30) )
def save(self):
if(self.expiry >= self.updated):
self.status = default = "P"
else:
self.status = default = "U"
self.expiry = default=self.updated+timedelta(days=1)
super(Users, self).save()
The DateTimeField have a auto_now property, that will set the field to the current date every time the object is saved.
The main question is what event should trigger the save action. For example you can improve your view, so that every time someone visit the page, your model would update as well as desired DateTimeField. On the other hand you may want this happen on the schedule basis, so you should use something like Cron, Celery or Huey.
In your case you have two options.
Make some code that will be executed periodically that will update the status based on current date.
Do not store status in the database at all. If it can depend only from expiry field it can be a computed property
P.S.
It's not related to what you are asking but I have a suggestion for you. Never ever use the direct result from datetime.now() as default value for DateTimeField. It's misleading for you and it's not the same as what you want to achieve. datetime.now will be executed at import time (when the server starts) and will stay the same until next restart.
If you want the default value to be the exact time when the record was created then default value need to be a function (which will be executed every time) and not the result of the function.
This means that updated need to be DateTimeFiled(default=datetime.now) (without the brackets).
This logic is more like for a field called created_at or date_created and not updated but this is your logic.

Django: How to filter on inner join based on properties of second table?

My question is simple: in a Django app, I have a table Users and a table StatusUpdates. In the StatusUpdates table, I have a column user which is a foreign key pointing back to Users. How can I do a search expressing something like:
users.filter(latest_status_update.text__contains='Hello')
Edit:
Please excuse my lack of clarity. The query that I would like to make is something like "Give me all the users whose latest status update contains the text 'hello'". In Django code, I would do the following (which is really inefficient and ugly):
hello_users = []
for user in User.objects.all():
latest_status_update = StatusUpdate.objects.filter(user=user).order_by('-creation_date')[0]
if latest_status_update.text.contains('Hello'):
hello_users.append(user)
return hello_users
Edit 2:
I've already found the solution but since I was asked, here are the important parts of my models:
class User(models.Model):
...
class StatusUpdate(models.Model):
user = models.ForeignKey(User)
text = models.CharField(max_length=140)
creation_date = models.DateTimeField(auto_now_add=True, editable=False)
....
Okay, I think I got it:
from django.db.models import Max, F
User.objects\
.annotate(latest_status_update_id=Max('statusupdate__id'))\
.filter(
statusupdate__id=F('latest_status_update_id'),
statusupdate__text__icontains='hello'
)
For more info, see this section of the Django documentation.
Please note: I ended up changing my strategy a bit and settling for the strategy where the highest ID means the latest update. This is the case because I realized that a User could post two updates the same time and that would break my query.
latest_status_updates = filter(lambda x: x.text.contains('hello'),
[
user.statusupdates_set.order_by('-creation_date').first()
for user in User.objects.all()
]
)
users = list(set([status_update.user for status_update in latest_status_updates]))
EDIT:
Now I first get all LATEST status updates of each user into a list which is then filtered by the text field found in StatusUpdate class. In the second line, I extract users out of the filtered status updates and then produce a unique list of users.
I hope this helps!
Not sure I understand, are you trying to do something like
(StatusUpdates
.objects
.select_related("user")
.filter(text__contains = "hello")
.order_by("-updated")
.first())
This will return the StatusUpdate that was modified last (if you have a field called updated that stores the time of the last modification) which contains "Hello" in the text field. If none of the StatusUpdates contains that string, it will return None.
Then you can do:
latest = (StatusUpdates
.objects
.select_related("user")
.filter(text__contains = "hello")
.order_by("-updated")
.first())
#then if you needed the user too
if latest is not None:
user = latest.user #which does not call the DB again since you selected related`
If this isn't what you needed, please provide more details (models) and clarify your need

Django Custom Primary Key Tries to insert Null

There are some constraints to this problem. We currently use a production database, with live Virtual Machine Statistics. We are trying to create a django interface, that interfaces with the tables we want our administrators to be able to edit. Thus, migrations are out of the question, for unless I have come to understand migrations wrong it will affect the current database structure and or data.
I matched the database structure exactly in my models.py file. However I have run into a few issues. One of the issues I have run into is when I try to add a new item under the admin control panel it will give me an integrity error as it is attempting to insert a null value for the field I have set as the primary key in the models.py file.
We are currently using an oracle database.
My Models.py not all of it but a sample of it.
class License(models.Model):
license_id = models.AutoField(primary_key = True, editable = False, db_column='license_id')
license_authority_id = models.ForeignKey(License_authoritie, on_delete = models.PROTECT, db_column='license_authority_id')
product = models.CharField(max_length = 20)
class Meta:
managed = False
db_table = 'licenses'
ordering = ['product']
def __unicode__(self): # Python 3: def __str__(self):
return self.product
class Vm_license(models.Model):
vm_license_id = models.AutoField(primary_key = True, db_column='vm_license_id')
vm_id = models.ForeignKey(Vm, on_delete = models.PROTECT, db_column='vm_id')
license = models.ManyToManyField(License)
class Meta:
managed = False
db_table = 'vm_licenses'
The error I get:
Request Method: POST
Request URL: http://127.0.0.1:8000/admin/portal/vm_license/add/
Django Version: 1.6.5
Exception Type: IntegrityError
Exception Value:
ORA-01400: cannot insert NULL into ("DEV"."VM_LICENSES"."VM_LICENSE_ID")
On top of that I have run into another problem.
For these two tables, under the vm_licenses section in the admin panel which is a table that holds all VM's and their assigned licenses. I need the ability to select multiple licenses at a time for each vm_id under the add section of the admin panel but i'm not quite sure how to do this.
admin.py code
class vm_license_admin(admin.ModelAdmin):
#list_display = ('vm_id', 'license_id')
list_display = ('vm_id',)
search_fields = ('vm_id__vm_name',)
ordering = ('vm_id',)
filter_horizontal = ('license',)
admin.site.register(Vm_license, vm_license_admin)
I also made an oracle trigger to auto increment a primary key if there is none, but im still getting the same error.
CREATE OR REPLACE TRIGGER license_trigger
BEFORE INSERT ON vm_licenses
FOR EACH ROW
BEGIN
SELECT vm_license_seq.nextval
INTO :new.vm_license_id
FROM dual;
END;
to be more percise I am using a manytomany field and it displays correctly when I goto add a new item before clicking save and getting the null error, however if I goto an existing item it will say table or view doesnt exist.
I was going to comment on your question, but I do not have the reputation yet...
but can I suggest you post your relevant admin.py code? Perhaps there is something within it relating to the Null PK error.
With regards to the second part, a ManyToManyField sounds more suitable.