django model: get primary key of the model just created - python

The Model is defined as below:
class UnitTab(models.Model):
unit_id = models.PositiveIntegerField(primary_key=True)
create_time = models.PositiveIntegerField()
update_time = models.PositiveIntegerField()
read_db_connection = 'game_center_admin_db'
write_db_connection = 'game_center_admin_db'
class Meta:
db_table = u'unit_tab'
def save(self, *args, **kwargs):
curr_time = int(time.time())
if not self.create_time:
self.create_time = curr_time
self.update_time = curr_time
super(UnitTab, self).save(*args, **kwargs)
def __unicode__(self):
return u'unit_tab_%s' % (self.unit_id)
And I am just saving the a UnitTab with UnitTab.objects.create() to create a new object. The unit_id has auto_increment, so I did not have to set it.
But if I use "u = UnitTab.objects.create()", the object "u" I get back is with unit_id as None — although the save is successful. So how can I can get the primary key(unit_id) of the UnitTab I just saved/created?
Edit: I am using Django 1.6.11 for my project
Edit: I logged those attributes and found that after u.save(), the unit_id is None. create_time is OK and the entity is saved successfully.
Edit: After changing from PositiveIntegerField to AutoField the unit_id field is auto assigned after save(). Just not sure why this is the case.

After you create your new object:
u = UnitTab.objects.create()
follow it up by refreshing its value from the db using the refresh_from_db() method.
u.refresh_from_db()
You can find this info here:
https://docs.djangoproject.com/en/1.9/ref/models/instances/#refreshing-objects-from-database
EDIT:
If you're on django 1.6, use the save() method and you should be able to access it like this:
>>> b2 = Blog(name='Cheddar Talk', tagline='Thoughts on cheese.')
>>> b2.id # Returns None, because b doesn't have an ID yet.
>>> b2.save()
>>> b2.id # Returns the ID of your new object.
https://docs.djangoproject.com/en/1.6/ref/models/instances/#auto-incrementing-primary-keys

Is there a particular reason you don't want to use models.AutoField instead?
unit_id = models.AutoField(primary_key=True)

Related

Custom Id in Django Models

In my model I need an ID field that is different from the default ID given by Django. I need my IDs in the following format: [year][ascending number]
Example: 2021001,2021002,2021003
The IDs shall not be editable but model entries shall take the year for the ID from a DateTimeField in the model. I am unsure if I use a normal Django ID and also create some kind of additional ID for the model or if I replace the normal Django ID with a Custom ID.
This problem is pretty similar to one I had solved for a previous project of mine. What I had done for this was to simply use the default id for the primary key, while using some extra fields to make the composite identifier needed.
To ensure the uniqueness and the restarting of the count I had made a model which would (only by the logic, no actual constraints) only have one row in it. Whenever a new instance of the model which needs this identifier would be created this row would be updated in a transaction and it's stored value would be used.
The implementation of it is as follows:
from django.db import models, transaction
import datetime
class TokenCounter(models.Model):
counter = models.IntegerField(default=0)
last_update = models.DateField(auto_now=True)
#classmethod
def get_new_token(cls):
with transaction.atomic():
token_counter = cls.objects.select_for_update().first()
if token_counter is None:
token_counter = cls.objects.create()
if token_counter.last_update.year != datetime.date.today().year:
token_counter.counter = 0
token_counter.counter += 1
token_counter.save()
return_value = token_counter.counter
return return_value
def save(self, *args, **kwargs):
if self.pk:
self.__class__.objects.exclude(pk=self.pk).delete()
super().save(*args, **kwargs)
Next suppose you need to use this in some other model:
class YourModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
yearly_token = models.IntegerField(default=TokenCounter.get_new_token)
#property
def token_number(self):
return '{}{}'.format(self.created_at.year, str(self.yearly_token).zfill(4))
#classmethod
def get_from_token(cls, token):
year = int(token[:4])
yearly_token = int(token[4:])
try:
obj = cls.objects.get(created_at__year=year, yearly_token=yearly_token)
except cls.DoesNotExist:
obj = None
return obj
Note: This might not be very refined as the code was written when I was very inexperienced, and there may be many areas where it can be refined. For example you can add a unique_for_year in the yearly_token field so:
yearly_token = models.IntegerField(default=TokenCounter.get_new_token, unique_for_year='created_at')

Django generate custom ID

