Django - Modelform with override password field : initial value not visible (and not saved) in Based UpdateView - forms

I use a form for handling FTP/SFTP (files) accesses. They can be sometimes updated.
My password field is a Charfield (not required) in my model but I wan't to hide it on updates. I use Bcrypt when saving it.
For this I followed the example here for adding the PasswordInput() widget to my field. I use Crispy so my form in my template is just displayed with {% crispy form %}.
class FlowStockAutoForm(forms.ModelForm):
class Meta:
model = FlowStockAuto
exclude = ['fl_st_auto_report_cross_import', ]
fl_st_auto_pwd = forms.CharField(widget=forms.PasswordInput,
label="Password",
required=False, help_text="BlaBla")
def __init__(self, *args, **kwargs):
super(FlowStockAutoForm, self).__init__(*args, **kwargs)
pwd = self.instance.fl_st_auto_pwd
self.fields['fl_st_auto_pwd'].initial = pwd
In Django Doc, it's recommended to set an initial value with init (as done above) but when the password widget is active, on my update template, the field remains empty. So if I just wanna update another field, when I save, password is saved blank. It's ok when the Password Widget is off.
I would like it to be prefilled but "blurred" and check when saving if its value has changed.
What do I miss ?

Visibly almost duplicated question
had to change from
fl_st_auto_pwd = forms.CharField(widget=forms.PasswordInput,
label="Password",
required=False, help_text="BlaBla")
to
fl_st_auto_pwd = forms.CharField(
widget=forms.PasswordInput(render_value=True))
by adding render_value=True
I also had to remove
label="Password", required=False, help_text="BlaBla"
and replace it with
self.fields['fl_st_auto_pwd'].label = 'Password'
self.fields['fl_st_auto_pwd'].help_text = 'BlaBla'

Related

Limiting a Django form's ManyToManyField queryset in a formtools wizard based on selection on previous form

I'm using a SessionWizardView from django-formtools to construct a two-form wizard. The challenge I'm facing is that I need to reference the input from the first form to limit the available querysets on the second form.
To make it more interesting, I'm using crispy forms for layout and the queryset needs to be limited by a method on a related item.
Here's the (much simplified) gist of where I'm at:
Models
class Product(models.Model):
# pk, name, etc....
catalogitem = ForeignKey("myapp.CatalogItem")
colors = ManyToManyField("myapp.Colors")
class Colors(models.Model):
# pk, name, etc....
class CatalogItem(models.Model):
# Colors are stored within CatalogVariants, which I've left
# as a blackbox in this example, since they are retrieved as
# a queryset on this model with this method:
# pk, name, etc....
def get_colors(self):
# Returns a queryset of color objects.
Views
ProductFormWizard(SessionWizardView):
form_list = [
productFormWizard_Step1,
productFormWizard_Step2,
]
def get_context_data(self, **kwargs):
# ...
pass
def get_form_initial(self, step):
initial = {}
# ...
return self.initial_dict.get(step, initial)
def process_step(self, form):
if self.steps.step1 == 1:
pass
return self.get_form_step_data(form)
def done(self, form_list, **kwargs):
return render(self.request, 'done.html', {
'form_data': [form.cleaned_data for form in form_list],
})
Forms
productFormWizard_Step1(forms.ModelForm):
# Defines a form where the user selects a CatalogProduct.
model = Product
productFormWizard_Step2(forms.ModelForm):
"""
Defines a form where the user chooses colors based on
the CatalogProduct they selected in the previous step.
"""
model = Product
Based on research via the Googles and some SO questions (none of which were =directly= related), I'm assuming I need to set the .queryset property on the colors field, but I'm not exactly sure where to do that. Two thoughts:
I would guess it goes in .get_form_initial() somehow, but I'm at a loss as to the best way to achieve that.
Alternatively, the appropriate code might go into the productFormWizard.get_context_data() method somehow.
Within .get_form_initial(), I can do something like this:
if step == '1':
itemID = self.storage.get_step_data('0').data.get('0-pfProduct', "")
if itemID:
obj = CatalogItem.objects.get(id=itemID)
initial['colors'] = obj.get_get_colors()
However, this just selects the available related items... it doesn't limit the list.
Additional Info
Python == 3.5.3
Django == 1.10.6
django-crispy-forms == 1.6.1
django-formtools == 2.0
The solution is to override the .get_form() method on the View:
def get_form(self, step=None, data=None, files=None):
form = super(bzProductFormWizard, self).get_form(step, data, files)
if step == '1':
past_data = self.get_cleaned_data_for_step('0')
product = past_data['product']
form.fields['colors'].queryset = ... #CUSTOM QUERYSET
return form

