django admin saving inline with blank value - django

I have two models as
class Employer(models.Model):
..
..
class JobTitle(models.Model):
type = models.IntegerField(null=False, choices=JobTitles.CHOICES,blank=True,default=0)
employer = models.OneToOneField(Employer,unique=True,null=False)
I have defined admin for Employer and defined JobTitle as inline class.
However, when I saved an Employer object without selecting JobTitle, it raises the error invalid literal for int() with base 10: '' due to the type field of JobTitle.
I would like to set type to 0, as I defined default=0, if I don't select any jobtitle when employer is saved.
How can I achieve this ?

So, I'm inferring this from the code you posted, but I think that you're mixing strategies here. I assume based on the way you've constructed your question that you want to me able to make a job title with no type. Alternatively, you want one job type to be the default selection. I further infer, although now I'm stretching a bit, that you have a custom "No Type" choice (you didn't show us your JobTitles.CHOICES tuple so I have to guess) that corresponds to the 0 value that you've set as the default.
If you want to have a default job type (even if you're calling it "No Job Type" or something similar), then you should set it using the default keyword argument (as you have done) and you should set null=False and blank=False (alternatively, omit both, as these are the default values).
However, if you want it to be possible that there is a job title with no type, then unless you have some reason in your implementation to do something else, the purest way to represent this in data is to use the null SQL value. If you want to go that route, the correct way to do it is to set blank=True and null=True, and then either set default=None or omit that keyword argument entirely.
That should get you the behavior that you seek.

For Model arguments, default is used when Django save your data to Database...
On the other hand, your error occurs during data parsing. When you submit your form, django parses data to relevant type if needed. When your html form is submitted, django recieves string data like
{'title':'12', 'employer_id':'23'...}
So, django have to parse type into ineger before it save it to the database... Since your forms sends a nullstring '' , int('') simply fails with TypeError.
What you must do is removing blank=True. Or somehow you must override the default behaviour of combobox default value and set it 0 instead of ''.

Related

How to set Django model date field default value to future date?

I'm trying to set the default value for a Date field to a future date with respect to today. However, it gives me the following warning when I set it as below.
return_date = models.DateField(default=(timezone.now() + timedelta(days=1)))
booking.Booking.return_date: (fields.W161) Fixed default value provided.
HINT: It seems you set a fixed date / time / datetime value as default for
this field. This may not be what you want. If you want to have the
current date as default, use `django.utils.timezone.now`
Same warning with the following code.
return_date = models.DateField(default=(date.today() + timedelta(days=1)))
What is the correct way to do this?
Thanks.
You are giving it a fixed time(cause you are calling the timezone.now() so its returned value will be the default) you should pass the function to the default without calling it, like this
def return_date_time():
now = timezone.now()
return now + timedelta(days=1)
and in your field:
return_date = models.DateField(default=return_date_time)
### dont call it, so it will be evaluated by djanog when creating an instance
Try using datetime.date instead of adding to timezone.now
Here is an excerpt from django docs
DateField
class DateField(auto_now=False, auto_now_add=False, **options)
A date, represented in Python by a datetime.date instance. Has a few extra, optional arguments:
DateField.auto_now
Automatically set the field to now every time the object is saved. Useful for “last-modified” timestamps. Note that the current date is always used; it’s not just a default value that you can override.
The field is only automatically updated when calling Model.save(). The field isn’t updated when making updates to other fields in other ways such as QuerySet.update(), though you can specify a custom value for the field in an update like that.
DateField.auto_now_add
Automatically set the field to now when the object is first created. Useful for creation of timestamps. Note that the current date is always used; it’s not just a default value that you can override. So even if you set a value for this field when creating the object, it will be ignored. If you want to be able to modify this field, set the following instead of auto_now_add=True:
For DateField: default=date.today - from datetime.date.today()
For DateTimeField: default=timezone.now - from django.utils.timezone.now()
The default form widget for this field is a TextInput. The admin adds a JavaScript calendar, and a shortcut for “Today”. Includes an additional invalid_date error message key.
The options auto_now_add, auto_now, and default are mutually exclusive. Any combination of these options will result in an error.

Storing Default Values for BooleanField() and IntegerField() in Django REST

I know as per the documentation the following fields do not take allow_blank=True and allow_null=True
BooleanField()
IntegerField()
I need to allow the client to not specify g or d (as per below) and to store the value in the DB as None.
g = serializers.BooleanField()
d = serializers.IntegerField()
Any ideas ?
The different options for handling of empty, blank and null fields are (necessarily) a little subtle, so its unsurprising that it sometimes trips folks up.
I know as per the documentation the following fields do not take allow_blank=True and allow_null=True
That's incorrect:
IntegerField(allow_null=True) is valid.
If you want to allow null inputs for boolean fields you need to use the NullBooleanField() class.
You are correct that neither of them take allow_blank, as the empty string isn't going to be a valid value in either case.
I need to allow the client to not specify g or d (as per below) and to store the value in the DB as None.
You can either use IntegerField(default=None) and NullBooleanField(default=None).
In this case when the values are omitted they will be included as None in serializer.validated_data. You'll want to make sure you use null=True/NullBooleanField on the model field.
Or IntegerField(required=False) and NullBooleanField(required=False).
In this case when the values are omitted they will not be included in serializer.validated_data, and the model field default will be used. You'll want to make sure you use default=None and null=True/NullBooleanField on the model field.
Note that there was a bug when using the Browsable API that empty fields in HTML input did not get the default values set. This is resolved in the upcoming 3.1.4 release.
The initial argument suggested in Edwin's answer can also be useful, but is for setting an value to be initially rendered in HTML form fields.
Django Rest Framework Serializer has initial options to set the dafault value of a field.
g = serializers.BooleanField(initial=True)
d = serializers.IntegerField(initial=0)
http://www.django-rest-framework.org/api-guide/fields/

How to deal with the required value which does not have a default value in Django model

The version of my Django is 1.7.
I have a model named Booking, it has a Boolean field named is_departure, which is used to describe the booking is departure or arrival.
class Booking(models.Model):
...
is_departure = models.BooleanField()
...
When I migrate my app, it will return me a warning that is_departure does not have a default value.
However, I do not want to add a default value for is_departure. This is a required value and it needs to be filled by user. I do not want to use NullBooleanField neither, because is_departure should not be null.
Is there any good way to remove this warning?
The problem is, what will Django put as a value for all the existing rows that now have a is_departure value that according to you, cannot be null, you can't satisfy this constraint.
If you're still developing, then you can reset the DB and you can indeed use BooleanField without default (since there will be no existing rows violating this)
Otherwise, I'd make the migration put a is_departure value (true or false) on the existing rows, consistent with your business logic

Should I use a default empty string or allow null value in Django fields

I guess this question is very relevant outside Django ORM too. Since I am working on it, I will put it this way.
Say, I have a django model abstract class having the below field
content = models.TextField()
One inherited model definitely requires it and another takes it based on the choice (another field). I see there are two ways to go:
use null=True, blank=True in abstract class, make necessary conditional validation statements in inherited classes
use default='' in abstract class and make necessary conditional validation
There are two things I'd like to achieve here in this case
Form validation should be conditional
Database validation should be conditional
Which is more preferred approach? and Why not the other?
From django docs:
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.
You dont have to specify empty string for default because that is what django use for "no-data" anyway. Avoid null. I recommend you specify the field as blank=True (no null) and enforce your validation at form level.

heroku, postgreSQL, django, comments, tastypie: No operator matches the given name and argument type(s). You might need to add explicit type casts

I have a simple query on django's built in comments model and getting the error below with heroku's postgreSQL database:
DatabaseError: operator does not exist: integer = text LINE 1:
... INNER JOIN "django_comments" ON ("pi ns_pin"."id" = "django_...
^
HINT: No operator matches the given name and argument type(s).
You might need to add explicit type casts.
After googling around it seems this error has been addressed many times before in django, but I'm still getting it (all related issues were closed 3-5 years ago) . I am using django version 1.4 and the latest build of tastypie.
The query is made under orm filters and works perfectly with my development database (sqlite3):
class MyResource(ModelResource):
comments = fields.ToManyField('my.api.api.CmntResource', 'comments', full=True, null=True)
def build_filters(self, filters=None):
if filters is None:
filters = {}
orm_filters = super(MyResource, self).build_filters(filters)
if 'cmnts' in filters:
orm_filters['comments__user__id__exact'] = filters['cmnts']
class CmntResource(ModelResource):
user = fields.ToOneField('my.api.api.UserResource', 'user', full=True)
site_id = fields.CharField(attribute = 'site_id')
content_object = GenericForeignKeyField({
My: MyResource,
}, 'content_object')
username = fields.CharField(attribute = 'user__username', null=True)
user_id = fields.CharField(attribute = 'user__id', null=True)
Anybody have any experience with getting around this error without writing raw SQL?
PostgreSQL is "strongly typed" - that is, every value in every query has a particular type, either defined explicitly (e.g. the type of a column in a table) or implicitly (e.g. the values input into a WHERE clause). All functions and operators, including =, have to be defined as accepting specific types - so, for instance there is an operator for VarChar = VarChar, and a different one for int = int.
In your case, you have a column which is explicitly defined as type int, but you are comparing it against a value which PostgreSQL has interpreted as type text.
SQLite, on the other hand, is "weakly typed" - values are freely treated as being of whatever type best suits the action being performed. So in your dev SQLite database the operation '42' = 42 can be computed just fine, where PostgreSQL would need a specific definition of VarChar = int (or text = int, text being the type for unbounded strings in PostgreSQL).
Now, PostgreSQL will sometimes be helpful and automatically "cast" your values to make the types match a known operator, but more often, as the hint says, you need to do it explicitly. If you were writing the SQL yourself, an explicit type case could look like WHERE id = CAST('42' AS INT) (or WHERE CAST(id AS text) = '42').
Since you're not, you need to ensure that the input you give to the query generator is an actual integer, not just a string which happens to consist of digits. I suspect this is as simple as using fields.IntegerField rather than fields.CharField, but I don't actually know Django, or even Python, so I thought I'd give you the background in the hope you can take it from there.
Building on IMSoP's answer: This is a limitation of django's ORM layer when a Generic foreign key uses a text field for the object_id and the object's id field is not a text field. Django does not want to make any assumptions or cast the object's id as something it's not. I found an excellent article on this http://charlesleifer.com/blog/working-around-django-s-orm-to-do-interesting-things-with-gfks/.
The author of the article, Charles Leifer came up with a very cool solution for query's that are affected by this and will be very useful in dealing with this issue moving forward.
Alternatively, i managed to get my query to work as follows:
if 'cmnts' in filters:
comments = Comment.objects.filter(user__id=filters['cmnts'], content_type__name = 'my', site_id=settings.SITE_ID ).values_list('object_pk', flat=True)
comments = [int(c) for c in comments]
orm_filters['pk__in'] = comments
Originally i was searching for a way to modify the SQL similar to what Charles has done, but it turns out all i had to do was break the query out into two parts and convert the str(id)'s to int(id)'s.
To do not hack you ORM and external software postgres allow you register your own casts and compare operations. Please look example in similar question.