How do I make an auto increment integer field in Django? - python

I am making an Order model for a shopping cart and I need to make a field that auto increments when the order is made:
class Order(models.Model):
cart = models.ForeignKey(Cart)
add_date = models.DateTimeField(auto_now_add=True)
order_number = models.IntegerField()
enable = models.BooleanField(default=True)
How do I make the IntegerField auto increment?

In Django
1 : Django model class has default field with name id which is auto increment Field.
2 : You can define your own auto increment field using AutoField
field.
class Order(models.Model):
auto_increment_id = models.AutoField(primary_key=True)
# primary_key = True if you do not want to use default field "id" given by django to your model
db design
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+
| core_order | CREATE TABLE `core_order` (
`auto_increment_id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`auto_increment_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)
If you want to use django's default id as increment field .
class Order(models.Model):
add_date = models.DateTimeField(auto_now_add=True)
db design
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+
| core_order | CREATE TABLE `core_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`add_date` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+

In django with every model you will get the by default id field that is auto increament. But still if you manually want to use auto increment. You just need to specify in your Model AutoField.
class Author(models.Model):
author_id = models.AutoField(primary_key=True)
you can read more about the auto field in django in Django Documentation for AutoField

class Belly(models.Model):
belly_id = models.AutoField(primary_key=True)
belly_name = models.CharField(max_length=50)
******** or *******
class Belly(models.Model):
belly_name = models.CharField(max_length=50)
The difference is:
The first table has the primary key belly_id (specified as AutoField) and second table has the primary key id (implicitly).
I think no need to use this directly; a primary key field will automatically be added to your model if you don’t specify. Otherwise
Check the Django Documentation for AutoField for further details related to AutoField.

You can create an autofield. Here is the documentation for the same
Please remember Django won't allow to have more than one AutoField in a model, In your model you already have one for your primary key (which is default). So you'll have to override model's save method and will probably fetch the last inserted record from the table and accordingly increment the counter and add the new record.
Please make that code thread safe because in case of multiple requests you might end up trying to insert same value for different new records.

Edited: Fixed mistake in code that stopped it working if there were no YourModel entries in the db.
There's a lot of mention of how you should use an AutoField, and of course, where possible you should use that.
However there are legitimate reasons for implementing auto-incrementing fields yourself (such as if you need an id to start from 500 or increment by tens for whatever reason).
In your models.py
from django.db import models
def from_500():
'''
Returns the next default value for the `ones` field,
starts from 500
'''
# Retrieve a list of `YourModel` instances, sort them by
# the `ones` field and get the largest entry
largest = YourModel.objects.all().order_by('ones').last()
if not largest:
# largest is `None` if `YourModel` has no instances
# in which case we return the start value of 500
return 500
# If an instance of `YourModel` is returned, we get it's
# `ones` attribute and increment it by 1
return largest.ones + 1
def add_ten():
''' Returns the next default value for the `tens` field'''
# Retrieve a list of `YourModel` instances, sort them by
# the `tens` field and get the largest entry
largest = YourModel.objects.all().order_by('tens').last()
if not largest:
# largest is `None` if `YourModel` has no instances
# in which case we return the start value of 10
return 10
# If an instance of `YourModel` is returned, we get it's
# `tens` attribute and increment it by 10
return largest.tens + 10
class YourModel(model.Model):
ones = models.IntegerField(primary_key=True,
default=from_500)
tens = models.IntegerField(default=add_ten)

You can override Django save method official doc about it.
The modified version of your code:
class Order(models.Model):
cart = models.ForeignKey(Cart)
add_date = models.DateTimeField(auto_now_add=True)
order_number = models.IntegerField(default=0) # changed here
enable = models.BooleanField(default=True)
def save(self, *args, **kwargs):
self.order_number = self.order_number + 1
super().save(*args, **kwargs) # Call the "real" save() method.
Another way is to use signals. More one:
official Django docs about pre-save
stackoverflow example about using pre-save signal

What I needed: A document number with a fixed number of integers that would also act like an AutoField.
My searches took me all over incl. this page.
Finally I did something like this:
I created a table with a DocuNumber field as an IntegerField with foll. attributes:
max_length=6
primary_key=True
unique=True
default=100000
The max_length value anything as required (and thus the corresponding default= value).
A warning is issued while creating the said model, which I could ignore.
Afterwards, created a document (dummy) whence as expected, the document had an integer field value of 100000.
Afterwards changed the model key field as:
Changed the field type as: AutoField
Got rid of the max_length And defaultattributes
Retained the primary_key = True attribute
The next (desired document) created had the value as 100001 with subsequent numbers getting incremented by 1.
So far so good.

You can use default primary key (id) which auto increaments.
Note: When you use first design i.e. use default field (id) as a primary key, initialize object by mentioning column names.
e.g.
class User(models.Model):
user_name = models.CharField(max_length = 100)
then initialize,
user = User(user_name="XYZ")
if you initialize in following way,
user = User("XYZ")
then python will try to set id = "XYZ" which will give you error on data type.

If you are not going to use the auto increment field as the primary key, you can define an integer field and update this integer field in the save() method.
class Order(models.Model):
cart = models.ForeignKey(Cart)
add_date = models.DateTimeField(auto_now_add=True)
order_number = models.IntegerField()
enable = models.BooleanField(default=True)
def save(self, *args, **kwargs):
orders = Order.objects.all()
if orders.exists() and self._state.adding:
last_order = orders.latest('order')
self.order = int(last_order.order) + 1
super().save(*args, **kwargs)
If we do not use self._state.adding here, the order will increase automatically in the update process as well. We only query the self._state.adding that we want to increase the order in the create process.

Related

Django - Can I add a calculated field that only exists for a particular sub-set or occurences of my model?

Imagine that you have a model with some date-time fields that can be categorized depending on the date. You make an annotation for the model with different cases that assign a different 'status' depending on the calculation for the date-time fields:
#Models.py
class Status(models.TextChoices):
status_1 = 'status_1'
status_2 = 'status_2'
status_3 = 'status_3'
special_status = 'special_status'
class MyModel(models.Model):
important_date_1 = models.DateField(null=True)
important_date_2 = models.DateField(null=True)
calculated_status = models.CharField(max_length=32, choices=Status.choices, default=None, null=True, blank=False,)
objects = MyModelCustomManager()
And the manager with which to do the calculation as annotations:
# managers.py
class MyModelCustomManager(models.Manager):
def get_queryset(self):
queryset = super().get_queryset().annotate(**{
'status': Case(
When(**{'important_date_1' is foo, 'then':
Value(Status.status_1)}),
When(**{'important_date_2' is fii, 'then':
Value(Status.status_2)}),
When(**{'important_date_1' is foo AND 'importante_date_2' is whatever, 'then':
Value(Status.status_3)}),
# And so on and so on
)
}
)
return queryset
Now, here's where it gets tricky. Only one of these sub-sets of occurrences on the model requires an ADDITIONAL CALCULATED FIELD that literally only exists for it, that looks something like this:
special_calculated_field = F('important_date_1') - F('importante_date_2') #Only for special_status
So, basically I want to make a calculated field with the condition that the model instance must belong to this specific status. I don't want to make it an annotation, because other instances of the model would always have this value set to Null or empty if it were a field or annotation and I feel like it would be a waste of a row in the database.
Is there way, for example to do this kind of query:
>>> my_model_instance = MyModel.objects.filter(status='special_status')
>>> my_model_instance.special_calculated_field
Thanks a lot in advance if anyone can chime in with some help.

When Django models field is empty, set value to the Default value

Whenever the user doesn't add a value, I need my Django models to replace the otherwise empty field with the value set in default.
My models looks like this:
not_before = models.TimeField(blank=True, null=True, default='00:00:00')
max_num_per_day = models.IntegerField(blank=True, null=True, default=0)
I tried every combination of null, blank and default but no matter what I do, the fields gets replaced by null instead of '00:00:00' and 0.
Is there anyway I can force it to the default value whenever the field is empty?
you can set up your form with a default function like:
class YourForm(forms.Form):
.....
def clean_field(self):
data = self.cleaned_data['not_before']
if not data:
data = '00:00:00'
or write a function in your model like:
class Molde(models.Model):
not_before = models.TimeField(blank=True, null=True, default='00:00:00')
def time(self):
if self.not_before:
return self.not_before
else:
return '00:00:00'
In this case you would call the function instead of the model field itself. You can also take a look at this.
Hope that helps.
from what I understood from your question is you just want to set it to default. you can use:
https://code.djangoproject.com/ticket/6754
don't
not_before = models.TimeField(blank=True, null=True, default='00:00:00')
instead,
import datetime
not_before = models.TimeField(default=datetime.time(0,0))
max_num_per_day = models.IntegerField(default=0)
It seems you are using a ModelForm to grab the data from the user.
In this case, the solution proposed by sasuke will not work. First, you would have to set the required param to False in your form fields, so you would stop seing those "This field is required" messages. Still, you would see errors when saving the form. Even if your model instance is initialized with the default value, the form will replace it with None, since there is an existing field in the form matching the field in the model and its value is None.
My solution is to override the values in the model instance before saving them:
model_instance = myform.save(commit=False)
if not model_instance.not_before:
model_instance.not_before = '00:00:00'
if not model_instance.max_num_per_day:
model_instance.max_num_per_day = 0
model_instance.save()

Django - Checking for two models if their primary keys match

I have 2 models (sett, data_parsed), and data_parsed have a foreign key to sett.
class sett(models.Model):
setid = models.IntegerField(primary_key=True)
block = models.ForeignKey(mapt, related_name='sett_block')
username = models.ForeignKey(mapt, related_name='sett_username')
ts = models.IntegerField()
def __unicode__(self):
return str(self.setid)
class data_parsed(models.Model):
setid = models.ForeignKey(sett, related_name='data_parsed_setid', primary_key=True)
block = models.CharField(max_length=2000)
username = models.CharField(max_length=2000)
time = models.IntegerField()
def __unicode__(self):
return str(self.setid)
The data_parsed model should have the same amount of rows, but there is a possibility that they are not in "sync".
To avoid this from happening. I basically do these two steps:
Check if sett.objects.all().count() == data_parsed.objects.all().count()
This works great for a fast check, and it takes literally seconds in 1 million rows.
If they are not the same, I would check for all the sett model's pk, exclude the ones already found in data_parsed.
sett.objects.select_related().exclude(
setid__in = data_parsed.objects.all().values_list('setid', flat=True)).iterator():
Basically what this does is select all the objects in sett that exclude all the setid already in data_parsed. This method "works", but it will take around 4 hours for 1 million rows.
Is there a faster way to do this?
Finding setts without data_parsed using the reverse relation:
setts.objects.filter(data_parsed_setid__isnull=True)
If i am getting it right you are trying to keep a list of processed objects in another model by setting a foreign key.
You have only one data_parsed object by every sett object, so a many to one relationship is not needed. You could use one to one relationships and then check which object has that field as empty.
With a foreign key you could try to filter using the reverse query but that is at object level so i doubt that works.

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.

Django ManyToManyField Error when saving in admin?

What is wrong with my code?
class Group(ImageModel):
title = models.CharField(verbose_name = "Title", max_length=7)
photos = models.ManyToManyField('Photo', related_name='+',
verbose_name=_('Photo'),
null=True, blank=True)
.....
pid = Photo.objects.get(image = str_path)
gid= Group.objects.get(id = self.id)
self.save_photos(gid, pid)
....
def save_photos(self, gid, pid):
group_photo = GroupPhotos(groupupload=gid.id,
photo=pid.id
)
group_photo.save()
and my GroupPhotos models is:
class GroupPhotos(models.Model):
groupupload = models.ForeignKey('Group')
photo = models.ForeignKey('Photo')
class Meta:
db_table = u'group_photos'
when i want to save it from admin panel i am getting value error sth like this:
Cannot assign "38": "GroupPhotos.groupupload" must be a "Group" instance.
with group_photo = GroupPhotos(groupupload=gid, photo=pid) defination it is working but there is no any changes in GroupPhotos table(group_photos). printing this print pid.id,' >>> ',gid.id i am getting true relation...
UPDATE:
I have been working since morning, but no progress... i have also tried this but nothing changed:
pid = Photo.objects.get(image = str_path)
ger = Group.objects.get(id = self.id)
ger.title = self.title
ger.save()
ger.photos.add(pid)
The error is here:
group_photo = GroupPhotos(groupupload=gid.id, photo=pid.id)
The arguments to groupupload and photo should be instances of Group and Photo respectively. Try the following:
group_photo = GroupPhotos(groupupload=gid, photo=pid)
In other words, when creating an object you need to pass arguments of the expected type and not an integer (which may be the primary key key of the desired object but it also might not, which is why you need to pass an object of the correct type).
i have solved my problem with adding through option to my manytomanyfield:
photos = models.ManyToManyField('Photo', related_name='+',
verbose_name=_('Photo'),
null=True, blank=True, through=GroupPhotos)
some info about ManyToManyField.through here:
Django will automatically generate a table to manage many-to-many
relationships. However, if you want to manually specify the
intermediary table, you can use the through option to specify the
Django model that represents the intermediate table that you want to
use.
The most common use for this option is when you want to associate extra data with a many-to-many relationship.

Categories

Resources