I have a Model that looks like:
class MyModel(Model):
name = models.TextField(blank=True, default='')
bio = models.TextField(blank=True, default='')
and a ModelForm that looks like:
class MyModelForm(ModelForm):
class Meta:
model = MyModel
fields = ('name', 'bio')
When I create/ init my form like this:
form = MyModelForm(instance=my_model, data={'bio': 'this is me'}) # where my_model has a name already set
then:
form.is_valid() # evaluates to true
form.save() # overwrites the name field in my_model and makes it blank!
Is this the expected behaviour? How would I change this so that I can ensure that if a field is not specified in the form, but it exists already in the instance that it is not overwritten with an empty string?
Note that Django only sets the name field to empty because you have set blank=True. If the field was required, there would be a validation error instead.
Recently there was a discussion on the django-developers group about changing this behaviour. The core developers Adrian and Paul were in favour of the current behaviour.
So, if you're going to use model forms for this view with models where you use blank=True, you'll need to include all the model fields in your data dict. You can use the function model_to_dict for this.
from django.forms.models import model_to_dict
data = model_to_dict(my_model)
data.update({'bio': 'this is me'}) # or data.update(request.POST)
form = MyModelForm(instance=my_model, data=data)
Providing the instance argument to a ModelForm is the same as passing initial, i.e. they serve the same purpose. What gets save is always the data. If a field in that dict is empty, then it will be when the model is saved as well.
If you want to maintain the entire state of the model when only dealing with a single field as data. Then, try something like the following:
data = my_model.__dict__
data['bio'] = request.POST.get('bio')
MyModelForm(instance=my_model, data=data)
If you want to pass initial data to the form, use initial instead of data
MyModelForm(instance=my_model, initial={'bio': 'this is me'})
^
[edit]
If you have included the field for name in your form
fields = ('name', 'bio')
but do not pass any data for "name"
data={'bio': 'this is me'}
The form field will behave as if the name had been submitted blank.
Which is allowed in your model, so is_valid() will be True
name = models.TextField(blank=True, default='')
Related
I have problem with django:
models.py:
SUSPEND_TIME = (
('0', '0'),
('10', '10'),
('15', '15'),
('20', '20'),
class Order(models.Model):
user = models.ForeignKey(User)
city = models.CharField(max_length=20)
...
processed = models.BooleanField(default=False)
suspend_time = models.CharField(max_length=2, choices=SUSPEND_TIME, default='0')
..
form.py:
class OrderForm(forms.ModelForm):
class Meta:
model = Order
fields = ('suspend_time', 'processed')
view.py:
try:
order = Order.objects.get(id=order_id)
except Order.DoesNotExist:
order = None
else:
form = OrderForm(request.POST, instance=order)
if form.is_valid():
form.save()
....
then I send ajax request to update instance with only "processed" param..
form.is_valid is always False if I don't send "suspend_time" !
if request contain {'suspend_time': 'some_value' ...} form.is_valid is True
I don't understand why ? suspend_time has default value.. and order.suspend_time always has some value: default or other from choices.
why after form = OrderForm(request.POST, instance=order) form['suspend_time'].value() is None, other fields (city, processed) has normal value .
The behavior is as expected. The form should validate with given data. i.e. Whatever required fields are defined in the form, should be present in the data dictionary to instantiate it.
It will not use data from instance to populate fields that are not provided in form data.
Text from django model forms
If you’re building a database-driven app, chances are you’ll have forms that map closely to Django models. For instance, you might have a BlogComment model, and you want to create a form that lets people submit comments. In this case, it would be redundant to define the field types in your form, because you’ve already defined the fields in your model.
For this reason, Django provides a helper class that let you create a Form class from a Django model.
The following shows up instead of a field in my template.
<django.contrib.localflavor.us.forms.USStateSelect object at 0x92b136c>
my template has
{{ form.state }}
what could the issue be?
class RegistrationForm(forms.Form):
first_name = forms.CharField(max_length=20)
last_name = forms.CharField(max_length=20)
phone = USPhoneNumberField()
address1 = forms.CharField(max_length=45)
address2 = forms.CharField(max_length=45)
city = forms.CharField(max_length=50)
state = USStateSelect()
zip = USZipCodeField()
also is there anyway i can make the state and zip optional?
To limit the choices to a drop down list, use us.us_states.STATE_CHOICES in your model, and use us.forms.USStateField() instead of us.forms.USStateSelect() in your forms.
To make a field optional in a form, add blank = True to that field in the model.
from django.contrib.localflavor.us.us_states import STATE_CHOICES
from django.contrib.localflavor.us.models import USStateField
class ExampleLocation(models.Model):
address1 = models.CharField(max_length=45) #this is not optional in a form
address2 = models.CharField(max_length=45, blank = True) #this is made optional
state = USStateField(choices = STATE_CHOICES)
Instead of STATE_CHOICES, there are several options you can find in the localflavor documentation. STATE_CHOICES is the most inclusive, but that may not be what you desire. If you just want 50 states, plus DC, use US_STATES.
This answer assumes you're using ModelForms. If you aren't, you should be. Once you've made your model, you should follow DRY and create basic forms like so:
from django.forms import ModelForm
class ExampleForm(ModelForm):
class Meta:
model = ExampleLocation
And it inherits your fields from your model. You can customize what fields are available, if you don't want the whole model, with other class Meta options like fields or exclude. Model forms are just as customizable as any other form, they just start with the assumption of your model's fields.
I want to create a Django model Field (IntegerField) with a default value, and also create a form derived from the model, where the field is optional. If it's not set on the form, then when I save the form, I want the default value saved to the DB.
# model.py
class Invoice(models.Model):
# IntegrityError "Column 'expireDays' cannot be null"
expireDays = models.PositiveSmallIntegerField(default=1)
# expireDays = *null* in DB
expireDays = models.PositiveSmallIntegerField(default=1, blank=True, null=True)
# forms.py
class InvoiceForm(forms.ModelForm):
# leaving this line out gives invalid form
expireDays = forms.IntegerField(required=False)
class Meta:
model = Invoice
(I used only one of the field declaration lines at a time. :)
I'm not even sure that I'm declaring the default value correctly. The only reference I could find to it was in an article on handling choices by James Bennett. I have yet to find it in the Django docs (I'm using version 1.2 - maybe it's in 1.3?)
Update - I tried setting the field's default value in the MySql database, to no effect. It seems as if, even when the form does not have a value for the field, it goes ahead and assigns null to the DB, over-riding the MySql default value.
Although I am currently just setting a default value in the view that creates the form - I don't really like that, since it puts the responsibility for the field's integrity in the view, not the DB.
The way I would have thought it would work, is that the field could be set, or not, in the form - if set, that value would be written to the DB, and if not set, the DB default would be used. Instead, if not set, the form is writing a null to the DB. So what's the point of having a default value in the ModelField declaration if it's not used? What exactly does it do?
i you want field to be optional - just leave second definition in the model and do not add anything in the form definition:
class Invoice(models.Model):
expireDays = models.PositiveSmallIntegerField(default=1, blank=True, null=True)
class InvoiceForm(forms.ModelForm):
class Meta:
model = Invoice
update, so in case there is no value set, use 1 as the field value:
class InvoiceForm(forms.ModelForm):
def clean_expireDays(self):
exp_days = self.cleaned_data.get('expireDays')
if exp_days is None:
return self.fields['expireDays'].initial
# above can be: return 1
# but now it takes value from model definition
else:
return exp_days
class Meta:
model = Invoice
I have a form that contains a foreignkey field (Place). I've set up this field to use a custom form field type based off of CharField instead of ModelChoiceField as I want users to be able to use an autocomplete text input. The autocomplete is working, and if the instance of the foreignkey'ed model exists, I have no issues. However, if this instance of Place doesn't exist I want the user to be able to enter in the street address, town, state, and zip and use those to create a new Place using get_or_create in my custom field type. My code is like so:
class PlaceAutoCompleteField(forms.CharField):
def clean(self, value):
super(PlaceAutoCompleteField, self).clean(value)
place, created = Place.objects.get_or_create(name=value,
defaults={'street': self.form.cleaned_data['street'],
'town': self.form.cleaned_data['city'],
'state': self.form.cleaned_data['state'],
'zipcode': self.form.cleaned_data['zipcode']})
return place
class EventForm(forms.ModelForm):
place = PlaceAutoCompleteField()
street = forms.CharField(max_length=50)
city = forms.CharField(max_length=40)
state = USStateField()
zipcode = USZipCodeField()
class Meta:
model = Event
fields = (.....)
I get the error: 'PlaceAutoCompleteField' object has no attribute 'form'
I'm not surprised by this, but I'm unsure how to pass the form data into my custom field. Any ideas?
This kind of logic doesn't belong on the field - it belongs on the form, specifically in the form's clean method.
I want to make a form used to filter searches without any field being required. For example given this code:
models.py:
class Message(models.Model):
happened = models.DateTimeField()
filename = models.CharField(max_length=512, blank=True, null=True)
message = models.TextField(blank=True, null=True)
dest = models.CharField(max_length=512, blank=True, null=True)
fromhost = models.ForeignKey(Hosts, related_name='to hosts', blank=True, null=True)
TYPE_CHOICES = ( (u'Info', u'Info'), (u'Error', u'Error'), (u'File', u'File'), (u'BPS', u'BPS'),)
type = models.CharField(max_length=7, choices=TYPE_CHOICES)
job = models.ForeignKey(Jobs)
views.py:
WHEN_CHOICES = ( (u'', ''), (1, u'Today'), (2, u'Two days'), (3, u'Three Days'), (7, u'Week'),(31, u'Month'),)
class MessageSearch(ModelForm): #Class that makes a form from a model that can be customized by placing info above the class Meta
message = forms.CharField(max_length=25, required=False)
job = forms.CharField(max_length=25, required=False)
happened = forms.CharField(max_length=14, widget=forms.Select(choices=WHEN_CHOICES), required=False)
class Meta:
model = Message
That's the code I have now. As you can see it makes a form based on a model. I redefined message in the form because I'm using an icontains filter so I didn't need a giant text box. I redefined the date mostly because I didn't want to have to mess around with dates (I hate working with dates! Who doesnt?) And I changed the jobs field because otherwise I was getting a drop down list of existing jobs and I really wanted to be able to search by common words. So I was able to mark all of those as not required
The problem is it's marking all my other fields as required because in the model they're not allowed to be blank.
Now in the model they can't be blank. If they're blank then the data is bad and I don't want it in the DB. However the form is only a filter form on a page to display the data. I'm never going to save from that form so I don't care if fields are blank or not. So is there an easy way to make all fields as required=false while still using the class Meta: model = Message format in the form? It's really handy that I can make a form directly from a model.
Also this is my first serious attempt at a django app so if something is absurdly wrong please be kind :)
You can create a custom ModelForm that suit your needs. This custom ModelForm will override the save method and set all fields to be non-required:
from django.forms import ModelForm
class SearchForm(ModelForm):
def __init__(self, *args, **kwargs):
super(SearchForm, self).__init__(*args, **kwargs)
for key, field in self.fields.iteritems():
self.fields[key].required = False
So you could declare your forms by simply calling instead of the ModelForm, e.g.:
class MessageForm(SearchForm):
class Meta:
model = Message
You could also pass empty_permitted=True when you instantiate the form, e.g.,
form = MessageSearch(empty_permitted=True)
that way you can still have normal validation rules for when someone does enter data into the form.
I would give a try to the django-filter module :
http://django-filter.readthedocs.io/en/develop/
fields are not required. these are filters actually. It would look like this :
import django_filters
class MessageSearch(django_filters.FilterSet):
class Meta:
model = Message
fields = ['happened', 'filename', 'message', '...', ]
# django-filter has its own default widgets corresponding to the field
# type of the model, but you can tweak and subclass in a django way :
happened = django_filters.DateFromToRangeFilter()
mandatory, hidden filters can be defined if you want to narrow a list of model depending on something like user rights etc.
also : setup a filter on a 'reverse' relationship (the foreignkey is not in the filtered model : the model is referenced elsewhere in another table), is easy, just name the table where the foreign key of the filtered model field is :
# the 'tags' model has a fk like message = models.ForeignKey(Message...)
tags= django_filters.<some filter>(name='tags')
quick extendable and clean to setup.
please note I didn't wrote this module, I'm just very happy with it :)