I saw this answer but there is no specific answer yet. I want to create custom id that starts with letter. When a new record comes into database I want to change the id to A00001, .... A00002, .... A00010, ...A10000 etc. The id will be always in range 99999- 00001 so how can I do that?
my model is simple:
class Custom(models.Model):
id = models.AutoField(primary_key=True, editable=False)
The AutoField field is a kind of IntegerField field, so you can't use PKs as A00001 .
So, the possible way to achieve the requirement is to change the AutoField to CharField.
Technically you can use "String PK Field" But, you should be aware of the problems/performance issues if you are going to use that.
Here I found one nice SO post that explains the same - Strings as Primary Keys in SQL Database========================================================================
If you still really wish to migrate to String PKs, read the following
First you need to use the CharField instead of AutoField and override the save() method of model
from django.db.models import Max
class Custom(models.Model):
id = models.CharField(primary_key=True, editable=False, max_length=10)
name = models.CharField(max_length=100)
def save(self, **kwargs):
if not self.id:
max = Custom.objects.aggregate(id_max=Max('id'))['id_max']
self.id = "{}{:05d}".format('A', max if max is not None else 1)
super().save(*kwargs)
string as Primary Key not good idea if you plan to do references to the table, so i recommend you to add a property, for example:
class Custom(models.Model):
id = models.AutoField(primary_key=True, editable=False)
#property
def sid(self):
return "A%05d" % self.id
and to do queries you can do processing the input values, for example:
s_input = "A%05d" % 231 # 'A00231'
number = s_input[1:] # '00231'
input_id = int(number) # 231
I also have another way, That i use in my django project. Here are some code
def ids():
no = Employee.objects.count()
if no == None:
return 1
else:
return no + 1
emp_id = models.IntegerField(('Code'), default=ids, unique=True, editable=False)
id = models.CharField(primary_key=True, editable=False, max_length=30)
def save(self, **kwargs):
if not self.id:
self.id = "{}{:08d}".format('ABC', self.emp_id)
super().save(*kwargs)
It's better to create a new field for the custom id in the models and the process in the backend. You can set that as primary_key with unique=True and editable=False:
class Custom(models.Model):
id = models.Autofield(primary_key=True, editable=False, max_length=10)
uid= models.CharField(max_length=100, unique=True)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
self.set_uid() # calling the set_uid function
def set_uid(self):
if not self.uid: # if uid of the instance is blank
uid = "CUS" + str(self.id + (10 ** 5)) # generating the uid
customer= Custom.objects.get(id=self.id) # getting the instance
customer.uid = uid # allocating the value
customer.save() # saving the instance
def __str__(self):
return self.uid
Can also merge the set_uid() inside the save() where the function is called:
class Custom(models.Model):
id = models.Autofield(primary_key=True, editable=False, max_length=10)
uid= models.CharField(max_length=100, unique=True)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if not self.uid: # if uid of the instance is blank
self.uid = "CUS" + str(self.id + (10 ** 5)) # generating the uid and allocating the value
self.save() # saving the instance
def __str__(self):
return self.uid
I tried to use answer of #JPG, but it has a bug.
The bug is becasue it can't auto increment.
I fixed the bug, and this my resultant code:
def save(self, **kwargs):
if not self.id:
max = YourModel.objects.aggregate(
id_max=models.Max('id'))['id_max']
if max is not None:
max += 1
else:
max = 100
self.id = "{:08d}".format(
max) # id from 100 to start
super().save(*kwargs)

Django ORM Issue

