django postgres conditional constrain - python

I'm trying to set a conditional unique constrain to one of my model field (column on db perspective) with respect to another boolean field but can't find a way to do it.
I'm creating a web management tool, to aid/control custumers atendance, based on numbered cards while they are still on the store.
My main objective is to prevent my user to save an card number while this number is still active (aka boolean field) with another custumer.
Something like this on models.py:
class Cards(models.Model):
card_number = models.PositiveIntegerField("""{{RESTRICTION NEEDED}}""")
card_date = models.DateTimeField(auto_now=False, auto_now_add=True)
custumer = models.ForeignKey(Customer)
status = models.BooleanField(default=False)
maybe something like this would work directly on postgres, didn't tried yet
CREATE UNIQUE INDEX card_number_ind ON Cards (card_number) WHERE (status is False);
Any chance to do it directly with Django? I mean set a conditional unique constrain with respect to a boolean field (status) on Django?
TIA
Obs.This is my first question ever on stackoverflow, feel free to criticize on style.

Django doesn't support defining conditional constraints in models.py, however you can create a migration to add such a constraint.
Start by creating an empty migration
./manage.py makemigrations appname --empty
This will create an empty migration file. Next, you'll want to add your custom constraint to the migration
class Migration(migrations.Migration):
...
operations = [
migrations.RunSQL('create unique index card_number_ind on cards (card_number) where (status is false)'),
]

Starting in Django 2.2 there is now a UniqueConstraint declaration available that has a condition option.
condition
UniqueConstraint.condition
A Q object that specifies the condition you want the constraint to enforce.
For example:
UniqueConstraint(fields=['user'], condition=Q(status='DRAFT'), name='unique_draft_user')
ensures that each user only has one draft.

Related

Why do I get an error when I try to add a new object?

I added a field that is a foreign key called user in a model but I initially received an error that said:
It is impossible to add a non-nullable field 'user' to bid without specifying a default.
So I made the default the string 'user'. However, instead I have been receiving the error:
ValueError: invalid literal for int() with base 10: 'user' when I try to migrate the changes I have made (python3 manage.py migrate).
And when I try to add a new bid, I get an error in the web page:
OperationalError: no such column: auctions_listing.user_id
How do I fix this?
models.py:
class Bid(models.Model):
item = models.ForeignKey(Listing, on_delete=models.CASCADE)
price = models.FloatField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
The fundamental reason you're getting the error is because you have provided a string to an IntegerField.
The real reason you have a problem is because you have tried to run migrations with a new non-nullable field without providing Django with a suitable default value.
When you run the migrations, Django needs to populate the new fields on existing objects with something as it can't enter null. It probably asked you in the terminal whether you would like to provide a default - you entered 'user' when what it wanted was a user_id integer (1, 2, 3, etc.).
This may not be the option you want as you would end up assigning all of the existing items to one particular user. Your fix would be to:
Remove the migration you have tried to apply
Allow the field to be nullable in your code
Re-run the migrations
Assign your desired users to each existing object
Remove the null=True from the field
Run another migration
If it doesn't matter that a particular user would be temporarily allocated all the objects then you can skip the nullable step and just pass a default ID when asked and change them after the migration has been run. You might not want to do this if it will affect current users of your application.
first error:
It is impossible to add a non-nullable field 'user' to bid without
specifying a default.
You faced This error because you already have some records in your database and they didn't have this new field/column 'user', so when you tried to add 'user' field to this table without having null=Ture for it, something like:
user = models.ForeignKey(User, null=True, Blank=True, on_delete=models.CASCADE)
what will happen to the old records? because they didn't have that column already and you didn't provide that new field/column as an unrequired field/column so you should provide a default value for the old record in migrating flow.
second error:
ValueError: invalid literal for int() with base 10: 'user
The Django ORM by default uses integer type for id/pk field of tables, so when you are creating a foreign key in your tables because it refers to a integer field so it should be integer as same as id/pk, (you can set custom type for your foreign keys in Django tables if you want)
Because you provide an string as default for existing records so you entered corrupted data in your table
third error:
OperationalError: no such column: auctions_listing.user_id
This happened because you migration flow faced issues and Django couldn't create new migration for your table
What should you do:
if existed data is not important to you:
truncate your database/table
remove your app migrations
create migrations again
else:
find the existed rows that you create a wrong default value for them
update them with existed users ids (integer value) or add a default integer value in your model field that refers to the your target user, like
user = models.ForeignKey(User, default=1, on_delete=models.CASCADE)

