django boolean default not respected when not specifically excluded in form - python

I'm using django 1.5.
In my model definition, I have a boolean field with default=True defined. However, when a record is saved using my modelForm, the value is not saved as True with the record; it is saved as False.
forms.py
class ExampleForm(forms.ModelForm):
hours = forms.DecimalField(required=False,
max_digits=5,
decimal_places=2,
widget=forms.TextInput(attrs={'placeholder':'3.5'}))
class Meta:
model = Example
exclude = ('fk')
models.py
class Example(models.Model):
id = models.AutoField(primary_key=True)
fk = models.ForeignKey(FK)
hours = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True)
active = models.BooleanField(default=True)
They way this is set up to me it would seem that since active is not provided by the form that there is no value supplied when validating the record, therefore it should default to the default value set in the model. That is NOT the case. It defaults to False. However, I notice that if I explicitly add it to excluded fields, then the default does stay at True. I.e. if I change this line exclude = ('fk') to exclude = ('fk','active') and change nothing else, the validation works as I would expect.
Is this the correct behavior? My assumption that if a model field is not specified in the form it will use the default value defined in the model is wrong? This seems counterintuitive to me... just want to make sure I'm not missing anything.

Related

Django is putting empty string by default while expecting it to be null in DB column

I am using postgresql version 10.6 with my Django 2.1 application. The problem is that when I am using null=True in my model field it is translating to empty string ('') in database column as default where I am trying default it as null.
In my following code sample image column should be null:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(upload_to='profile_pics', null=True, blank=True)
And I am calling this class from a signal like this:
#receiver(post_save, sender=User)
def create_profile(sender, **kwargs):
if kwargs.get('created') is True:
Profile.objects.create(user=kwargs.get('instance'))
In this snapshot you can see that image column is inserting as empty string instead of null
I have also tried to use default value for my model field as:
image = models.ImageField(default=None, upload_to='profile_pics', null=True, blank=True)
but it doesn't work either.
image = models.ImageField(upload_to='profile_pics', null=True)
remove the blank=True
Null: It is database-related. Defines if a given database column will accept null values or not.
Blank: It is validation-related. It will be used during forms validation when calling form.is_valid().
The default values of null and blank are False.
You should remove blank=True

Get minimum of related model date field (django)

I have the two following classes:
class Incident(models.Model):
iid = models.IntegerField(primary_key=True)
person = models.ForeignKey('Person', on_delete=models.SET_NULL, null=True)
class Source(models.Model):
sid = models.IntegerField(primary_key=True)
incident = models.ForeignKey('Incident', on_delete=models.SET_NULL, null=True)
url = models.TextField(validators=[URLValidator()])
datereported = models.DateField(null=True, blank=True)
I want to create a field within the Incident that will pull the minimum datereported of related sources. Is this best done in the model, or in the template? Unsure what best practice is, or how to execute in this case.
Is this best done in the model, or in the template?
Well a template should - strictly speaking - not contain business logic. It should contain render logic. It should thus specify how something should be visible, not what should be visible. So it dos not really belong in the template layer, only in the model layer.
You can obtain the smallest datereported with:
from django.db.models import Min
class Incident(models.Model):
iid = models.IntegerField(primary_key=True)
person = models.ForeignKey('Person', on_delete=models.SET_NULL, null=True)
#property
def first_reporteddate(self):
return self.source_set.aggregate(first=Min('datereported'))['first']
This will ignore Sources with datereported set to None (so if there are multiple sources, it takes the smallest datereported that is not None). If there are no Source with a datereported not equal to None, or no related Sources at all, it will return None however, since the minimum of an empty set is considered to be NULL (in SQL, or None in Python/Django).
You can then use this in the template like:
{{ some_incident.first_reporteddate }}
In case you want the entire object, you can use self.source_set.order_by('datereported').first() which will give you the earliest related Source instance. But this will have a (quite) small impact on performance (it will take a bit longer). In that case Django will fetch all columns into memory first. If you thus only need one column, this will result in the fact that you did some useless serialization (at the database end) and deserialization (at the Python end).
You can use model's property for this:
class Incident(models.Model):
iid = models.IntegerField(primary_key=True)
person = models.ForeignKey('Person', on_delete=models.SET_NULL, null=True)
#property
def first_datereported(self):
first_source = self.source_set.order_by('datereported').first()
if first_source:
return first_source.datereported
In template or in any other part of code you can use first_datereported as normal model's field:
{{ incident_instance.first_datereported }}

FieldError: Unknown field(s) (created_at, modified_at) specified for Account

