Previsouly, I used forms.ModelForm to create a Django form and in views.py used:
p_form = ProfileUpdateForm(request.POST,
request.FILES,
instance=request.user.profile)
This way, if the form was not valid when submitted, it would repopulate the information the user entered previously. This saved a user from having to enter the information again.
I have since changed to forms.Form because I can't figure out how to customize Crispy Forms using forms.ModelForm, but now if I call instance=request.user.profile, I get an error.
What can I do to repopulate the information the user previously placed in the form so they do not have to do it again?
You can populate the fields of a form with the initial=… parameter [Django-doc], this then contains a dictionary that maps the fields to the corresponding value.
So if your form for example is defined as:
class ProfileUpdateForm(forms.Form):
name = forms.CharField()
age = forms.IntegerField()
you can construct a form with:
ProfileUpdateForm(initial={'name': 'Jason G', 'age': 25})
A ModelForm basically does the same, except that here it obtains the initial values form the instance=… parameter, and thus obtains attributes from that object and passes these to the corresponding form fields.
That being said, normally crispy forms work quite well with ModelForms, so perhaps it is better to take a look what the problem is with your ModelForms and try to fix this over "circumventing" the problem, but making it harder to work with your forms.
Related
I need to know why may I need to use instance into ModelForm I read about it in Django document what I understand that it can replace with save() method somehow if that is correct why I have to use it if not how can I use it and why?
Suppose you have a record in your models whose pk=1. So first you fetch the instance, and then you can create a form in your views by passing the instance argument. So if changes are made to some fields, the same record will be updated.
In your views -
a = ModelName.objects.get(pk=1) //Fetching the record you want to update
form = ModelFormName((request.POST, instance=a) or None)
if form.is_valid():
record = form.save(commit=False)
//Modify the records fields which you get from form
record.save()
// if not valid send it to template via context
Use this form in your templates as you would do in a normal form.
Help needed with pre-populating Django forms: I have a form that updates a UserProfile model, when the form is loaded I want it pre-populated with the existing UserProfile data. Simple enough, the instance can be passed to the form. However, it seems that fields with choices (which come out as select drop-down elements in HTML) are not pre-populated with the instance data and instead default to '-----------'.
I've tried manually setting the initial value for a specific form (e.g. country) but it doesn't pull through to the HTML.
form = UserProfileForm(instance=user_profile)
form.fields['country'].initial = 'GBR'
I'm sure I could create a convoluted work around to get the current country selected in the front-end but it feels like it should be possible in Django. I couldn't see any solutions in other questions.
You can dynamically set default for a form field as:
form = UserProfileForm(instance=user_profile, initial={'country': 'GBR'})
If country field should be populated from user_profile data, then you can define form as following (assuming country is a field in UserProfile class)
class UserProfileForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(UserProfileForm, self).__init__(*args, **kwargs)
if self.instance:
self.initial['country'] = instance.country
Why what you are doing isn't working?
You are using:
form.fields['country'].initial = 'GBR'
This will not work on for a bounded form. form = UserProfileForm(instance=user_profile) will return a bounded form.
From docs:
The initial argument lets you specify the initial value to use when rendering this Field in an unbound Form.
To specify dynamic initial data, see the Form.initial parameter.
I'm trying to set up a django form consisting solely of a formset. In forms.py I have:
class StudentEnrolmentForm(forms.ModelForm):
school_class = forms.ModelChoiceField(SchoolClass.objects.currently_enrolling())
class Meta:
model = StudentApplication
fields = []
StudentEnrolmentFormSet = modelformset_factory(StudentApplication, StudentEnrolmentForm, extra=0)
but I'm unclear how to incorporate the FormSet into a CBV (In this case I've chosen a FormView). In this case I'm basically displaying a table of students, and allowing the operator to assign each student to a class. I only want a single 'submit' button at the end of the page.
If you will take a look on sources of Django views and check how FormView is working, you find that it just overrides default get and post methods of base View class and adds some additional methods for the form handling.
So you can:
try to assign your formset to the form_class field of your view and
play around. Probably you will have to override some additional
methods;
take a look on https://github.com/AndrewIngram/django-extra-views;
if options #1 and #2 causes too much pain - use default View
I'm currently work on some project in pyramid and have problem with wtforms SelectField.
I have a 3 SelectField fields:
car_make (e.g., "audi")
car_model (e.g., "audi 80")
car_version (e.g., "AUDI 80 B4").
The car_make choices I can load in the view. The choices for rest of SelectFields (car_model, car_version) I will load on the client side via AJAX/javascript (I can choose car_model when car_make is selected and so on).
The problem is that when I submit the form, car_model and car_version raise 'Not valid choice' because (in SelectField.pre_validation line 431) self.choices is empty.
How can I get around this problem?
What you are asking to do is have WTForms handle "cascading selects" (having the valid fields of one choice be determined by the value of another field). There really isn't a good way using the built in fields.
The SelectField in WTForms does NOT provide you with an option to say "Don't validate that the choice supplied is valid". You MUST provide choices in order for the field to validate the choice.
As shown in the docs, while you typically could fill the choices field with a static list of choices...
class PastebinEntry(Form):
language = SelectField(u'Programming Language', choices=[('cpp', 'C++'), ('py', 'Python'), ('text', 'Plain Text')])
...however, since you are dynamically coming up with the options, you need to set the choices attribute after instantiating the form.
def edit_user(request, id):
user = User.query.get(id)
form = UserDetails(request.POST, obj=user)
form.group_id.choices = [(g.id, g.name) for g in Group.query.order_by('name')]
In the above sample, the choices for "group_id" is filled dynamically in what would be your Pyramid view. So, that's what you would need to do: you need to fill the choices in your view. This is how you can fix your issue with car_make (although I think in your question you said that car_make was okay).
The problem that you have, however, is that the valid choices for car_model cannot be determined, since they depend on car_make having already been parsed and validated. WTForms doesn't really handle this well (at least with SelectFields) because it assumes that all of the fields should be validated at once. In other words, in order to generate the list of valid choices for car_model, you first need to validate the value for car_make, which is not possible to easily do given how the SelectField works.
The best way I see doing this is to create a new field type that extends the SelectField type, but removes the validation:
class NonValidatingSelectField(SelectField):
def pre_validate(self, form):
pass
This new type overrides pre_validate, which typically does the check to determine if a choice is valid.
If you use this for car_model, you won't have the error anymore. However, this now means that your field isn't actually being validated! To fix this, you can add an in-line validator on your form...
class MyForm(Form):
car_make = SelectField(u'Make', choices=[...])
car_model = NonValidatingSelectField(u'Model', choices=[])
def validate_car_model(self, field):
choices = query_for_valid_models(self.car_make.data)
# check that field.data is in choices...
You might need to tweak this a bit to get it to work exactly how you want, I haven't actually tested that this works.
This should be pretty straightforward but I'm not figuring it out from the Django documentation. I have an IntegerField Model Field from which I've created a ModelForm Field. The field in question contains $ values (price) and it would be preferable from a UX standpoint if the user did not receive a error message ('enter whole number') when they input $10 instead of 10.
I've tried manual form cleaning but it seems that the clean_field method is run after other validation methods. My reading so far seems to confirm that as well.
def clean_bill(self):
bill = self.cleaned_data["bill"]
if '$' in bill:
bill=bill.replace('$','',1)
return bill
Is there a way around this while maintaining the IntegerField in the modelform? Should I just make it a RegexField?
EDIT: I ended up going with a combination of the above and a RegexField. Still curious if there is another/better way.
bill= forms.RegexField(label=_("Bill"), max_length=10,required=True, regex=r'^\$?[\d]+$',
error_messages = {'invalid': _("Please enter a whole number.")})
Create an IntegerField in the model, and a CharField in the form.
You can still use modelform to do this but you'll have to:
'exclude' the model field from the form,
write a clean_bill method in the form
set the value of the model's field to the parsed integer value
try this and/or this
Yes, you are right. According to the Django docs of form and field validation it will not even go to your clean method and already raise ValidationError in the first step (to_python method).
There is nothing much you can do on the form level I think. What you could do though is processing the POST data in the view before passing it to the form. Some simple example:
post_dict = request.POST.copy() #makes a copy of request.POST so you can modify it
bill = post_dict['bill']
if '$' in bill:
bill = bill.replace('$','',1)
post_dict['bill'] = bill
# pass post_dict to your form instead of request.POST
...