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.
Related
My table is defined as below.
class Role(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
to_field="email",
db_column="user",
on_delete=models.CASCADE
)
vertical = models.ForeignKey(
Verticals,
to_field="name",
db_column="vertical",
on_delete=models.CASCADE
)
product_domain = models.ForeignKey(
ProductDomains,
to_field="name",
db_column="product_domain",
null=True,
on_delete=models.CASCADE
)
class Meta:
constraints = [
models.UniqueConstraint(fields=[
'user',
'vertical',
'product_domain'
],
name='unique-permissions-per-user')
]
Here are the migrations that are generated
migrations.CreateModel(
name='Role',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('product_domain', models.ForeignKey(db_column='product_domain', null=True, on_delete=django.db.models.deletion.CASCADE, to='verticals.ProductDomains', to_field='name')),
('user', models.ForeignKey(db_column='user', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, to_field='email')),
('vertical', models.ForeignKey(db_column='vertical', on_delete=django.db.models.deletion.CASCADE, to='verticals.Verticals', to_field='name')),
],
),
migrations.AddConstraint(
model_name='role',
constraint=models.UniqueConstraint(fields=('user', 'vertical', 'product_domain'), name='unique-permissions-per-user'),
),
Serializers for the Role model is
class RoleSerializer(serializers.ModelSerializer):
class Meta:
model = Role
fields = '__all__'
Here is the interactive console for the same
Link to console image (Unable to add due to less reputation )
Here the UniqueConstraint is not working, why?
I use models.UniqueConstraint in the same project many times but it doesn't work in this case.
My configuration is
Django - 3.0.4
Django Rest Framework - 3.11.0
Database - MySql
Please help and ask if any information is missing out.
Your logs don't show any attempt to insert the same record into the DB twice, just validation.
It appears Django Rest Framework does not import constraints from UniqueConstraint, and only has code to validate with the old Meta.unique_together constraint definition.
There is a tracking issue, ModelSerializer not generating validators for constraints, on the DRF GitHub where you can follow the latest updates on adding support for UniqueConstraint validation.
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 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.
I have a contact information model/form which I want to add a contacttype foreign key to that should be displayed in a drop down menu:
models.py
class ContactType(models.Model):
contact_type = models.CharField(max_length=50)
class Contact(models.Model):
contact_name = models.CharField(max_length=200)
contact_email = models.CharField(max_length=200, validators=[validators.validate_email])
contact_type = models.ForeignKey(ContactType, default='General Enquiry')
forms.py
class ContactForm(forms.ModelForm):
# contact_type = forms.ModelChoiceField(queryset=ContactType.objects.all())
class Meta:
model = Contact
fields = ['contact_name', 'contact_email']
If I leave the contact_type variable where it is, I get the following error:
no such table: contact_contacttype
Here is my last migration:
dependencies = [
('contact', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='ContactType',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('contact_type', models.CharField(max_length=50)),
],
),
migrations.AddField(
model_name='contact',
name='contact_type',
field=models.ForeignKey(default='General Enquiry', on_delete=django.db.models.deletion.CASCADE, to='contact.ContactType'),
),
]
whereas if I move it into meta class (should I?) nothing renders into the template.
So my question is, is a foreign key the appropriate relationship? And if so, how should I add the ModelChoiceField to the ContactForm
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?