I have a model that looks something like:
class DooDad(models.Model):
doo_dad_dogue = models.BooleanField(default=True)
Trouble is, that default needs to be manipulated by... stuff that is irrelevant to this question. If I were creating the form that creates the object, the solution would be trivial. I'm still using the django default form for creating these things, though, and I'd rather keep it that way.
I tried the obvious:
class DooDad(models.Model):
doo_dad_dogue = models.BooleanField(default=True)
def __init__(self, *args, **kwargs):
super(DooDad, self).__init__(*args, **kwargs)
self.doo_dad_dogue = False
...which I suspect would have terrible side effects, but was worth experimenting with. The form still comes up with the box checked.
EDIT: I should have mentioned that this is Django 1.9
If it is not possible to continue using the default model creation form, is there anything unusual that I need to do to to make a ModelForm that only impacts CREATE, and not EDIT?
I do not think using the __init__in model is a good practice. However, if you want to try it is important to know that your code is not correct one the field doo_dad_dogue is a descriptor. The correct way to access it is
using self.fields['doo_dad_dogue'] = False.
Using a form is the correct way to do that. You can override the default value in the Form by using the init method:
def __init__(self, *args, **kwargs):
super(<YOUR_FORM>, self).__init__(*args, **kwargs)
if args is not None and len(args) and args[0] is not None:
<MANIPULATE HERE>
Hope that helps.
Related
After two years of experience with Django forms, I ran into the following dilemma related to __init__ method:
I have a Django form definition as follows:
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
super(MyForm, self).__init__(*args, **kwargs)
if not MyModel.objects.filter(user = self.user).exists():
self.fields['field_1'].widget = forms.HiddenInput()
self.fields['field_2'].widget.attrs['placeholder'] = 'Enter value'
This code is ok if I initialize the form like this:
my_form = MyForm()
However, the problem arises when I try to save user input in the following way:
my_form = MyForm(request.POST)
My point is that I do not want to waste code execution time for setting placeholder property or deciding upon whether some field should be hidden or not AFTER the user has already submitted form.
My concern is that maybe that's because I misuse __init__ method?
Is there any way to check whether request.POST parameter has been provided? And if yes, is it considered best-practice to perform this check and do thinks like settings placeholder, initial values, etc. only if request.POST is not provided?
You can check self.is_bound; it's only true if data is passed to the form.
However, I really think you're over-optimising here. This will only have a tiny impact on the performance of the code.
I have a model with a custom save function, where I want to perform certain functions based on a condition like this:
class ClassName(models.Model):
def save(self, *args, **kwargs):
reindex = **kwargs.pop("reindex")
super().save(*args, **kwargs)
if reindex:
People.objects.create()
Now inside a task I want to call the following:
kwargs = { "reindex": False}
ClassName.objects.get_or_create(**kwargs)
When it does a create, it obviously runs the save function, but it's giving me an error saying reindex is not a field.
I have been researching it for a while now and can't figure out what to do.
Maybe someone can point me in the right direction.
I just want to pass in an argument into the get_or_create, so that I can conditionally perform a certain function in the save method.
When you do
kwargs = { "reindex": False}
ClassName.objects.get_or_create(**kwargs)
it is actually equivalent to
ClassName.objects.get_or_create(reindex=False)
Thus, since reindex appears not to be a field defined in the model ClassName, you get an error.
BTW, beyond things which appear erroneous, e.g. reindex = **kwargs.pop("reindex"), you should define reindex as one of the fields of your model. But I admit that I answer blindly, because to me, your class definition cannot work like so. If one assumes that reindex is an integer field, you could do
class ClassName(models.Model):
reindex = models.IntegerField(null=True)
def save(self, *args, **kwargs):
super(ClassName, self).save(*args, **kwargs)
if "reindex" in kwargs:
People.objects.create()
I have a model with a customized save() method that creates intermediate models if the conditions match:
class Person(models.Model):
integervalue = models.PositiveIntegerField(...)
some_field = models.CharField(...)
related_objects = models.ManyToManyField('OtherModel', through='IntermediaryModel')
...
def save(self, *args, **kwargs):
if self.pk is None: # if a new object is being created - then
super(Person, self).save(*args, **kwargs) # save instance first to obtain PK for later
if self.some_field == 'Foo':
for otherModelInstance in OtherModel.objects.all(): # creates instances of intermediate model objects for all OtherModels
new_Intermediary_Model_instance = IntermediaryModel.objects.create(person = self, other = otherModelInstance)
super(Person, self).save(*args, **kwargs) #should be called upon exiting the cycle
However, if editing an existing Person both through shell and through admin interface - if I alter integervalue of some existing Person - the changes are not saved. As if for some reason last super(...).save() is not called.
However, if I were to add else block to the outer if, like:
if self.pk is None:
...
else:
super(Person, self).save(*args, **kwargs)
the save() would work as expected for existing objects - changed integervalue is saved in database.
Am I missing something, or this the correct behavior? Is "self.pk is None" indeed a valid indicator that object is just being created in Django?
P.S. I am currently rewriting this into signals, though this behavior still puzzles me.
If your pk is None, super's save() is called twice, which I think is not you expect. Try these changes:
class Person(models.Model):
def save(self, *args, **kwargs):
is_created = True if not self.pk else False
super(Person, self).save(*args, **kwargs)
if is_created and self.some_field == 'Foo':
for otherModelInstance in OtherModel.objects.all():
new_Intermediary_Model_instance = IntermediaryModel.objects.create(person = self, other = otherModelInstance)
It's not such a good idea to override save() method. Django is doing a lot of stuff behind the scene to make sure that model objects are saved as they expected. If you do it in incorrectly it would yield bizarre behavior and hard to debug.
Please check django signals, it's convenient way to access your model object information and status. They provide useful parameters like instance, created and updated_fields to fit specially your need to check the object.
Thanks everyone for your answers - after careful examination I may safely conclude that I tripped over my own two feet.
After careful examination and even a trip with pdb, I found that the original code had mixed indentation - \t instead of \s{4} before the last super().save().
As you can see in the code sample below, I'm trying to add that multiple choice field from my constructor (instead of doing it like in the commented line) but it doesn't seem to work, doesn't matter if it's before or after the call of super().
Any advices on how i can add that attribute from my constructor?
class PageForm(forms.Form):
# answers = forms.ModelMultipleChoiceField(Answer.objects.all())
def __init__(self, *args, **kwargs):
self.answers = forms.ModelMultipleChoiceField(Answer.objects.all())
super(forms.Form, self).__init__(*args, **kwargs)
self.answers = forms.ModelMultipleChoiceField(Answer.objects.all())
P.S. I know it might be irrelevant for this example, but I need this thing for a more complex thing :D
Fields need to be added after super. Instead self.answers, try self.fields['answers']
I have a form where I am overriding the init so I can set some checkboxes to be disabled, these disabled checkboxes are pre-checked.
class HomePhoneBundleOrderForm(forms.Form):
def __init__(self, *args, **kwargs):
super(HomePhoneBundleOrderForm, self).__init__(*args, **kwargs)
self.fields['telephone_line'].widget.attrs['disabled'] = 'disabled'
self.fields['voice_mail'].widget.attrs['disabled'] = 'disabled'
telephone_line = forms.BooleanField(initial=True, help_text="Local telephone line included.")
voice_mail = forms.BooleanField(label="", help_text="Voicemail – included in this bundle.", initial=True)
The problem is when I submit this form, even when they are pre-checked, the form gives me an error saying that the field is required and then the checkbox becomes unchecked. Can you anyone give me some help as to why and how to fix this?
Thanks
-J
Here's another option that will work for both single Forms as well as Formsets. #sandinymyjoints links to a good post and the accepted answer there is certainly well thought out. But hacking Request parameters can lead to a lot of trouble as a general rule. As a specific case, hacking the POST of a formset submission involves regex and that's really ugly. In a Formset (remember that they are collections of Forms), your forms will POST as form-0-telephone_line and form-0-voice_mail and such. Try this instead.
In your Form class define two fields - a visible one to look good and a hidden one to carry the data. The visible checkbox will be disabled and thus will never POST (as you well know), but it will give clear visual indication to the user what is happening. The hidden checkbox will hold your initial True checked status.
class HomePhoneBundleOrderForm(forms.Form):
def __init__(self, *args, **kwargs):
super(HomePhoneBundleOrderForm, self).__init__(*args, **kwargs)
self.fields['telephone_line_visible'].widget.attrs['disabled'] = 'disabled'
telephone_line = forms.BooleanField(initial=True,
widget=forms.HiddenInput)
telephone_line_visible = forms.BooleanField(initial=True,
required=False,
label="Telephone line",
help_text="Local telephone line included.")
Or if you want to keep the "hack" code entirely in one location:
def __init__(self, *args, **kwargs):
super(HomePhoneBundleOrderForm, self).__init__(*args, **kwargs)
# Hack
self.fields['telephone_line'].widget=forms.HiddenInput()
self.fields['telephone_line_visible'] = forms.BooleanField(initial=True,
required=False,
label="Telephone line",
help_text="Local telephone line included.")
self.fields['telephone_line_visible'].widget.attrs['disabled'] = 'disabled'
I think you are having the same problem as this: disabled field is not passed through - workaround needed
"Since you are disabling the widget and not the field, as far as the form is concerned it's always receiving nothing for fieldA and that will always fail validation."
See the accepted answer to that question for some possible solutions. Looks like the best one involves modifying POST to put the value you want into the fields.
You could try enabling them on the client side before submitting with javascript/jQuery, something similar to this:
//enable disabled fields so they would get submitted with the request
$('#form').submit(function() {
$('input,select', this).prop('disabled', '');
return true;
});