I am learning RestAPI and When I try to post data to update my database columns the modified_on column should automatically populated to current date and time but it is not updating.
I am currently using django cassandra engine ORM where there is no functionality like auto_add_now() or auto_now().
Can any one give a suggestion where am I going wrong?
Model Class:
class Mydb(DjangoCassandraModel):
id = columns.UUID(primary_key=True, default=uuid.uuid4())
user_name = columns.Text()
user_email = columns.Text(default=None)
user_password = columns.Text()
description = columns.Text()
creation_date = columns.DateTime(default=datetime.datetime.today(), static=True)
modified_on = columns.DateTime(default=datetime.datetime.today())
My Serialization class:
class TaskSerializer(serializers.Serializer):
# id = serializers.UUIDField(default=uuid.uuid4)
USER_ID = serializers.UUIDField(default= uuid.uuid4(),source='id')
# user_name = serializers.CharField(max_length=50)
USER_NAME_FIELD = serializers.CharField(max_length=50, source='user_name')
USER_EMAIL = serializers.CharField(source='user_email')
USER_PASSWORD = serializers.CharField(max_length=20, source='user_password')
EXPLANATION = serializers.CharField(max_length=100, source='description')
MODIFIED_AT = serializers.DateTimeField(default=datetime.datetime.today(), source='modified_on')
CREATED_ON = serializers.DateTimeField(default=datetime.datetime.today(), source='creation_date')
def create(self, validated_data):
return Mydb.objects.create(**validated_data)
def update(self, instance, validated_data):
# instance.id = validated_data.get('id', instance.id)
instance.user_name = validated_data.get('user_name', instance.user_name)
instance.user_email = validated_data.get('user_email', instance.user_email)
instance.user_password = validated_data.get('user_password', instance.user_password)
instance.description = validated_data.get('description',instance.description)
instance.modified_on = validated_data.get('modified_on', instance.modified_on)
instance.save()
# instance.creation_date = validated_data.get('creation_date', instance.creation_date)
You should rather use utils now for timezone aware times
from django.utils.timezone import now
also in model you should set function not evaluated value ( no parenthesis after now )
MODIFIED_AT = serializers.DateTimeField(default=now, source='modified_on')
MODIFIED_AT = serializers.DateTimeField(default=datetime.datetime.today(), source='modified_on')
to
MODIFIED_ON = serializers.DateField(default=datetime.datetime.today(), source='modified_on')
change MODIFIED_AT to MODIFIED_ON
You can try:
create_date = models.DateField(auto_now_add=True,
verbose_name=u'Create date')
update_date = models.DateTime(auto_now=True,
verbose_name=u'Update date')
auto_now_add automatically set the field to now when the object is first created.
auto_now=True automatically set the field to now every time the object is saved.
Doc is here.
Please make sure to add the auto_now=True for your modified_at filed, in your model.
It automatically sets the field to now every time the object is saved. Useful for “last-modified” timestamps. Note that the current date is always used; it’s not just a default value that you can override.
Example:
class Mydb(DjangoCassandraModel):
creation_date = columns.DateTime(auto_now_add=True)
modified_on = columns.DateTime(auto_now=True)
Docs Here:
https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.DateField.auto_now
You use default=datetime.datetime.today() as your default value for the fields. Since you call the function immediately (by adding ()), the function is called exactly once on the first load of the code and the datetime at that moment is put into the default value and not updated until you reload the code (a.k.a. restart the server).
If you want to always use the then current time, leave away the () to cause Django to call the function each time. default=datetime.datetime.today
It's preferable for you to use now though, like iklinac did in his answer, as that also respects your timezone settings. His anwer also leaves out the parenteses, yielding the correct result.
from django.utils.timezone import now
...
MODIFIED_AT = serializers.DateTimeField(default=now, source='modified_on')

concatenate auto field with string using django

I want to generate an unique string in my django1.6 application. For that I want to concatenate the primary key (autofield) with the string. My code is
class Complaints(models.Model):
gid = models.AutoField(primary_key=True)
complaint_no = models.CharField(max_length=50)
status = models.IntegerField(default=0,blank=False,null=False)
def __str__(self):
return ('%s %s' % (self.gid, self.complaint_no))
def save(self, force_insert=False, force_update=False):
self.complaint_no = '%s%d' % (self.complaint_no,self.gid)
super(Complaints, self).save(force_insert, force_update)
class Meta:
db_table = 'complaints'
verbose_name = "complaints"
But I got the error message as
TypeError at /callcentre/register/
%d format: a number is required, not NoneType
Please help me to solve this error!
Create field id as charfield e.g.
string_id = models.CharField(max_length=100, default='file')
Make a function that make string id when the model is saved.
def save(self):
new_id = self.id
self.string_id = str(new_id) + '_dot'
super(YourModelName, self).save()
You need to call super() first.
As you can read in the documentation:
There’s no way to tell what the value of an ID will be before you call save(), because that value is calculated by your database, not by Django.
When you call super().save(), the database engine calculates the id (or gid in your case, but please reconsider using the provided id) and thus allows you to use it. Before that it is None.
You may check this topic as well.

Django UpdateView wont delete tha old entry if primary key is edited

So lets say we have this model:
class Student(models.Model):
am = models.SmallIntegerField(unique=True, primary_key=True) # XXX: max_value = 10000
date_enrolled = models.DateField('Date Enrolled')
semester = models.IntegerField(default=1)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
undergraduate = models.BooleanField(default=True)
An update view like this:
class StudentUpdateView(SqlPresenterMixin, StudentMixin, UpdateView):
model = Student
form_class = StudentForm
template_name = "profapp/student_form.html"
slug_field = "am"
Then this test fails:
class TestStudent(TestCase):
def setUp(self):
self.c = Client()
Student.objects.create(am=2222, first_name="Chris",
last_name="Perivolas",
date_enrolled=datetime.date(year=2010, day=15,
month=2))
Student.objects.create(am=7362, first_name="Mary",
last_name="Karagewrgena",
date_enrolled=datetime.date(year=2010, day=15,
month=2))
def test_update(self):
"""
"""
r = self.c.post("/profapp/students/2222/update/",
dict(am=7363, first_name="Chris",
last_name="Perivolas",
date_enrolled="3/15/2010",
semester=2,
undergraduate=1))
self.assertEquals(Student.objects.filter(am=2222).exists(), False)
In short update view doesn't delete the old entry when updating a primary key. What is the best way of solving this?
According to Django documentation, PK should never change. PK is what ties your object to a specific row in the DB. When you change the PK, Django loses the connection to the original row in the DB and assumes that you want to create another row.
You should add another field to act as changeable id if you really need it.

Categories