I have read this thread:
get_or_create throws Integrity Error
But still not fully understand when get_or_create returns False or IntegrityError.
I have the following code:
django_username = 'me'
user = get_user_model().objects.filter(username=django_username).first()
action_history, action_added = ActionModel.objects.get_or_create(
date=date_obj, # date object
account_name=unique_name, # e.g. account1234
target=follower_obj, # another model in django
user=user, # connected django user
defaults={'identifier': history['user_id']}
)
While the model looks like:
class ActionModel(models.Model):
"""
A model to store action history.
"""
identifier = models.BigIntegerField(
_("identifier"), null=True, blank=True) # target id
account_name = models.CharField(_("AccountName"), max_length=150, null=True, blank=True) # account name of the client
date = models.DateField(_("Date"), auto_now=False, auto_now_add=False) # action date
target = models.ForeignKey(Follower, verbose_name=_("Target"), on_delete=models.CASCADE) # username of the done-on action
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
null=True,
editable=False,
db_index=True,
) # django user that performed the action
class Meta:
verbose_name = _("Action")
verbose_name_plural = _("Actions")
unique_together = [
['account_name','date','target'],
]
Sometimes it return IntegrityError, and sometimes (when unique constrain exists it will return False on created).
You have unique_together constraint.
Lets imagine you have object in db with following data
account_name='bob', date='2020-12-12', target='b', user='12'
In you get_or_create method you are doing this
ActionModel.objects.get_or_create(
date='2020-12-12',
account_name='bob',
target='b',
user="13"
)
you providing exactly this three parameters with this data, but user this time is 13, so django could not find any object and it tries to create one, but with this parametres you cant create object because there is unique constraint
OK.
I figured it out, I sent:
account_name = 'adi'
date = '07-02-21'
target = 'nana1'
user = 'me'
While it was not exist with the specific user = 'me' but with user = None:
account_name = 'adi'
date = '07-02-21'
target = 'nana1'
user = None
So the get was failing and the created try to duplicate the unique_together = ['account_name','date','target'].
Related
I am trying to generate a uniq OrderItem_ID during the order create api. But, it generates the above error as django.db.utils.IntegrityError:
The first api is always successful from the postman, but in the second call I tried changing different products for creating the order, but I am getting this unique id order.
I have to remove the order_items from db , to create a new order_item object otherwise I get this unique error.
I am sending data like this.
My model:
import random
import string
# Create your models here.
def id_generator(size=10, chars=string.ascii_uppercase + string.digits):
return ''.join(random.choice(chars) for _ in range(size))
class Order(models.Model):
ORDER_STATUS = (
('To_Ship', 'To Ship',),
('Shipped', 'Shipped',),
('Delivered', 'Delivered',),
('Cancelled', 'Cancelled',),
)
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
order_status = models.CharField(max_length=50,choices=ORDER_STATUS,default='To_Ship')
ordered_date = models.DateTimeField(auto_now_add=True)
ordered = models.BooleanField(default=False)
total_price = models.CharField(max_length=50,blank=True,null=True)
def __str__(self):
return self.user.email
class Meta:
verbose_name_plural = "Orders"
ordering = ('-id',)
class OrderItem(models.Model):
orderItem_ID = models.CharField(max_length=12,unique=True, editable=False, default=id_generator())
order = models.ForeignKey(Order,on_delete=models.CASCADE, blank=True,null=True,related_name='order_items')
item = models.ForeignKey(Product, on_delete=models.CASCADE,blank=True, null=True)
order_variants = models.ForeignKey(Variants,on_delete=models.CASCADE,blank=True,null=True)
quantity = models.IntegerField(default=1)
total_item_price = models.PositiveIntegerField(blank=True,null=True,)
My serializers:
class OrderSerializer(serializers.ModelSerializer):
billing_details = BillingDetailsSerializer()
order_items = OrderItemSerializer(many=True)
user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
class Meta:
model = Order
fields = ['id','user','ordered_date','order_status', 'ordered', 'order_items', 'total_price','billing_details']
# depth = 1
def create(self, validated_data):
user = self.context['request'].user
if not user.is_seller:
order_items = validated_data.pop('order_items')
billing_details = validated_data.pop('billing_details')
order = Order.objects.create(user=user,**validated_data)
BillingDetails.objects.create(user=user,order=order,**billing_details)
for order_items in order_items:
OrderItem.objects.create(order=order,**order_items)
return order
else:
raise serializers.ValidationError("This is not a customer account.Please login as customer.")
In python shell, i tired this and it works fine
the problem is with
orderItem_ID = models.CharField(max_length=12,unique=True, editable=False, default=id_generator())
Here in the default, you're have assigned function call. Thus, it will only be evaluated once at the time of creation, .i.e., at first time you run makemigrations.
We need to have function references in the default values, this way it will be called each time a new instance is created.
Try replacing the line with
orderItem_ID = models.CharField(max_length=12,unique=True, editable=False, default=id_generator)
Note default=id_generator and not default=id_generator().
Hope this answers your question.
PS: you would be required to rerun the makemigrations and migrations commands to set this change into effect.
The validation error comes from here:
orderItem_ID = models.CharField(max_length=12,unique=True, editable=False, default=id_generator())
Add orderItem_ID in OrderItemSerializer and try sending orderItem_ID in "order_items" with a unique value on each post.
As for this: default=id_generator()
Check what this function is generating on each hit, probably its saving the same value each time which is causing the error.
Scenario:
I am trying to create/insert data to Django model (POSTGRES database) employee and profile.
When I insert duplicate records (i.e. with duplicate work_email and duplicate employee_id) I am expecting a database error in an atomic transaction.
However, the below code doesn't throw any error, but when I see the database table no duplicate is created.
Dropped a database and created a new DB and new migrations to make sure there is no problem with the sync. However, the result is the same with no errors.
Any help is appreciated. Thanks
class Employee(models.Model):
"""
Employee table containing all the employee information.
"""
profile = models.OneToOneField(Profile, on_delete=models.CASCADE)
id = models.CharField(max_length=50, unique=True, blank=False, default=uuid.uuid4, editable=False, db_index=True)
employee_id = models.CharField(
max_length=100,
blank=False,
unique=True,
primary_key=True,
error_messages={'employee_id': "A user with employee id already exists."}
)
class Profile(AbstractUser):
"""
Enhancing user model with additional fields. This is in relation with a table ProfileExtras.
Extras can be utilised to add any fields to further enhance Profile with key value pair.
"""
email = None
date_of_birth = models.DateField(blank=False)
work_email = models.EmailField(
max_length=50,
blank=False,
unique=True,
db_index=True,
error_messages={'work_email': "A user with work email already exists."}
)
def create_employee(app_context, data_dict):
try:
with transaction.atomic():
employee_model = models.Employee()
profile_model = models.Profile()
# Data insertion logic
# e.g. setattr(employee_model, "first_name", "xxxxxx")
employee_model.profile = profile_model
profile_model.save()
employee_model.save()
except Exception as e:
log.error(e)
raise e
Found the solution. I was testing with Django test. It is found that data is not retained between each test, hence the duplicate error is not raised.
I have a model like this:
class CreateDeal(models.Model):
name = models.CharField(max_length=100)
fuel = models.CharField(max_length=15)
mileage = models.PositiveIntegerField(db_index=True)
phone_number = models.CharField(max_length=17)
location = models.CharField(max_length=100, db_index=True)
car_picture = models.ImageField(upload_to='car_picture')
description = models.TextField()
price = models.PositiveSmallIntegerField(db_index=True)
available = models.BooleanField(default=True)
created_on = models.DateTimeField(default=timezone.now)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.name
and I have a test class to test the model above like this:
class CreateDealTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(
username='alfa', email='alfa#hotmail.com', password='top_secret'
)
self.deal = CreateDeal.objects.create(
name='deal1', mileage=100, price=25, user=self.user
)
def test_deal_name(self):
deal = CreateDeal.objects.get(name='deal1')
expected_deal_name = f'{self.deal.name}'
self.assertAlmostEqual(expected_deal_name, str(deal))
if I run the test I have:
Ran 1 test in 0.166s
OK
My question is why django don't raise an exception since almost all fields in my model are required. And what I don't understand is if I remove one field of Createdeal in my setUp (like mileage, price, user or name) I have an error.
For instance if I remove mileage, I have this error:
raise utils.IntegrityError(*tuple(e.args))
django.db.utils.IntegrityError: (1048, "Column 'mileage' cannot be null")
Charfield, Imagefield and Textfield can be empty string which is valid at the database level, some of your fields have default values so they will be written if not set so that makes them also valid at the database level.
PositiveIntegerField and Foreign key cannot be set to empty string, just to value or null so they will fail since null=False by default.
The default blank=False option is only applied at the validation level, not at the database level. This means if you call full_clean() on your model, it will raise a ValidationError. But nothing stops you from saving an invalid model (save() does not call full_clean() as explained here).
when I save the crawl the data to the database using scrapy it shows an error like,
self.field.remote_field.model._meta.object_name, ValueError: Cannot assign "'1'": "mymodel.provider" must be a "Providers" instance
in django
spider.py
class CrawlSpider(scrapy.Spider):
name = 'example'
start_urls = ['https://example.com/'
]
def parse(self, response):
items = crawlItem()
all_section = response.css(' div.brief_box ')
# all_sec = response.css('div._3WlLe')
news_provider = '1'
# for quotes in all_sec:
# dec = quotes.css('._3WlLe::text').extract()
for quote in all_section:
title = quote.css('.posrel img').xpath("#alt").extract()
details = quote.css('p a').xpath('text()').extract()
image = quote.css('.posrel img').xpath("#data-src").extract()
page_url = quote.css('p a').xpath("#href").extract()
items['provider'] = provider
items['title'] = title
items['details'] = details
items['image'] = image
items['page_url'] = page_url
yield items
item.py
from scrapy_djangoitem import DjangoItem
from applications.crawl.models import Mymodel
class NewscrawlItem(DjangoItem):
django_model = Mymodel
models.py
class Mymodel(models.Model):
"""
model for storing news details
"""
id = models.AutoField(primary_key=True)
provider = models.ForeignKey(Providers, related_name='provider')
details = models.CharField(max_length=1000, null=True, blank=True)
page_url = models.CharField(max_length=1000, null=True, blank=True)
image = models.CharField(max_length=1000, null=True, blank=True)
title = models.CharField(max_length=255)
class Providers(models.Model):
provider = models.AutoField(primary_key=True)
url = models.CharField("Website URL", max_length=255, null=True, blank=True)
region = models.CharField(max_length=7, choices=REGIONS, null=True, blank=True)
image = ImageField(upload_to='provider/%Y/%m/%d/', null=True, blank=True)
As the error message clearly states, MyModel.provider should be a Provider instance, not the string representation of the related provider's pk. Either pass the Provider instance or, better, pass the pk but using the proper field name (would be provider_id in most cases but since your provider model's pk is named provider it might be provider_provider - but you just have to check your YourModel db schema to find out).
I have three models in my django app...a members model, an application model and an applications review model.
My members model looks like this...
class Members(models.Model):
TITLES = (
('chairman', 'Chairman'),
('secretary', 'Secretary')
)
user = models.OneToOneField(User, on_delete=models.CASCADE)
title = models.CharField(max_length=10, choices=TITLES, default='secretary')
My Applications model...
class Application(models.Model):
firstname = models.CharField(max_length=20)
middlename = models.CharField(max_length=20)
lastname = models.CharField(max_length=20)
dob = DateField()
The applications review model...
class ApplicationsReview(models.Model):
APPLICATION_STATUS = (
('pending', 'Pending Review'),
('approved', 'Approved'),
('rejected', 'Rejected')
)
applicant = models.OneToOneField(Application, on_delete=models.CASCADE, primary_key=True)
chairman = models.ForeignKey(Members, related_name='chairs', on_delete=models.CASCADE)
secretary = models.ForeignKey(Members, related_name='secretaries', on_delete=models.CASCADE)
application_status = models.CharField(max_length=10, choices=APPLICATION_STATUS, default='pending')
status_justification = models.TextField()
date = models.DateTimeField(auto_now_add=True)
When an application is created, I would like its review instantiated as well, hence, I have the following signal right below the applications review model...
# When an application is created, create with it an application review and associate it with the application instance
#receiver(post_save, sender=Application)
def create_application_review(sender, **kwargs):
instance = kwargs['instance']
created = kwargs['created']
if created:
ApplicationReview.objects.create(applicant=instance)
However, when I try to add an application in django admin I get the error
null value in column "chairman_id" violates not-null constraint
DETAIL: Failing row contains (3, pending, 2019-02-08 03:26:04.643452+00, null, null).
The error seems to be as a result of the signal trying to instantiate an ApplicationsReview instance without providing the values for the chairman and secretary. Even setting those to allow null fields doesn't get rid of the error. Is there something I'm missing here?
Creating ApplicationsReview requires you to pass the following details - chairman, secretary, status_justification But while creating ApplicationReview in the signal you are just passing value of applicant, So Django is assuming the values of chairman, secretary, status_justification fields as Null that is why you are getting this error.
If you want to make these field Non-compulsory you can pass null=True, Blank=True while defining the field in the model.
Something like this:
chairman = models.ForeignKey(Members, null=True, blank=True, related_name='chairs', on_delete=models.CASCADE)
# End
You can refer this answer to get more understanding of when to use null=True, blank=True or both.
https://stackoverflow.com/a/8609425/6280433