Modifying individual field form inputs (CSS, Placeholder, etc) Drupal 7 registration form preprocess

I'm attempting to preprocess CSS and placeholder etc to the form inputs for the Registration Form. I'm manually rendering each field input on the registration form.
The following code works fine for email or password, but not 'custom fields'.
$form['account']['mail']['#attributes']['placeholder'] = t('Email address');
$form['account']['mail']['#attributes']['class'][] = 'email-input';
$form['account']['mail']['#title_display'] = "invisible";
$form['account']['mail']['#description'] = t('');
When I apply the same to the custom fields, it won't apply.
I have looked through the user module and printed avialable varibles
$form['field_first_name']['#attributes']['placeholder'] = t('First Name');
$form['field_first_name']['#attributes']['class'][] = 'first-name-input';
$form['field_first_name']['#title_display'] = "invisible";
$form['field_first_name']['#description'] = t('');
I found multiple mentions of field_first_name in the variable print out, but I'm unable to modify the text inputs directly.

Web2py SQLFORM.grid Edit page

I'm using SQLFORM.grid for my form. The view is showing up fine and i can hide fields. But when i go into the Edit page, i want to have certain fields not readable and not writeable. I have set it to be readable=False before the SQLFORM.grid call and also in the request.args == 'edit'. I can confirm it detects the edit page but for some reason, it doesn't run the read and write.
following is the controller:
def display_form():
db.items.timeStamp.readable = False
db.items.imageName.readable = False
db.items.isCopied.writeable = True
query = (db.items.numSold > 100)
default_sort_order = [db.items.numSold]
if len(request.args) > 1 and ('edit' in request.args):
db.items.timeStamp.readable = False
db.items.imageName.writeable = False
form = SQLFORM.grid(query=query, orderby=default_sort_order, create=False,
deletable=False, editable=True, maxtextlength=64, paginate=25, csv=False,
user_signature=False,
links=[dict(header=T('Profit'),body=lambda row: row.profit),
dict(header=T('Image'), body = lambda rowB: A(IMG(_src=URL('static', "images/"+
rowB.imageName.replace('\\','/')), _width=50, _height=50),
_href=URL('static', "images/"+ rowB.imageName.replace('\\','/'))))],
selectable=get_chosenItems
)
else:
form = SQLFORM.grid(query=query, orderby=default_sort_order, create=False,
deletable=False, editable=True, maxtextlength=64, paginate=25, csv=False,
user_signature=False,
links=[dict(header=T('Profit'),body=lambda row: row.profit),
dict(header=T('Image'), body = lambda rowB: A(IMG(_src=URL('static', "images/"+
rowB.imageName.replace('\\','/')), _width=50, _height=50),
_href=URL('static', "images/"+ rowB.imageName.replace('\\','/'))))],
selectable=get_chosenItems
)
return dict(form=form)
I can tell that when on the edit page, it would go in the if statement, however, it totally ignores the readables i set there.
Should i be calling the form once again within the edit page?
I feel that it's redundant.
To prevent fields from being included on edit forms, set both the readable and writable attributes to False. If you set writable to False but leave readable as True, then you'll see a read-only value for the field on the form.

On a HTML Edit form, what is a good approach to have both reset and remember the posted values features?

