I have a simple model::
class HasUUID(models.Model):
name = models.CharField(max_length=10)
batchid = models.UUIDField(default=uuid.uuid4(), unique=True)
running makemigrations gives me the migration::
operations = [
migrations.CreateModel(
name='HasUUID',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=10)),
('batchid', models.UUIDField(default=uuid.UUID('79a2d9fe-e1d0-4d4b-884f-fad0bfb14f0f'), unique=True)),
],
),
]
Running migrate gives me the new table no problem. But I can run makemigrations again and I get::
operations = [
migrations.AlterField(
model_name='hasuuid',
name='batchid',
field=models.UUIDField(default=uuid.UUID('3b96231c-5848-430b-aa90-b6e41b11fd0a'), unique=True),
),
]
and while I have lived with this for a while, manually removing the unnecessary code, I need to resolve it.
so I think, make the default a separate function in the migrations as shown in various examples::
def create_uuid(apps, schema_editor):
m = apps.get_model('web', 'HasUUID')
for inst in m.objects.all():
inst.batchid = uuid.uuid4()
inst.save()
...
migrations.CreateModel(
name='HasUUID',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=10)),
('batchid', models.UUIDField(blank=True, null=True)),
],
),
migrations.RunPython(create_uuid),
migrations.AlterField(
model_name='hasuuid2',
name='batchid',
field=models.UUIDField(default=uuid.uuid4, unique=True)
),
same problem. So I tried making the default a separate function in the model::
def create_uuid():
return uuid.uuid4()
class HasUUID2(models.Model):
name = models.CharField(max_length=10)
batchid = models.UUIDField(default=create_uuid(), unique=True)
and this gets me this migration::
migrations.CreateModel(
name='HasUUID3',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=10)),
('batchid', models.UUIDField(default=uuid.UUID('335c3651-b04e-4ed8-a91d-f2da3f53dd8f'), unique=True)),
],
),
and again, keeps generating new migrations. I've also tried without the unique = True.
I'm out of ideas. There must be some settings or code elsewhere as I have used UUID fields before and I cannot find a similar problem on stackoverflow. Any suggestions?
You should pass the callable as default, not the result of a call, like:
class HasUUID(models.Model):
name = models.CharField(max_length=10)
batchid = models.UUIDField(default=uuid.uuid4, unique=True)
Notice that there are no paratheses here to make the call, we thus pass a reference to the uuid4 function itself.
The default= value is not a specific UUID (that is determined when you start the server), it should be a value that is determined when you create a new object (without specifying the batchid yourself).
By passing a callable, Django will understand that the default is the result of a call to the callable, and it will encode that in the migration. By calling the function, you retrieve the result of the call, and each time you run makemigrations, Django will think that you changed your mind on what should be the default value (it will first think you want to use '3b96231c-5848-430b-aa90-b6e41b11fd0a' as default, and later that you want to use '335c3651-b04e-4ed8-a91d-f2da3f53dd8f'). By passing a callable, the value you pass as default remains the same.
Related
I'm trying to filter a table in django based on the selected category. The category field is equivalent to name.
I can't wrap my head around filtering all the blogposts which link to that certain category id.
I currently use this:
category_posts = BlogPost.objects.filter(category=cats).order_by('-published')
Which I could use without there being any foreign key. I am passing the string variable into the path
How could I make this more efficient and use the foreign key that relates the table..
Something like this:
category_posts = Blogpost.objects.filter(category__category__contains=cat)
I don't know if I am complicating things, just confused
The following are the 2 tables available:
name='Category',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('category', models.CharField(max_length=200)),
('summary', models.CharField(max_length=200)),
('slug', models.CharField(default=1, max_length=200)),
],
name='Blogpost',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200)),
('content', models.TextField()),
('published', models.DateTimeField(default=datetime.datetime(2020, 7, 8, 20, 49, 14, 897634), verbose_name='date published')),
('slug', models.CharField(max_length=200)),
('category', models.ForeignKey(default=1, on_delete=django.db.models.deletion.SET_DEFAULT, to='blogposts.Category', verbose_name='Category')),
],
Your example would work if cat was a string, and return all blogposts that point to any category that has cat in the name.
category_posts = Blogpost.objects.filter(category__category__contains="cat")
If you want to filter a specific category, e.g.
cat = Category.objects.get(category="cat")
then you can do with with:
BlogPost.objects.filter(category=cat)
In terms of design, it might be more clear to call the char field that stores category name as simply "name". Then the very first query would look like BlogPost.objects.filter(category__name__contains="cat") and the second cat = Category.objects.get(name="cat").
I have the following models:
class Contact(models.Model):
email = models.EmailField()
class EventList(models.Model):
event_contacts = models.ManyToManyField(Contact, through=EventMembership)
class EventMembership(models.Model):
event_list = models.ForeignKey(EventList, null=True, on_delete=models.PROTECT)
event_contact = models.ForeignKey(Contact, null=True, blank=False, on_delete=models.PROTECT)
However, when applying migrations for EventMembership on a completely clean database I get the following error:
psycopg2.errors.InvalidForeignKey: there is no unique constraint
matching given keys for referenced table "contacts_contact"
class Migration(migrations.Migration):
initial = True
dependencies = [
('lists', '0001_initial'),
('contacts', '0002_auto_20200308_2253'),
]
operations = [
migrations.CreateModel(
name='EventMembership',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('event_contact', apps.utils.django_multitenant.fields.TenantForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='contacts.Contact')),
('event_list', apps.utils.django_multitenant.fields.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='lists.EventList'))
]
]
Table contacts_contact clearly has a unique constraint in id as the primary key.
What could be causing this error? / How do I debug this?
You just need to do it step by step. Now you are trying to create a foreign key relationship with a table that is not in the database yet. So comment everything out except for Contact model, apply migrations, then add EventList etc. If you are relying on the fact that Contact model goes first, well, it doesn't help in this case.
I have an already migrated Django model, which was created this way:
operations = [
migrations.CreateModel(
name='Victim',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('email', models.EmailField(max_length=200)),
('instagram', models.CharField(max_length=254)),
('old_password', models.CharField(blank=True, max_length=200)),
('new_password', models.CharField(blank=True, max_length=200)),
],
),
]
But now, I want to make email and instagram attribute Blank=True, but password fields make Blank=False.
What is the easiest way to do this: delete and recreate the model (data is not important) or is there a possible way to do this?
You can still change your models and run manage.py makemigrations. It will create another migration to execute the required SQL statements to alter your database schema when running manage.py migrate. This is the role of migrations.
I have Django models with many uncompulsory fields Is there an alternative to avoid null=True and blank=True ? Is there something like blank_fields = ('email','image',) and null_fields = ('email', 'image') ?
No, Django does not have any options like blank_fields or null_fields.
You could subclass the fields to create optional versions, e.g. OptionalCharField, but I would recommend against this. It might be less repetitive than using blank=True, null=True repeatedly, but it will be less readable for other Django developers who look at your code. And as Cezar suggests in the comments you shouldn’t usually use null=True for CharField because that means you have two empty values, '' and None.
There is nothing which Django provides by default. These attributes are per field definitions, not something which is Model level. You have definitions as unique_together, index_together which are model level definitions combining different fields.
One approach can be of subclassing the Fields and provide a default definition -
class CustomIntegerField(models.IntegerField):
def __init__(self, *args, **kwargs):
kwargs['blank'] = True
kwargs['null'] = True
super(CustomIntegerField, self).__init__(**kwargs)
class Test(models.Model):
cus = CustomIntegerField(default=0)
Migration:
class Migration(migrations.Migration):
dependencies = [ ]
operations = [
migrations.CreateModel(
name='Test',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('cus', test.models.CustomIntegerField(blank=True, default=0, null=True)),
],
),
]
You can do this for other fields as well.
I'm moving the field some_field from Model_A to a new Model_B, with a OneToOne relationship. Before deleting this field in Model_A, I want to copy the value from (the historical) Model_A to the newly created Model_B. The problem is that I cannot retrieve the field at migration time, since Model_A doesn't any longer contain some_field.
This is the error message I get when I try running my custom migration:
AttributeError: 'Model_A' object has no attribute 'some_field'
Models before changes:
class Model_A:
some_field = models.BooleanField(default=False)
some_other_field = models.BooleanField(default=False)
Models after changes:
class Model_A:
some_other_field = models.BooleanField(default=False)
class Model_B:
model_a = models.OneToOneField(Model_A, related_name='extension')
some_field = models.BooleanField(default=False)
Migration:
class Migration(migrations.Migration):
dependencies = [
('my_app', '0001_initial'),
]
def forwards_func(apps, schema_editor):
# This is where I try to get the "historical" Model_A
Model_A = apps2.get_model("my_app", "Model_A")
# And this is where I intend to copy the some_field values
for model_A_instance in Model_A.objects.all():
b = Model_B(model_a=model_A_instance)
# b gets created correctly, but the following step fails
b.some_field = modelA_instance.some_field
b.save()
operations = [
migrations.CreateModel(
name='Model_B',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('some_field', models.BooleanField(default=False)),
('model_a', models.OneToOneField(related_name='extension', to='my_app.Model_A')),
],
options={
},
bases=(models.Model,),
),
migrations.RunPython(forwards_func),
migrations.RemoveField(
model_name='model_a',
name='some_field',
),
]
I'm pretty much aware that I somehow have to get hold of a "historical" representation of Model_A (= the one currently in the database), but I thought that was what the apps2.get_model("my_app", "Model_A") part was for.
Any input on how to accomplish this? Or should I maybe split the migration into two, where the first one creates Model_B + copies the some_field values and the second one deletes the some_field field from Model_A?
Yes, you need to have historical representation of Model_A and it's pretty easy to retrieve it. Thats what apps passed into your function called by RunPython migration is for, so why are you using some apps2 insdead of apps here? Also, you should get your Model_B from same apps instance as Model_A is from. Your migration should look like that:
class Migration(migrations.Migration):
dependencies = [
('my_app', '0001_initial'),
]
def forwards_func(apps, schema_editor):
# This is where I try to get the "historical" Model_A
# here is change - using apps passed into forwards_func by RunPython instead of apps2
Model_A = apps.get_model("my_app", "Model_A")
Model_B = apps.get_model("my_app", "Model_B")
# And this is where I intend to copy the some_field values
for model_A_instance in Model_A.objects.all():
b = Model_B(model_a=model_A_instance)
b.some_field = modelA_instance.some_field
b.save()
operations = [
migrations.CreateModel(
name='Model_B',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('some_field', models.BooleanField(default=False)),
('model_a', models.OneToOneField(related_name='extension', to='my_app.Model_A')),
],
options={
},
bases=(models.Model,),
),
migrations.RunPython(forwards_func),
migrations.RemoveField(
model_name='model_a',
name='some_field',
),
]
Why are you using apps2 anyway? And what it is?