Prepopulate non-model form attribute when instantiating in Django - django

I want to set a minimum value for a DecimalField attribute on a form at the time when I instantiate it in the view. And I want to get that minimum value from an object I gather from the database. I made it (sort of) work by manually putting in the html form in the template, but want to refactor it to use the form class because I can do more useful things with data in the view than I can in the template.
Based on my reading of other questions and docs, I can't set attributes with the .initial argument. I thought likely I need to override the __init__ method on the form, but I'm pretty sure I'm not doing this right and it makes no sense syntactically. Here's what I have tried:
class BidForm(forms.Form):
bid = forms.DecimalField(decimal_places=2)
listing_bid = forms.CharField(widget=forms.HiddenInput())
def __init__(self, min_bid, listing_pk, *args, **kwargs):
super(BidForm, self).__init__(*args, **kwargs)
self.fields['bid'].min = min_bid
self.fields['listing_bid'] = listing_pk
My idea is to have this form take a min_bid and a listing_pk, and fill the "min" attribute on the html input with whatever is in the min_bid variable. I want to put the listing_pk that's passed in as the value in a hidden field called "listing_bid". If it helps clarify, I'm trying to generate html equivalent to:
<input type="number" name="bid" min="{{ listing.current_bid }}">
<input type="hidden" name="listing_bid" value="{{ listing.pk }}">
In the view, I'd like to say something like:
form = BidForm(min_bid=foo, listing_bid=bar)
Then pass that into the template context for rendering.
Does this make sense? I've found some discussion of it in the context of ModelForms but can't wrap my head around how to do this with regular forms.
Edit: for future reference, here is what worked for me. I deleted the bid attribute on the form because there's no case where I would want to init it without supplying a min_bid:
class BidForm(forms.Form):
listing_bid = forms.CharField(widget=forms.HiddenInput())
def __init__(self, min_bid, listing_pk, *args, **kwargs):
super(BidForm, self).__init__(*args, **kwargs)
self.fields['bid'] = forms.DecimalField(decimal_places=2, min_value=min_bid)
self.fields['listing_bid'].initial = listing_pk

You can make use of .initial attribute:
class BidForm(forms.Form):
bid = forms.DecimalField(decimal_places=2)
listing_bid = forms.CharField(widget=forms.HiddenInput())
def __init__(self, min_bid, listing_pk, *args, **kwargs):
super(BidForm, self).__init__(*args, **kwargs)
self.fields['bid'].min_value = min_bid
self.fields['listing_bid'].intial = listing_pk

Related

how can I prevent users form extending/ minimizing TextField Django

How can I prevent this from happening
A Django TextField is rendered as a HTML textarea.
Looking at this question, you could use style="resize: none;".
If you would like to add that in your views/form (and not in the templates), you could try something like:
class MyModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['my_field_name'].widget.attrs['style'] = 'resize: none;'
or if you have a form instance
form.fields['my_field_name'].widget.attrs['style'] = 'resize: none;'

Setting HTML required attribute in Django formsets

To achieve client-side validation making the user to fill out non-null fields before submitting, I use the following code:
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
for field_name, field in self.fields.items():
field.widget.attrs['class'] = 'form-control'
if field.required == True:
field.widget.attrs['required'] = ''
This translates to the following html in the template:
<input class="form-control" ........ required="">
Now, when I use formsets, the required HTML attribute does not appear in the tempalte. The question is, how do I make Django formsets inherit this required attribute from the original forms - if it's possible whatsoever?
MyFormSet = modelformset_factory(MyModel, fields=(...))
formset = MyFormSet(queryset = MyModel.objects.filter(...))
How about creating formset from MyForm?
MyFormSet = forms.formset_factory(MyForm)
After spending three hours, I've solved the issue by setting a custom form in modelformset_factory. Maybe it will be useful for someone else
MyFormSet = modelformset_factory(MyModel, MyForm)
formset = MyFormSet(queryset = MyModel.objects.filter(...))
Specifying MyForm effectively tells Django to inherit all widget attributes that you have once declared in the MyForm definition.
Using formset_factory is for some reasons a headache for me, primarily because it accepts values instead of querysets which means I have to bother about foreign key relationships.

Crispy Forms Initial data

I'm confused on whether I can set initial data to a field with crispy forms. I have one field in which I generate a number and want to set that to a specific field. If I do anything like:
def __init__(self, *args, **kwargs):
self.fields['medical_record_number'].initial = 'whatever you want'
I get a error stating that name 'self' is not defined.
I am assuming there is something different I have to do while using crispy forms to accomplish the same thing?
Thanks,
Tom
Nothing to do with crispy-forms, this is regular Django forms code. You have forgotten to call parent constructor, if you are using python 2.X do:
def __init__(self, *args, **kwargs):
super(YourFormClassName, self).__init__(*args, **kwargs)
self.fields['medical_record_number'].initial = 'whatever you want'
Beware that maybe you prefer to set initial in your Django form field:
whatever = forms.CharField(initial="whatever you want")

Using self.id to populate other fields in Django

I'm trying to populate a field called 'identification' using the primary key 'id'. However, as you know, there is no way to know what 'id' an objects is going to be before it has been saved. Therefore, stubbornly I did this:
def save(self, *args, **kwargs):
super(Notifications, self).save(*args, **kwargs)
self.identification = str(self.id)
Amusingly, this works in console:
>>>new = Notifications.objects.create( # some fields to be filled )
>>>new.identification
'3' (# or whatever number)
but when I go to my template to retrieve this object:
{% for each in notifications %}
Identification: {{ each.identification }}
{% endfor %}
reality strikes:
Identification:
What is happening? Why is it working in console but not in a template?. What approach do you suggest to use an auto-populated field in other fields?.
Thanks a lot!
The problem is that you're not saving the changes to your database.
It works in the terminal because that particular model instance (the python object - very much temporary) has the property identification filled. When you access it in a view or template, the save() method has not been called so the property / field is blank.
To make it work, call save again after your first save. Also, it might make sense to set the id only on model creation. One extra call per initial save isn't so big of a deal in most cases.
def save(self, *args, **kwargs):
add = not self.pk
super(MyModel, self).save(*args, **kwargs)
if add:
self.identification = str(self.id)
kwargs['force_insert'] = False # create() uses this, which causes error.
super(MyModel, self).save(*args, **kwargs)

Django send key or value from the view to the form class

I am writing an Edit form, where some fields already contain data. Example:
class EditForm(forms.Form):
name = forms.CharField(label='Name',
widget=forms.TextInput(),
initial=Client.objects.get(pk=??????)) #how to get the id?
What I did for another form was the following (which does not work for the case of the previous EditForm):
class AddressForm(forms.Form):
address = forms.CharField(...)
def set_id(self, c_id):
self.c_id = c_id
def clean_address(self):
# i am able to use self.c_id here
views.py
form = AddressForm()
form.set_id(request.user.get_profile().id) # which works in the case of AddressForm
So what is the best way to pass an id or a value to the form, and that could be used in all forms for that session/user?
Second: is it right to use initial to fill in the form field the way I am trying to do it?
You need to override the __init__ method for your form, like so:
def __init__(self, *args, **kwargs):
try:
profile = kwargs.pop('profile')
except KeyError:
super(SelectForm, self).__init__(*args, **kwargs)
return
super(SelectForm, self).__init__(*args, **kwargs)
self.fields['people'].queryset = profile.people().order_by('name')
and, obviously, build your form passing the right parameter when needed :)