django: Does `unique=True` prevent `IntegrityError`? - python

I realized that when I have a simple model:
class Category(models.Model):
name = models.CharField(max_length=128, unique=True)
views = models.IntegerField(default=0)
likes = models.IntegerField(default=0)
the modifier unique=True has the effect that when I add a category to the database (via an HTML form) which has a name of an instance already present in the database, the application will not crash.
Instead, I get access to form.errors which I can print to the page or to the terminal.
If I omit unique=True and I try to add a category to the database with a name that already exists, I get an IntegrityError and my application crashes.
So it seems to me that defining unique=True is pretty important for the behaviour of the application. I guess then there must be other model attributes which are equally importa
In the django documentation, where can I read about this and if there are other such attributes?

First of all, you created the model using the unique constraint, so the database already has the unique constraint.Something like (for MySQL):
CONSTRAINT myapp_category UNIQUE (name)
Now, you are probably using a ModelForm, which picks this attribute unique=True, and applies that validation. So, you can catch the errors via form.errors and handle it gracefully.
When you remove the attribute, the database still has the constraint, but the ModelForm does not perform that validation for you, hence the IntegrityError
So, to answer your question - unique=True does help prevent raising of an exception when you use it in the context of a ModelForm, or you would have to handle it yourself to make it fail gracefully.
To remove the unique constraint from the database, you would have to use a migration tool like south

My guess is that you have a database model which has a UNIQUE constraint on the name column. If you try to insert two rows with the same name, then the database (via the database driver) will throw an IntegrityError. This happens completely independent of django.
django then has a model how to build forms. If you omit the unique=True, django assumes that the column in the database isn't unique and doesn't add the necessary checks in the form validation step.
So you basically have a config mismatch between your form and your database.
Solutions:
Make sure the constraints for forms and database columns are always the same
Write your own, custom validation rules (see the django documentation).
Alternatively: Write code that gets unique constraints from the database and then, when you create a form, check that the necessary attributes are set to avoid this error.

Related

django asking to add default values

I am new to django and I am facing some problem.
This was my previous model.
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
I added some data to this model.
After that I added one more field to it.
Now my model looks like this..
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
age = models.IntegerField()
When I ran the command python manage.py makemigrations I got the following error which is obvious.
You are trying to add a non-nullable field 'age' to blog 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:
I don't want to add default so I deleted the table contents by accessing db.sqlite3
But again after running python manage.py makemigrations I am getting the same error.why?
Even though you have deleted the table, any changes to your Django model will always refer to the previous migration of that model, hence it still requires you to set a default value. If you're still in development, then you could delete the migrations for that model and then makemigrations again. But, this is terrible practice and you should never do this unless you are consciously squashing your models, the better way is to do what Django is telling you and set a default. This'll help in the long run anyways with error handling.
The other answers address how to set defaults quite well.
When you add a new field (i.e., column) to a database table, that field applies to all rows in the table...the existing ones and any new ones. If the field cannot be null, then some value must be specified for all of the existing rows.
By default, IntegerField() is non-nullable. This explains why you are getting the warning. You have a few options:
You can choose a default "one-off" initial value like Django suggests. All existing rows will use this value for the new field.
Assign your own values to each existing row for the new field.
Use the blank and null arguments in your model field to allow the field to accept null values.
For the last option, you'd do this like so:
models.IntegerField(blank=True, null=True)
But that might not be the best choice depending on your use case. If nullable values don't make sense for your field, I would avoid making the field support nulls.
age = models.IntegerField(null=True)
Django models fields are null set to true by default.
The way you can solve this:
Delete the Blog table from the database.
Delete the migrations folder from the app that contains Blog model.
Remove all data from django_migrations table.
Now run makemigrations and then migrate command.
Hope this will solve your problem.
Do what Django is telling you, then you can remove the default attribute if you don't need it. But it's a good practice to keep it anyway

UNIQUE constraint failed Django models

I'm working on a Django webapp.
I'm trying to create a user from signup form.
I made sure that there is no user in DB with that username and entered data in signup form and hit enter.
There is a default language field that is populated with txt in User user model.
I'm using the following function to populate that field.
def get_default_language():
lang=Language.objects.get_or_create(
name='Plain text',
lang_code='text',
slug='text',
mime='text/plain',
file_extension='.txt',
)
return lang[0].id
But it's giving me the below error
IntegrityError at /signup/
UNIQUE constraint failed: djangobin_language.lang_code
Here is the code for Language model
class Language(models.Model):
name=models.CharField(max_length=100)
lang_code=models.CharField(max_length=100, unique=True, verbose_name="Language Code")
Isn't it supposed to get or create the row in Language table? Why am I getting this error?
Your model is not complete. You.sre.passing different values for the get_or_create and because it can't find the exact object you are searching for than it tries to create one but the language code it's already used and you get the unique issue.
I suggest you to use a choices option on your language code field and pass a tuple with the ISO state definitions.
I also suggest you to retrieve based only the language code field and then decide or to update.the object based the other field passed or create a new language object. You must implement your own method to do that

