UNIQUE constraint failed - django

I am going through Tango with Django and I can't solve this excercise.
I get django.db.utils.IntegrityError: UNIQUE constraint failed: rango_category.name error. This is after I try to implement views attribute to Category object. This is a excerpt from my database population script. I though maybe that I should makemigrations and then migrate to update models for DB. However, this didn't help.
cats = {
'Python' : {'pages': python_pages, 'views':128},
'Django': {'pages': django_pages, 'views':128},
"Other Frameworks": {'pages':other_pages, 'views':128},
}
for cat, cat_data in cats.items():
c = add_cat(cat, cat_data['views'])
for p in cat_data["pages"]:
add_page(c, p['title'], p['url'])
for c in Category.objects.all():
for p in Page.objects.filter(category=c):
print("- {0} - {1}".format(str(c), str(p)))
def add_cat(name, views):
c = Category.objects.get_or_create(name=name, views=views)[0]
c.views=views
c.save()
return c
Adding Category model:
class Category(models.Model):
name = models.CharField(max_length=128, unique=True)
views = models.IntegerField(default=0)
class Meta:
verbose_name_plural = 'Categories'
def __str__(self):
return self.name

You got the error because unique = True in name = models.CharField(max_length=128, unique=True) means that Django will raise constraint errror UNIQUE constraint failed in case you trying to save a new instance with the same name value; A violation of the unique constraint
get_or_create doesn't work because views=views that may be different even though name equals name
When you create or get your instance, you can do so with only the name field
def add_cat(name, views):
c = Category.objects.get_or_create(name=name, views=views)[0]
c.views=views
c.save()
return c