I am trying to create a timestamp for my model Account, but I don't want my two time stamps (created_at and modified_at) to be editable or even viewable by the user. Everything works fine and as expected until I add editable=False to the created_at and modified_at fields. Here is my model:
class Account(models.Model):
account_name = models.CharField(max_length=25)
active = models.BooleanField(default=True)
created_at = models.DateTimeField(null=True, blank=True, editable=False)
modified_at = models.DateTimeField(null=True, blank=True, editable=False)
def save(self):
if self.id:
self.modified_at = datetime.datetime.now()
else:
self.created_at = datetime.datetime.now()
super(Account, self).save()
class Meta:
ordering = ('id',)
Here is the obscure error I get when I try to do anything (migrate, runserver, etc):
django.core.exception.FieldError: Unknown field(s) (created_at, modified_at) specified for Account
As soon as I remove editable=False from both fields, everything works fine. Is this a Django bug? Is there a better way to make the field non-viewable and non-editable by the user?
I am using Django 1.9 and Python 3.6.1. Thanks for the help, let me know if you need me to post anything else (views, serializers, etc).
EDIT
Full traceback: https://pastebin.com/YEQACX5z
Accounts Form:
class AccountForm(ModelForm):
class Meta:
model = Account
fields = ['account_name', 'active', 'created_at', 'modified_at']
You could just do,
created_at = models.DateTimeField(auto_now_add=True)
and
modified_at = models.DateTimeField(auto_now=True)
From the docs,
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.
So, no need to add editable=False, its already non-editable.
Also, remember to remove your save() method override since it's trying to modify those fields.
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.

Provide default value for optional field in Django model

I have written a Python model as given below:
from django.db import models
class Product(models.Model):
title = models.CharField(max_length=255, unique=True)
description = models.TextField(blank=True)
image_url = models.URLField(blank=True)
quantity = models.PositiveIntegerField(default=0)
def sell(self):
self.quantity = self.quantity - 1
self.save()
return self.quantity
When I am trying to create the schema using migrate, I get the following message:
You are trying to add a non-nullable field 'description' to product without a default; we can't do that (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)
2) Quit, and let me add a default in models.py
Select an option:
My question is, if I am setting 'blank=True' for 'description', is it necessary to specify a default value for the field? Or am I missing something else?
blank=True is not the same as null=True, as the documentation explains. When a text field is blank, it still needs some kind of value: but that value can be the empty string.
So, just select option 1, and enter '' as the default value.
There is a ticket created for this behavior for Django 1.7.
Take a look here
You need to use default="".

Implementing USZipCodeField and USStateField in django

I'm looking to implement a zipcode field in django using the form objects from localflavor, but not quite getting them to work. I want to have a zipcode field in a form (or ModelForm in my case), but the fields never validate as a zipcode when calling _get_errors() on the form object. The way I'm implementing it seems right to me but is apparently wrong, does anyone know what the right way to do this might be?
I have a ModelForm that I want to use zipcode (and also USStateField) in:
from django.contrib.localflavor.us.forms import USStateField
from django.contrib.localflavor.us.forms import USZipCodeField
class FooForm(ModelForm):
class Meta:
model = Bar
fields = ('address', #This form uses a subset of fields from the model
'address_apt',
'address_city',
'address_state',
'address_zip',
'home_phone',
'mobile_phone')
widgets= {
'address_zip' : USZipCodeField(),
'address_state' : USStateField(),
}
The ModelForm 'FooForm' links to a model that looks like:
from django.contrib.localflavor.us import models as usmodels
class Bar(models.Model):
db_table = 'BAR'
address = models.CharField(max_length=255)
address_apt = models.CharField(max_length=40, blank=True)
address_city = models.CharField(max_length=90)
address_state = usmodels.USStateField()
address_zip = models.CharField(max_length=15)
home_phone = usmodels.PhoneNumberField( )
mobile_phone = usmodels.PhoneNumberField( )
#... There are more fields in the model...
But if I create an instance of the form and run it's validation, it never cares about the form level validation, only the model level validation:
foo_instance = FooForm(request.POST)
#Let's assume request.POST looks like:
#<QueryDict: {u'address_city': [u'asdf'], u'mobile_phone': [u'asdf'], u'address_state': [u'California'], u'home_phone': [u'asdf'], [u'1'], u'address': [u'123 foo'], u'address_zip': [u'asdf']}>
foo_instance._get_errors()
Yields:
<ul class="errorlist">
<li>mobile_phone<ul class="errorlist">
<li>Phone numbers must be in XXX-XXX-XXXX format.</li></ul>
</li><li>home_phone<ul class="errorlist">
<li>Phone numbers must be in XXX-XXX-XXXX format.</li></ul>
</li></ul>
I need to be able to call validation on the populated form object and have it tell me that the zipcode is formated improperly if so. Doing something wrong, just don't know what atm.
Using widgets declaratively has literally only just been added to the trunk SVN version in the last day or so. If you're using an older checkout, or a released version, it won't work - you'll need to go back to the old way of doing it, by overriding the field declarations at the top level of the form.

Categories