How can i set a default value to an OneToOneField relationship which it's automatically generated?

I have two model classes they like below:
from django.db import models
from django.urls import reverse
class Place(models.Model):
address = models.CharField(max_length=80)
class Author(Place):
name = models.CharField(max_length=50)
But when i want to execute makemigartions django shows me error below:
You are trying to add a non-nullable field 'place_ptr' to author 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 have some data in Author table but i dont want to remove that data.
is there anyone could help me please ?
You can delete the old migrations file or entire migration folder for the specific app and then do the python manage.py makemigrations <app_name>
If this is not the solution you wanted then you can find the solution here.

Django 1.7: Makemigration: non-nullable field

I am trying to use django-orderedmodel (https://github.com/kirelagin/django-orderedmodel) in my project.
Running makemigrations doesn't work:
You are trying to add a non-nullable field 'order' to slide 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)
2) Quit, and let me add a default in models.py
Select an option:
I would like to know where I'm doing this wrong. Thanks
As the order field is unique, you'll need to add the field in several migration steps, replacing the original operations in your migration:
Add a nullable field, set the default to NULL.
Set the field to a unique value in each row.
Add a NOT NULL constraint.
I.e. something like this:
operations = [
migrations.AddField('myapp.MyModel', 'order', models.PositiveIntegerField(null=True, unique=True)),
migrations.RunPython(set_order),
migrations.AlterField('myapp.MyModel', 'order', models.PositiveIntegerField(blank=True, unique=True)),
]
where set_order is a function that sets the order to a valid value, e.g.:
def set_order(apps, schema_editor):
MyModel = apps.get_model('myapp', 'MyModel')
for i, model in enumerate(MyModel.objects.all()):
model.order = i
model.save()
It's easiest to provide a default value (i.e. 0), and then replace the operations in the generated migration.

how to update model in django by adding new primary key field?

How to replace default primary key in Django model with custom primary key field?
I have a model with no primary key defined at first since django automatically adds an id field by default as primary field.
#models.py
from django.db import models
class Event(models.Model):
title = models.CharField(max_length=50, unique=True)
description = models.CharField(max_length=150)
I added some objects into it from django shell.
>>e = Event('meeting', 'Contents about meeting')
>>e.save()
>>e = Event('party', 'Contents about party')
>>e.save()
Then I require to add custom character field as primary into this model.
class Event(models.Model):
event-id = models.CharField(max_length=50, primary_key=True)
...
Running makemigrations:
$ python manage.py makemigrations
You are trying to add a non-nullable field 'event-id' to event 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)
2) Quit, and let me add a default in models.py
Select an option: 1
Please enter the default value now, as valid Python
The datetime and `django.utils.timezone modules` are available, so you can do e.g. timezone.now()
>>> 'meetings'
Migrations for 'blog':
0002_auto_20141201_0301.py:
- Remove field id from event
- Add field event-id to event
But while running migrate it threw an error:
.virtualenvs/env/local/lib/python2.7/site-packages/django/db/backends/sqlite3/base.py", line 485, in execute
return Database.Cursor.execute(self, query, params)
django.db.utils.IntegrityError: UNIQUE constraint failed: blog_event__new.event-id
In my experience (using Django 1.8.* here), I've seen similar situations when trying to update the PK field for models that already exist, have a Foreign Key relationship to another model, and have associated data in the back-end table.
You didn't specify if this model is being used in a FK relation, but it seems this is the case.
In this case, the error message you're getting is because the data that already exists needs to be made consistent with the changes you're requesting --i.e. a new field will be the PK. This implies that the current PK must be dropped for django to 'replace' them. (Django only supports a single PK field per model, as per docs[1].)
Providing a default value that matches currently existing data in the related table should work.
For example:
class Organization(models.Model):
# assume former PK field no longer here; name is the new PK
name = models.CharField(primary_key=True)
class Product(models.Model):
name = models.CharField(primary_key=True)
organization = models.ForeignKey(Organization)
If you're updating the Organization model and products already exist, then existing product rows must be updated to refer to a valid Organization PK value. During the migration, you'd want to choose one of the existing Organization PKs (e.g. 'R&D') to update the existing products.
[1] https://docs.djangoproject.com/en/1.8/topics/db/models/#automatic-primary-key-fields
Django has already established an auto incrementing integer id as primary key in your backend as and when u made the previous model.
When u were trying to run the new model , An attempt was made to recreate a new primary key column that failed.
Another reason is,When u made the field,Django was expecting a unique value be explicitly defined for each new row which it couldn't found,hence the reason.
As told in previous answer you can re-create the migration and then try doing it again.It should work.. cheers :-)
The problem is that you made the field unique, then attempted to use the same value for all the rows in the table. I'm not sure if there's a way to programmatically provide the key, but you could do the following:
Delete the migration
Remove the primary_key attribute from the field
Make a new migration
Apply it
Fill in the value for all your rows
Add the primary_key attribute to the field
Make a new migration
Apply it
It's bruteforce-ish, but should work well enough.
Best of luck!