I have a form which has both server side and client side validation.
It is an edit form, so the original user values are originally pre-populated.
e.g. The original pre-populated values are:
username = yeo
surname = yang
phonenumber = 11-12345
Now, the user edits to the below and submits.
e.g. The edited submitted values are:
username = yeoNew
surname = yangNew
phonenumber = 12-1111
This gets submitted to the serverside and fails the serverside validation because the phonenumber starting with 12 is not allowed.
Anyway, so the form is displayed back to the user as
e.g. The redisplayed form values are:
username = yeoNew
surname = yangNew
phonenumber = 12-1111
This is because my form allows the user to remember their submitted values.
At this stage, I'd like to allow the user to have the ability to reset the form values to the original values using clientside javascript. This is like a reset feature.
e.g. The reset button will restore the form values to:
username = yeo
surname = yang
phonenumber = 11-12345
The reason for this reset feature is that I want the user to have the option to edit the phonenumber again from the original values.
My question is:
What is a good way to keep track of the original values within the HTML so that I can restore it with javascript?
I'm thinking a new attribute called orig='' within the form elements which will store this value.
Is that a good idea?
Any other approaches?
thanks
I would use the HTML5 local storage.
See http://www.w3schools.com/html/html5_webstorage.asp
Using jquery I would do it this way:
<script type="text/javascript">
function load() {
if (localStorage["username"]) {
$('#username').val(localStorage["username"]);
}
if (localStorage["surname"]) {
$('#surname').val(localStorage["surname"]);
}
if (localStorage["phone"]) {
$('#phone').val(localStorage["phone"]);
}
}
function save() {
localStorage["username"] = $('#username ').val();
localStorage["surname"] = $('#surname').val();
localStorage["phone"] = $('#phone').val();
}
</script>

How to populate zend form field using session?

I am using sessions to populate a multi select box with options in my Zend application.
The user selects one or more options and fills in other fields on the form and then submits. If the user didn't select all of the options in the multi select then the form is displayed again but the multi select only has the options that the user did not select the last time. This process goes on until there are no more options from the multi select left to process.
Here is the code I use to get rid of the options that have already been processed so that they are not used to populate the multi select box:
if($form_successful){
// TODO remove $post['keyword_names'] (i.e. already processed) from $keyword_names (that come from $_SESSION)
$keyword_names = array_diff($keyword_names, $post['keyword_names']);
print_r($keyword_names);
if(is_array($keyword_names) && !empty($keyword_names)){
// save updated $keyword_names into $_SESSION['workflow1']
$session = new Zend_Session_Namespace('workflow1');
$session->keyword_names = $keyword_names;
// set flag to false so that we display form again
$form_successful = false;
}else{ // all keywords have been assigned
// go to next step
$this->_redirect('/workflow-1/step-'.($step+1).'/');
}
}
print_r($keyword_names); displays the correct options, however when the form is loaded when the user submits, the multi select displays the options that were there from the begining ie the options the user has just selected and submitted are not being taken out of the multi select, it is only when the user submits the form again then the multi select box updates.
Appreciate the help.
Solved the issue by making use of URL parameters. Here is the code (might differ a lot from what I posted first because some big changes were made):
// after successful form submission
if($form_successful){
// remove $post['keyword_names'] (i.e. already processed) from $keyword_names (that come from $_SESSION)
$keyword_names = array_diff($keyword_names, $post['keyword_names']);
// save remaining $keyword_names into $_SESSION['workflow1']
$session = new Zend_Session_Namespace('workflow1');
$session->keyword_names = $keyword_names;
if(is_array($keyword_names) && !empty($keyword_names)){
// redirect to the same step again - to ensure that the form will reflect (in select lists) newly created AdGroup and/or Campaign
// GET parameteres ($params_array) provide a way to remember user's choice
$params_array = array();
if(!empty($post['match_type_id'])){
$params_array['match_type_id'] = $post['match_type_id'];
}
if(!empty($post['with_permutations'])){
$params_array['with_permutations'] = $post['with_permutations'];
}
if(!empty($ad_group_id)){
$params_array['ad_group_id'] = $ad_group_id;
}
$this_step_url = UrlUtils::assemble('', $this->getRequest()->getActionName(), $this->getRequest()->getControllerName(), $this->getRequest()->getModuleName(), $params_array);
$this->_redirect($this_step_url);
}else{ // all keywords have been assigned
// go to next step
$this->_redirect('/workflow-1/step-'.($step+1).'/');
}
}
So you don't have any code about Zend_Form object here. How do you populate the form element? If you post your class code which extends Zend_Form (or any other code dials with your form) then I may help. But in any case you can populate your multiselectbox with setMultiOptions() method or addMultiOption() for each item in multiselectbox.