How to add a conditional field in a Django model?

Basically, what I want is a field to be available if a condition is met, so something like this:
class ConditionalModel(models.Model):
product = models.ForeignKey(product, on_delete=models.CASCADE)
if category == "laptop":
cpu_model = models.CharField(max_length=200)
so if I were to go to the Django admin page and create an instance of the model and then choose "laptop" as the product from the drop-down list of existing "products", a new field would be available. I couldn't find anything about this in the documentation, so I'm wondering whether it's even possible.
What you are asking for is not "technically" possible. A model relates a database object, and under traditional SQL rules, this isn't possible. You could instead make that field optional, and then customize the admin page's functionality.
Another potential option, though I do not have much experience with it, would be to use a NoSQL database in the case where you don't want to store NULL values in your db.
I do not think it is possible because models defines databases tables so the column has to be present.
You can use the keyword blank=True to allow an object without this field.
Maybe you can customize the admin interface to hide the field in some cases.
You can't do that in models.
You can hide it in admin panel or you can make separate model for laptop.
Or you can make field blank=True
Making a field optional is not possible but you can use a generalized model called Product and two or more specialized ones called for example : ElectronicProduct that contains the field cpu_model and NonElectronicProduct, the two specialized models have to contain a OneToOneField to the Product model to ensure inheritance.

How to generate indexes for related fields on Django models?

Say we're building a Django-based site that clones Medium.com's URL structure, where you have users and articles. We'd probably have this model:
class Article(models.Model):
user = models.ForeignKey(User)
slug = models.CharField()
We want to be able to build URLs that look like /<username>/<slug>/. Since we're going to have billions of articles and zillions of pageviews, we want to put an index on that model:
class Meta:
indexes = [
models.Index(fields=['user__username', 'slug'])
]
But this causes the makemigrations command to fail with the following error:
django.core.exceptions.FieldDoesNotExist: Article has no field named 'user__username'. The app cache isn't ready yet, so if this is an auto-created related field, it won't be available yet.
So, plain vanilla models.Index doesn't support relational lookups like a QuerySet does. How would I add an index like this? Let's assume PostgreSQL, if that's helpful.
It seems that you can't make multi-table index according to this answer.
So if it's not possible in the database, I don't see how can Django offer this feature...
What you can do to make your queries more efficients is an index using user_id and slug.
Django index meta class mainly provide declarative options for indexing table fields,
you can create an index using several field of a model or create different index for every fields of the model. you just don't have to provide user foriegnkey field name attribute which generate automatic user_id index migrations
migrations.AddIndex(
model_name='candidates',
index=models.Index(fields=['user'], name='candidates__user_id_569874_idx'),
),
you can also set the index name in the model meta, and db_tablspace as well if needed.

Django: How do I update previously created database entries?

I've got a bunch of previously created database entries of a model class I created, called Equations. I created a new type of model, EquationGroup, and I want to be able to link the existing Equations in the database to newly created EquationGroups. How would I do that?
Update:
I forgot to mention that I've got a ForeignKey relationship in Equation to EquationGroup.
Here is short version of my models.py
class EquationGroup(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Equation(models.Model):
name = models.CharField(max_length=50, null=False, blank=False)
group = models.ForeignKey(EquationGroup)
You can create a script that goes through all the equations and attaches them to equations groups. Using Equations.objects.all() you can get an iterable of all of the equations objects.
You can then go through in a for loop and assign each one to the specified Equations group.
Example:
for equation in Using Equations.objects.all():
equation.equationGroup = some_group #based on however you specify what goes in a group.
equation.save()
Do you want to make associations Equation <-> EquationGroup? If so, you should add OneToOne relation field. Look here for examples.
There is many other ways to add relations, like ManyToMany and ForeighKey.
Do you need to associate instances? In this way, you should have data to find related data and save it as OneToOne/ForeignKey.
How do you plan to update the database with the new change to your equation model? If you are using django 1.7+ you can use the built-in migrations. If not you will need to look to something like South. Thirdly you can alter the database by hand.
Anyway, the reason i bring up migrations is because you cant add a null foreign key field. When adding the field to the database table you will need to specify a default FK value, that you can go back and update using #user3154952 suggestion.
Depending on the database you are using, you may be able to remove the foreign-key constraint check temporarily. Allowing you to add the new field to the table without a dummy value, by allowing null.

Categories