Django ForeignKey which does not require referential integrity?

I'd like to set up a ForeignKey field in a django model which points to another table some of the time. But I want it to be okay to insert an id into this field which refers to an entry in the other table which might not be there. So if the row exists in the other table, I'd like to get all the benefits of the ForeignKey relationship. But if not, I'd like this treated as just a number.
Is this possible? Is this what Generic relations are for?
This question was asked a long time ago, but for newcomers there is now a built in way to handle this by setting db_constraint=False on your ForeignKey:
https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.db_constraint
customer = models.ForeignKey('Customer', db_constraint=False)
or if you want to to be nullable as well as not enforcing referential integrity:
customer = models.ForeignKey('Customer', null=True, blank=True, db_constraint=False)
We use this in cases where we cannot guarantee that the relations will get created in the right order.
EDIT: update link
I'm new to Django, so I don't now if it provides what you want out-of-the-box. I thought of something like this:
from django.db import models
class YourModel(models.Model):
my_fk = models.PositiveIntegerField()
def set_fk_obj(self, obj):
my_fk = obj.id
def get_fk_obj(self):
if my_fk == None:
return None
try:
obj = YourFkModel.objects.get(pk = self.my_fk)
return obj
except YourFkModel.DoesNotExist:
return None
I don't know if you use the contrib admin app. Using PositiveIntegerField instead of ForeignKey the field would be rendered with a text field on the admin site.
This is probably as simple as declaring a ForeignKey and creating the column without actually declaring it as a FOREIGN KEY. That way, you'll get o.obj_id, o.obj will work if the object exists, and--I think--raise an exception if you try to load an object that doesn't actually exist (probably DoesNotExist).
However, I don't think there's any way to make syncdb do this for you. I found syncdb to be limiting to the point of being useless, so I bypass it entirely and create the schema with my own code. You can use syncdb to create the database, then alter the table directly, eg. ALTER TABLE tablename DROP CONSTRAINT fk_constraint_name.
You also inherently lose ON DELETE CASCADE and all referential integrity checking, of course.
To do the solution by #Glenn Maynard via South, generate an empty South migration:
python manage.py schemamigration myapp name_of_migration --empty
Edit the migration file then run it:
def forwards(self, orm):
db.delete_foreign_key('table_name', 'field_name')
def backwards(self, orm):
sql = db.foreign_key_sql('table_name', 'field_name', 'foreign_table_name', 'foreign_field_name')
db.execute(sql)
Source article
(Note: It might help if you explain why you want this. There might be a better way to approach the underlying problem.)
Is this possible?
Not with ForeignKey alone, because you're overloading the column values with two different meanings, without a reliable way of distinguishing them. (For example, what would happen if a new entry in the target table is created with a primary key matching old entries in the referencing table? What would happen to these old referencing entries when the new target entry is deleted?)
The usual ad hoc solution to this problem is to define a "type" or "tag" column alongside the foreign key, to distinguish the different meanings (but see below).
Is this what Generic relations are for?
Yes, partly.
GenericForeignKey is just a Django convenience helper for the pattern above; it pairs a foreign key with a type tag that identifies which table/model it refers to (using the model's associated ContentType; see contenttypes)
Example:
class Foo(models.Model):
other_type = models.ForeignKey('contenttypes.ContentType', null=True)
other_id = models.PositiveIntegerField()
# Optional accessor, not a stored column
other = generic.GenericForeignKey('other_type', 'other_id')
This will allow you use other like a ForeignKey, to refer to instances of your other model. (In the background, GenericForeignKey gets and sets other_type and other_id for you.)
To represent a number that isn't a reference, you would set other_type to None, and just use other_id directly. In this case, trying to access other will always return None, instead of raising DoesNotExist (or returning an unintended object, due to id collision).
tablename= columnname.ForeignKey('table', null=True, blank=True, db_constraint=False)
use this in your program

Categories