Django CharField with dynamic default value - python

I want to add a new field to a PostgreSQL database.
It's a not null and unique CharField, like
dyn = models.CharField(max_length=31, null=False, unique=True)
The database already has relevant records, so it's not an option to
delete the database
reset the migrations
wipe the data
set a default static value.
How to proceed?
Edit
Tried to add a default=uuid.uuid4
dyn = models.CharField(max_length=31, null=False, unique=True, default=uuid.uuid4)
but then I get
Ensure this value has at most 31 characters (it has 36).
Edit 2
If I create a function with .hex (as found here)
def hex_uuid():
"""
The UUID as a 32-character lowercase hexadecimal string
"""
return uuid.uuid4().hex
and use it in the default
dyn = models.CharField(max_length=31, null=False, unique=True, default=hex_uuid)
I'll get
Ensure this value has at most 31 characters (it has 32).
Note: I don't want to simply get a substring of the result, like adjusting the hex_uuid() to have return str(uuid.uuid4())[:30], since that'll increase the collision chances.

I ended up using one of the methods shown by Oleg
def dynamic_default_value():
"""
This function will be called when a default value is needed.
It'll return a 31 length string with a-z, 0-9.
"""
alphabet = string.ascii_lowercase + string.digits
return ''.join(random.choices(alphabet, k=31)) # 31 is the length of the string
with
dyn = models.CharField(max_length=31, null=False, unique=True, default=dynamic_default_value)
If the field was max_length of 32 characters then I'd have used the hex_uuid() present in the question.
If I wanted to make the dynamic field the same as the id of another existing unique field in the same model, then I'd go through the following steps.

you can reset the migrations or edit it or create new one like:
python manage.py makemigrations name_you_want
after that:
python manage.py migrate same_name
edit:
example for funcation:
def generate_default_data():
return datetime.now()
class MyModel(models.Model):
field = models.DateTimeField(default=generate_default_data)

Related

Error while changing a Char field to Array Field in Django

I am trying to change a existing CharField in a django model (it also allows null and contains null values in db currently)
now I am trying to change it to an Array Char Field, but it throws the below error
"django.db.utils.IntegrityError: column "tags" contains null values"
From
tags= models.CharField(max_length=255, blank=True, null=True)
To
tags = ArrayField(models.CharField(max_length=255, blank=True, null=True))
I have selected option 2 while before running migrate
In this option 2 you selected, you were asked to select a default value yourself, right?
Add a default attribute to your model's field. The existing null values will be turned into that value, for instance:
tags = ArrayField(models.CharField(max_length=255, blank=True, null=True, default=[]))

Converting UUIDField to CharField

This is currently what I have in my models.py:
class Campaign(models.Model):
campaign_id = UUIDField(auto=True)
name = models.CharField("Campaign Name", max_length=255)
class CampaignResponse(models.Model):
campaign = models.ForeignKey(Campaign)
user = models.EmailField("Email", max_length=200)
response = models.TextField()
Recently I was thinking of changing the campaign_id in the Campaign model from UUIDField to CharField instead, but when I tried to do so, I noticed the data for campaign_id is being altered (e.g. the data was "7182c679f72844ca83c9648a120bb066" when UUIDField is being used, but it now becomes "7182c679-f728-44ca-83c9-648a120bb066" after converting it to CharField).
Is there some steps I have to follow where I can convert that UUIDField to CharField without changing the data and ensure the relationship between Campaign and CampaignResponse is not broken?
campaign_id is not the primary key for model Campaign. But probably it's for the best because now your relations can't break when you convert UUID to CharField.
First make campaign_id CharField
campaign_id = models.CharField(max_length=36, ) # add other stuff too which you want like unique etc.
About converting from uuid to str, open shell py manage.py shell and run this:
from myApp.models import Campaign
for c in Campaign.objects.all():
c.campaign_id = str(c.campaign_id).replace('-', '')
Check your database to make sure it worked. Now you can decrease the max_length of CharField to 32 since all the extra dash (-) from campaign_id are gone.
Just because a Field is UUID doesn't means it's the primary key. To make a field primary key you need to pass primary_key=True. You can make primary key like this
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
The two commenters are correct, the UUID is not your primary key here. Any foreign keys relating to your campaign are holding the primary key (PK), which is just an auto-incrementing integer here.
Unfortunately it can sometimes be a pain to change primary keys for exactly the reason you're worried about. I've gone through this before, here's what I suggest:
Leave your primary keys as they are. It will ensure that you won't have any problems with relations. If you want a UUID field to be able to look up objects or pass around the reference without the potential issues caused by integer PKs. An example of such an issue would be if you're using a REST API, it would look weird if someone saw this link:
GET /campaigns/6 <-- because they would know to just try GET /campaigns/7 and maybe they'd see something they weren't meant to see.
clearly GET /campaigns/7182c679-f728-44ca-83c9-648a120bb066 is better.
You can add unique=True to your UUID field to ensure django creates an index for it, then the performance wouldn't differ.
Does this answer you question?

Is it possible to set the constraint unique = True except for empty string into SQLAlchemy?

I use the following column in my table:
email = Column(String(60), unique=True)
However, I need the possibility to save empty strings in this column. Namely, I need something like this:
email = Column(String(60), unique=True, blank=True)
but it doesn't work. Is it possible to implement this within SQLAlchemy? What is correspondent SQL (Postgresql dialect)?
You have to try to set not an empty string, but a NULL istead. As it's said in PostgreSql documentation NULL-values are not equals.

Django ModelForm won't allow Null value for required field?

I have a Model with this field...
fulfillment_flag = models.CharField(pgettext_lazy('Delivery group field', 'Fulfillment Flag'), max_length=255,
null=True, choices=FULFILLMENT_FLAG_CHOICES)
And here is my form...
class FulfillmentFlagForm(forms.ModelForm):
class Meta:
model = DeliveryGroup
fields = ['fulfillment_flag', ]
def clean_fulfillment_flag(self):
return self.cleaned_data['fulfillment_flag'] or None
I have an HTML select drop down that has a blank value option at the top. Every time I select the blank option and click Save, form will not save it as a Null value on my model. It'll save any of the other fields though, just not the blank one. And it will tell me that the field is required as well.
How can I tell the form to just save the blank value as Null?
https://docs.djangoproject.com/en/1.8/ref/models/fields/#null
Avoid using null on string-based fields such as CharField and TextField because empty string values will always be stored as empty strings, not as NULL. If a string-based field has null=True, that means it has two possible values for “no data”: NULL, and the empty string. In most cases, it’s redundant to have two possible values for “no data;” the Django convention is to use the empty string, not NULL.
For both string-based and non-string-based fields, you will also need to set blank=True if you wish to permit empty values in forms, as the null parameter only affects database storage (see blank).
Do blank=True instead and save "" instead of None.

Can I change the type of Model Field?

Say, I have a model with this field:
is_retired = models.NullBooleanField()
It has never been set for any of the records i the database, so I think all the records will have a null value for this field.
And now want to change it to a string field:
is_retired = models.CharField(max_length=50)
I am using Django 1.7 and ran makemigrations which seemed to go fine, said something about altered field. However, when I actually run the migrate command it fails with
django.db.utils.IntegrityError: column "is_retired" contains null values
How can I resolve this?
If you want to enable null/empty values, change it to:
is_retired = models.CharField(max_length=50, null=True, blank=True)
You might also want to change the null values to empty strings ('') in another migration.

Categories

Resources