I have the following model:
class Site(models.Model):
"""
Model for a site entry
#author: Leonardo Pessoa
#since: 05/09/2016
"""
from decimal import Decimal
consolidated_financials = models.BooleanField(blank=True)
type = models.ForeignKey(Type)
tier1_business = models.ForeignKey(Business, limit_choices_to = {'tier': 1}, related_name='%(class)s_tier1')
Note that the consolidated_financials field has now the blank=True statement.
This was newly included. When I ran makemigrations, it didn't get the change, but when I add to finance_manager_sso it worked normally.
Is there a restriction with the Boolean field specifically?
Thanks
BooleanField does not take null=True, instead, use NullBooleanField.
There is a great answer on Programmers.se by Jacob Maristany that explains the reasoning behind this well
By allowing nulls in a boolean field, you are turning an intended binary representation (true/false) into a tri-state representation (true, false, null) where your 'null' entries are indeterminate.
For the full discussion, see Should I store False as Null in a boolean database field?
The blank parameter is not used by BooleanField. Instead, it is hard-coded to True. Passing blank=False has no effect, so the migration autodetector doesn't detect any changes to the field, and doesn't create any migrations.
Since the blank parameter is used by IntegerField, passing in blank=False will lead to a change in the serialized field. The migration autodetector will detect that change and create a migration (even though that change doesn't affect the database).
Related
I added the field user which is a foreign key to another model called User. This field was added to the model called Bid. However, when I tried to migrate the changes, I got the message:
It is impossible to add a non-nullable field 'user' to bid without specifying a default. This is because 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 manually define a default value in models.py.
Last time, I set it to 'user' and got an error that stated: ValueError: invalid literal for int() with base 10: 'user'.
What should I set the default value as?
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)
As the error says, you are creating a new field in your table. When creating a new field, existing rows need to be taken into consideration. Safest approach is to set it as null=True and handle the field later.
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
However you may not want a Bid to have a null user. In which case I recommend looking into how to write a custom migration to populate existing rows.
Another note: if the data you have in your table is not needed, you could consider dropping the table and rebuilding it or deleting your migrations and creating them again with manage.py makemigrations - again only if the data you have in your db is not needed.
If you add a relationship via a new foreign key, you have two options
You make the new FK nullable and let it default to NULL (i.e. None in python). This way, legacy entries with unknown relations will have NULL as the FK, i.e. do not know their users.
You manually populate the legacy fields with the appropriate foreign keys to the Bid records. This requires that you have kept that information beforehand.
I have a model name CustomerProject and I want to track individually few fields when it was last updated. Right now I am using auto_now= True in datetime fields which can tell me when the whole model was last edited but I want to track individually few boolean fields such as when the work started? when the work delivered etc. here is my
models.py
class CustomerProject(models.Model):
project_title = models.CharField(max_length=2000,blank=True,null=True)
project_updated_at = models.DateTimeField(auto_now= True,blank=True,null=True)
#I want to track separately those BooleanField
project_started = models.BooleanField(default=True)
wting_for_sample_work = models.BooleanField(default=False)
sample_work_done = models.BooleanField(default=False)
working_on_final_project = models.BooleanField(default=False)
project_deliverd = models.BooleanField(default=False)
project_closed = models.BooleanField(default=False)
I think there might be three solutions for this.
First one is using simple history library.
https://django-simple-history.readthedocs.io/en/latest/
It creates a history model for your model and keeps track of all changes. This way, you can check which field is modified when, or go back to that state easily. But if you only want to keep track of the time which this booleans are set to True, this might be an overkill.
Second option would be to add a JSONField to keep track a time for any field you want.
Would be something like that.
{
"project_started": "2021-08-01 15:18:31",
"wting_for_sample_work": "2021-08-03 15:18:31"
}
You can set it in your view (by checking if each field is changed).
Third option would be just change these booleans to DateTimeField.
Then, you can check those datetime fields. If it is set, it is true and vice versa.
I think easiest is to go with 3rd option.
Using Django 1.5.1. Python 2.7.3.
I wanted to do a unique together constraint with a foreign key field and a slug field. So in my model meta, I did
foreign_key = models.ForeignKey("self", null=True, default=None)
slug = models.SlugField(max_length=40, unique=False)
class Meta:
unique_together = ("foreign_key", "slug")
I even checked the table description in Postgres (9.1) and the constraint was put into the database table.
-- something like
"table_name_foreign_key_id_slug_key" UNIQUE CONSTRAINT, btree (foreign_key_id, slug)
However, I could still save into the database table a foreign_key of None/null and duplicate strings.
For example,
I could input and save
# model objects with slug="python" three times; all three foreign_key(s)
# are None/null because that is their default value
MO(slug="python").save()
MO(slug="python").save()
MO(slug="python").save()
So after using unique_together, why can I still input three of the same valued rows?
I'm just guessing right now that it might have to do with the default value of None for the foreign_key field, because before the unique_together, when I just had unique=True on slug, everything worked fine. So if that is the case, what default value should I have that indicates a null value, but also maintains the unique constraint?
In Postgresql NULL isn't equal to any other NULL. Therefore the rows you create are not the same (from Postgres' perspective).
Update
You have a few ways to deal with it:
Forbid the Null value for foreign key and use some default value
Override the save method of your model to check that no such row exists
Change SQL standard :)
Add a clean method to your model, so you can edit an existing row.
def clean(self):
queryset = MO.objects.exclude(id=self.id).filter(slug=self.slug)
if self.foreign_key is None:
if queryset.exists():
raise ValidationError("A row already exists with this slug and no key")
else:
if queryset.filter(foreign_key=self.foreign_key).exists():
raise ValidationError("This row already exists")
Beware, clean (or full_clean) isn't called by the default save method.
NB: if you put this code in the save method, update forms (like in the admin) won't work: you will have a traceback error due to the ValidationError exception.
Just manually create secondary index on slug field, but only for NULL values in foreign_key_id:
CREATE INDEX table_name_unique_null_foreign_key
ON table_name (slug) WHERE foreign_key_id is NULL
Please note, that Django does not support this, so without custom form/model validation you will get pure IntegrityError / 500.
Possible duplicate of Create unique constraint with null columns
As hobbyte mentioned, "In Postgresql NULL isn't equal to any other NULL. Therefore the rows you create are not the same (from Postgres' perspective)."
Another possible way to address this challenge is to add custom validation at the view level in the form_valid method.
In views.py:
def form_valid(self, form):
--OTHER VALIDATION AND FIELD VALUE ASSIGNMENT LOGIC--
if ModelForm.objects.filter(slug=slug,foreign_key=foreign_key:
form.add_error('field',
forms.ValidationError( _("Validation error message that shows up in your form. "),
code='duplicate_row', ))
return self.form_invalid(form)
This approach is helpful if you are using class based views, especially if you are automatically assigning values to fields that you want to hide from the user.
Pros:
You don't have to create dummy default values in the database
You can still use update forms (see Toff's answer)
Cons:
- This doesn't protect against duplicate rows created directly at the database level.
- If you use Django's admin backend to create new MyModel objects, you'll need to add this same validation logic to your admin form.
How can I set a default value for FloatField in django.
Initially when I had created the model and declared the float field as:
cost = models.FloatField(null=True, blank=True)
and db also created successfully through south migration.
But now when I am trying to do edit on that field using the html form, and no value is entered in the cost field, in the form its throwing value error.
Thus I think if I can set a default value for cost field it may fix the problem.
But I don't know how to set a default value for FloatField in django.
Your code should work. Perhaps something is setting the empty value to a non-null value (like an empty string). I'd have to see the full error message.
Since this is an old thread, I'm going to post the answer to the question I came here for, "How do you set FloatField default to Null in the database?"
cost = models.FloatField(null=True, blank=True, default=None)
Float Field is quite related to Decimal Field so, probably it should take a default argument. Try doing this..
cost = models.FloatField(default=to_some_value)
How come my "date" field doesn't come up in the admin system?
In my admin.py file i have
from django.contrib import admin
from glasses.players.models import *
admin.site.register(Rating)
and the Rating model has a field called "date" which looks like this
date = models.DateTimeField(editable=True, auto_now_add=True)
However within the admin system, the field doesn't show, even though editable is set to True.
Does anyone have any idea?
If you really want to see date in the admin panel, you can add readonly_fields in admin.py:
class RatingAdmin(admin.ModelAdmin):
readonly_fields = ('date',)
admin.site.register(Rating,RatingAdmin)
Any field you specify will be added last after the editable fields. To control the order you can use the fields options.
Additional information is available from the Django docs.
I believe to reason lies with the auto_now_add field.
From this answer:
Any field with the auto_now attribute
set will also inherit editable=False
and therefore will not show up in the
admin panel.
Also mentioned in the docs:
As currently implemented, setting
auto_now or auto_now_add to True will
cause the field to have editable=False
and blank=True set.
This does make sense, since there is no reason to have the field editable if it's going to be overwritten with the current datetime when the object is saved.
Major Hack:
If you really need to do this (as I do) you can always hack around it by immediatley setting the field to be "editable" defining the field as follows:
class Point(models.Model):
mystamp=models.DateTimeField("When Created",auto_now_add=True)
mystamp.editable=True
This will make the field editable, so you can actually alter it. It seems to work fine, at least with the mysql backing engine. I cannot say for certian if other backing stores would make this data immutable in the database and thus cause a problem when editing is attempted, so use with caution.
Depending on your specific needs, and any nuances in difference in behavior, you could do the following:
from django.utils.timezone import now
class MyModel(models.Model):
date = models.DateTimeField(default=now)
The default field can be used this way: https://docs.djangoproject.com/en/dev/ref/models/fields/#default
The default value for the field. This can be a value or a callable object. If callable it will be called every time a new object is created.
This does not set editable to False
It might have to do with the auto_now_add being true. Perhaps instead of that parameter to capture the date on add, you could override the model save method to insert the datetime when the id is null.
class Rating(models.Model):
....
def save(self, *args, **kwargs)
if not self.id:
self.date = datetime.datetime.now()
If you want any field to be visible in the list of all your entries (when you click on a model in the admin people) and not when you open that particular entry then -
class RatingAdmin(admin.ModelAdmin):
list_display = ('name', 'date')
admin.site.register(Rating, RatingAdmin)
'name' being your main field or any other field you want to display in the admin panel.
This way you can specify all the columns you want to see.
Can be displayed in Django admin simply by below code in admin.py
#admin.register(model_name)
class model_nameAdmin(admin.ModelAdmin):
list_display = ['date']
Above code will display all fields in django admin that are mentioned in list_display, no matter the model field is set to True for auto_now attribute
You can not do that, check the documentation
auto_now and auto_now_add are all non-editable fields and you can not override them...
I wanted to create an editable time field that defaults to the current time.
I actually found that the best option was to avoid the auto_now or auto_add_now altogether and simply use the datetime library.
MyTimeField = models.TimeField(default=datetime.now)
The big thing is that you should make it's the variable now instead of a function/getter, otherwise you're going to get a new default value each time you call makemigrations, which will result in generating a bunch of redundant migrations.
This is a combination of the answers by Hunger and using a decorator as suggested by Rahul Kumar:
In your admin.py, you just need:
#admin.register(Rating)
class RatingAdmin(admin.ModelAdmin):
readonly_fields = ('date',)
The fields specified in readonly_fields will appear in the add and change page i.e. inside the individual record. And - of course - are not editable.
The fields in list_display will constitute the representation in the main list page of the admin (i.e. the list of all records). In this case, it makes sense not to specify list_display = ('date',) only, because you will see only the dates. Instead, include also the main title / name of the record.
Example:
readonly_fields = ('title', 'date',)
if in the models.py this model is defined as:
class Rating(models.Model):
title = models.CharField('Movie Title', max_length=150)
...
date = models.DateTimeField(editable=True, auto_now_add=True)
def __str__(self):
return self.title