As mentioned in comments, the error is caused by violation of the unique constraint on Category.name. The reason that this is happening might not be completely obvious, however. When you call Category.objects.get_or_create, you are asking django to look for an object with both the given name and the given number of views. If one is not found, then django tries to create one, which violates the unique constraint if there is already a Category of the same name, but with a different number of views. Depending on desired behavior, you can fix by either:
remove unique constraint altogether
change unique constraint to a unique_together constraint
change the model reference to first get or create by name, then set (or modify) the views attribute (don't forget to save)

Related

Django : Foreign Key to a choice field model

I am relatively new to Python / Django.
I am trying to create a relationship between food items and the category (name)
they belong:
class Category(models.Model):
options=(
('vegetable','vegetable'),
('fruit','fruit'),
('carbs','carbs'),
('fish','fish'),
('meat', 'meat'),
('sweet', 'sweet'),
('dairy', 'dairy'),
)
name=models.CharField(max_length=10,choices=options,unique=True)
def __str__(self):
return self.name
class Fooditem(models.Model):
name = models.CharField(max_length=50)
category = models.ForeignKey(Category,on_delete=models.CASCADE)
The code above throws error when running migrate:
ValueError: invalid literal for int() with base 10: 'vegetable'
I created some items in the database, is it the reason?
What is the best way to solve this problem?
Thank you,
D
It would fix it, but you may have some unexpected results.
category = models.ForeignKey(Category,on_delete=models.CASCADE)
will be an id pointing to the other model.
You can make name the primary key by changing it to the following:
name=models.CharField(max_length=10,choices=options,unique=True,primary_key=True)
Also reviewing https://docs.djangoproject.com/en/3.1/ref/models/fields/#choices more closely may help you with your options down the road. It will programming easier to define as costants and follow thier syntaxing
I would realy recommend u to do it this way. if there is something that is unclear just let me know
class Category(models.TextChoices):
vegetable='vegetable'
fruit='fruit'
carbs='carbs'
fish='fish'
meat='meat'
sweet='sweet'
dairy='dairy'
class Fooditem(models.Model):
name = models.CharField(max_length=50)
category = models.CharField(max_length=20, choices=Category.choices)```

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.

Self M2M with django

I am trying to create create a M2M value on self within the same model. I can update the name field fine. However, I keep getting the TypeError when I update the M2M (supertag) field.
models.py
class Tag(models.Model):
name = models.CharField("Name", max_length=5000, blank=True)
supertag = models.ManyToManyField('self', blank=True)
serializers.py
supe = tag.all()
print(supe)
# returns [<Tag: XYZ>, <Tag: 123>]
for y in supe:
# import pdb; pdb.set_trace()
tag = Tag.objects.update(supertag__pk=y.pk)
tag.save()
error:
TypeError: 'supertag__pk' is an invalid keyword argument for this function
I also tried just tag = Tag.objects.update(supertag=supe) which gave the same error
supe is a queryset, it doesn't have a pk attribute.
Also, you are using same name for different variables. tag has already been assigned.
supe = tag.all()
When assigning tag to a new object would affect the working of the for loop, which is based on the former tag variable.
tag = Tag.objects.get_or_create(supertag__pk=supe.pk)
You can't do this.
EDIT
The function get_or_create actually returns tuple, an object and a boolean flag.
The boolean flag specifies whether the object was created or not.
So, the logic you were implementing was wrong. As we discussed,
You could do something like this,
for x in supe:
if x.taglevel == 1:
for value in supe:
x.tag.add(value)
x.save()
else:
#your next logic
print("No level 1")

How can I add 2 foreign keys from the same table? is it possible?

The Django documentation says that I should be able to do this as I did below:
class comp_name(models.Model):
competition_type = models.ForeignKey(comptype, verbose_name='Level')
competition_name = models.CharField(verbose_name='Division',max_length = 60)
comp_style = models.CharField(verbose_name='HT/SC', max_length = 20)
def __unicode__(self):
return str(self.competition_type) + " " + self.competition_name + " "+ self.comp_style
class peaople(models.Model):
comp_name = models.ForeignKey(comp_name, blank=True, related_name="competition_name" ,verbose_name='Division')
heat_num = models.ForeignKey(comp_name,blank=True, related_name="heat_number")
However doing this gives me this error:
in __init__
assert isinstance(to, six.string_types), "%s(%r) is invalid. First parameter to ForeignKey must be either a model, a model name, or the string %r" % (self.__class__.__name__, to, RECURSIVE_RELATIONSHIP_CONSTANT)
AssertionError: ForeignKey(<django.db.models.fields.related.ForeignKey>) is invalid. First parameter to ForeignKey must be either a model, a model name, or the string 'self'
What exactly am I doing wrong here?
I'm trying to add two foreign keys from the same model
As for an answer to your question: Yes. You can add multiple foreign keys to your model that are connected to same table. Only thing you must take into consideration is giving them different related_name values, just like you did.
As for the error you are getting; you created a ForeignKey field named comp_name in peaople model which actually shadows the other model named comp_name which was defined above it. You should change the name of the field to something else, then your problem will be solved.
Note: Assuming comptype is another model which defines competition type.
class Competition(models.Model):
competition_type = models.ForeignKey(CompetitionType, verbose_name='Level')
name = models.CharField(verbose_name='Division',max_length=60)
style = models.CharField(verbose_name='HT/SC', max_length=20)
def __unicode__(self):
return "{} {} {}".format(self.competition_type, self.name, self.style)
class Competitor(models.Model):
competition_name = models.ForeignKey(Competition, blank=True, related_name="competition_name" ,verbose_name='Division')
heat_num = models.ForeignKey(Competition, blank=True, related_name="heat_number")
I'd recommend you to follow correct naming conventions. Please see PEP8 documentation for more information.
Foreign key should not be blank. Remove blank=True

get_or_create failure with Django and Postgres (duplicate key value violates unique constraint)

Thanks for taking time to read my question.
I have a django app with the following model:
class UserProfile(models.Model):
user = models.OneToOneField(User)
...
class Visit(models.Model):
profile = models.ForeignKey(UserProfile)
date = models.DateField(auto_now_add=True, db_index=True)
ip = models.IPAddressField()
class Meta:
unique_together = ('profile', 'date', 'ip')
In a view:
profile = get_object_or_404(Profile, pk = ...)
get, create = Visit.objects.get_or_create(profile=profile, date=now.date(), ip=request.META['REMOTE_ADDR'])
if create: DO SOMETHING
Everything works fine, except that the Postgres Logs are full with duplicate key errors:
2012-02-15 14:13:44 CET ERROR: duplicate key value violates unique constraint "table_visit_profile_id_key"
2012-02-15 14:13:44 CET STATEMENT: INSERT INTO "table_visit" ("profile_id", "date", "ip") VALUES (1111, E'2012-02-15', E'xx.xx.xxx.xxx') RETURNING "table_visit"."id"
Tried different solution e.g.
from django.db import transaction
from django.db import IntegrityError
#transaction.commit_on_success
def my_get_or_create(prof, ip):
try:
object = Visit.objects.create(profile=prof, date=datetime.now().date(), ip=ip)
except IntegrityError:
transaction.commit()
object = Visit.objects.get(profile=prof, date=datetime.now().date(), ip=ip)
return object
....
created = my_get_or_create(prof, request.META['REMOTE_ADDR'])
if created: DO SOMETHING
This only helps for MySQL? Does anyone know how to avaid the duplicate key value errors for postgres?
Another possible reason for these errors in get_or_create() is data type mismatch in one of the search fields - for example passing False instead of None into a nullable field. The .get() inside .get_or_create() will not find it and Django will continue with new row creation - which will fail due to PostgreSQL constraints.
I had issues with get_or_create when using postgres. In the end I abandoned the boilerplate code for traditional:
try:
jobInvite = Invite.objects.get(sender=employer.user, job=job)
except Invite.DoesNotExist:
jobInvite = Invite(sender=employer.user, job=job)
jobInvite.save()
# end try
Have you at some point had unique=True set on Visit's profile field?
It looks like there's been a unique constraint generated for postgres that's still in effect. "table_visit_profile_id_key" is what it's auto generated name would be, and naturally it would cause those errors if you're recording multiple visits for a user.
If this is the case, are you using South to manage your database changes? If you aren't, grab it!
PostgreSQL behaves somewhat differently in some subtle queries, which results in IntegrityError errors, especially after you switch to Django 1.6. Here's the solution - you need to add select_on_save option to each failing model:
class MyModel(models.Model):
...
class Meta:
select_on_save = True
It's documented here: Options.select_on_save