I am working with flask and need django form like class so that in flask view i can simply instantiate a class and check its validity.Something like this.
class StringField(object):
def __init__(self, value=None, null=False):
self.value = value.strip() if value is not None else value
self.nullable = nullable
def clean(self):
if self.null:
if self.value in ('', None):
return self.value
else:
if self.value in ('', None):
raise Exception(
"Value can not be null or blank"
)
try:
self.value = str(self.value)
except:
raise Exception(
"Value is neithe string nor can be coerced into one"
)
class MyForm(Form):
username = StringField(null=True)
in my views i want do this
mf = MyForm(data_dict)
if mf.is_valid():
# do something...
Problem is:
how to get all fields like username, email etc in constructor of our main Form class (one which gets inherited), so that i can apply some validation to its attributes as these fields can be variable in number
Django's docs contains a lot of information regarding forms, start here.
For example:
from django import forms
# your form:
class UserForm(forms.Form):
username = forms.CharField(max_length=100)
email = forms.EmailField(null=True, blank=True)
# and your view:
def user_view(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = Userorm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = UserForm()
return render(request, 'user.html', {'form': form})
See the link above for more info.
Related
I have a model in my application:
models.py:
class bdAccesorios(models.Model):
fdClienteAcc=models.CharField(max_length=35)
fdProveedorAcc=models.CharField(max_length=60)
fdSkuAcc=models.CharField(max_length=30)
fdNombreAcc=models.CharField(max_length=60)
fdCostoAcc=models.DecimalField(max_digits=8, decimal_places=2)
fdUnidadAcc=models.CharField(max_length=30)
fdExistenciaAcc=models.IntegerField()
fdAuxAcc=models.CharField(max_length=60, default="0")
Then, I have a form to add new entries to the model
class fmAccesorios(ModelForm):
class Meta:
model=bdAccesorios
fields='__all__'
What I can't accomplish is that the form starts with an initial value, so far what I have done in my views is this, but the field shows blank
views.py
def vwCrearAccesorio(request):
vrCrearAcc=fmAccesorios(initial={'fdClienteAcc':"foo"}) ###Here is the problem ###
if request.method == "POST":
vrCrearAcc=fmAccesorios(request.POST)
if vrCrearAcc.is_valid():
vrCrearAcc.save()
return redirect('/')
else:
vrCrearAcc=fmAccesorios()
return render(request,"MyApp/CrearAccesorio.html",{
"dtCrearAcc":vrCrearAcc
})
MORE INFO:
I know that I can use the following code in my form to set initial values
def __init__(self, *args, **kwargs):
super(fmAccesorios, self).__init__(*args, **kwargs)
self.fields['fdClienteAcc'].disabled = True
self.fields['fdClienteAcc'].initial = "foo"
But I can't use that, because I need the variable "foo" to change dynamically, my ultimate goal is to use
the request.user.username variable and then use that variable to get another value from another model
In your view you have to pass the current instance you need to the form like this:
def vwCrearAccesorio(request):
vrCrearAcc=fmAccesorios(initial={'fdClienteAcc':"foo"}) # this will not be used because you reassign `vrCrearAcc` later
if request.method == "POST":
vrCrearAcc=fmAccesorios(request.POST, initial={'fdClienteAcc':"foo"}) # pass it here
if vrCrearAcc.is_valid():
vrCrearAcc.save()
return redirect('/')
else:
vrCrearAcc=fmAccesorios(initial={'fdClienteAcc':"foo"}) # and here
return render(request,"MyApp/CrearAccesorio.html",{
"dtCrearAcc":vrCrearAcc
})
I am trying to manually change the name of a form field after it initialized in a CBV. You could do this after initializing:
form = self.get_form() # it is initialized here
form.cleaned_data['name'] = form.instance.name + ' (new)'
But I need to change the value after validating, so form.cleaned_data is no longer involved. I need to directly change the value of the BoundField. How can I do that? Or how can I do what I am trying to do in any other way?
Here is some cody of my view and form class:
class MyView(CBV):
def form_valid(self, form):
if copy:
form.instance.pk = None
name = form.instance.name + ' (new)'
form.instance.name = name
# it does work this way
data = form.data.copy()
data['name'] = name
form.data = data
self.object = form.save()
else:
pass
return self.render_to_response(self.get_context_data(form=form))
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
exclude = []
def __init__(self, *args, **kwargs):
self.copy = kwargs.get('copy', False)
super(MyForm, self).__init__(*args, **kwargs)
def clean_name(self):
# (1) doing this has no effect in the displayed form included in the response of a POST request
# (2) I don't always want to do this, only when a form is valid
cleaned_data = self.cleaned_data['name']
if self.copy:
cleaned_data['name'] += ' (new)'
return cleaned_data
You can override clean method. After inherit form, you can modify your cleaned_data after validation.
def clean(self)
# It will validate data
self.cleaned_data = super().clean()
# do what you want
self.cleaned_data['name'] = 'blah...'
return self.cleaned_data
Or you can use specific field, in this situcation, name.
def clean_name(self):
data = self.cleaned_data.get('name', '')
if not data:
raise forms.ValidationError("You must enter a name")
if not data.endswith('( new)'):
return data += '( new)'
return data
ADD
You can see the validation actually already DONE after super().clean().
You can check print self.is_valid() then you can see it's True. Or just add debugger in that line to check validation is already done
def clean(self):
# It will validate data
self.cleaned_data = super().clean()
# Check validation is already done here
# Or debugging it
# from IPython import embed; embed()
print(self.is_valid())
# do what you want
return self.cleaned_data
You should save the form first, with commit=False, and then change it in the instance.
def form_valid(self, form):
if copy:
instance = form.save(commit=False)
instance.pk = None
name = instance.name + ' (new)'
instance.save()
return...
(Also, you should always redirect after a successful post, not render a template.)
So basic task is: add the template name and text to the choices in the ChoiceField with Selector widget to the form using the data from dB linked to the authenticated User only. Template is a model linked to user as a ForeignKey.
I'd like to access the request data(user) via the Form class linked to the connected view as django.views.generic.View class.
I've checked similar questions here:
Curious about get_form_kwargs in FormView
and here: Sending request.user object to ModelForm from class based generic view in Django
and here: Django: Accessing request in forms.py clean function
However they don't touch base with non-FormView classes. And as it's fairly old solutions, I was curious if there's more likely approach to reach request from forms.Form class.
Here's my code:
views.py
class InformFill(View):
form_class = InformForm
temlate_name = 'distrib_db/inform_fill.html'
def get(self, request):
if request.user.is_authenticated():
form = self.form_class(None)
return render(request, self.temlate_name, context={'form': form})
else:
return redirect('distrib_db:login')
def post(self, request):
if request.user.is_authenticated():
form = self.form_class(request.POST)
if form.is_valid():
inform = Inform(flt_numbr=form.cleaned_data['flight_number'], date=form.cleaned_data['date'],
template=form.cleaned_data['text'], user=request.user)
inform.save()
date = form.cleaned_data['date']
flt_numbr = form.cleaned_data['flight_number']
try:
emails, contacts = get_mail_cnt(date, flt_numbr)
# inform = get_object_or_404(Inform, pk=request['pk'])
paxdata = PaxData(inform=inform, emails=' '.join(emails), contacts=' '.join(contacts))
paxdata.save()
return redirect('/inform/{0}/'.format(inform.pk))
# 'distrib_db:detail', context={'pk': inform.id}
except Exception as e:
return render(request, 'distrib_db/sample.html',
context={'date': date, 'flight_number': flt_numbr, 'error': e})
# return render(request, 'distrib_db/sample.html', context={'date': date, 'flt_numbr': flt_numbr})
return render(request, self.temlate_name, context={'form': form})
else:
return redirect('distrib_db:login')
forms.py
class InformForm(forms.Form):
flight_number = forms.CharField(5, widget=forms.TextInput())
date = forms.DateField(widget=forms.DateInput(attrs={'class': 'datepicker'}))
template = forms.ChoiceField(choices=templates, widget=forms.Select(attrs={'id': 'select_box',
'onchange': 'javascript:changer();'}))
text = forms.CharField(widget=forms.Textarea(attrs={'id': 'txt_box', 'class': 'latin',
'maxlength': "160", 'onchange': 'javascript:validateTextArea();'}))
Generally speaking i'd like to achieve smth like this:
`class InformForm(forms.Form):
def get_template_choices(self):
templates = self.request.user.template_set.all()
choices = []
for t in templates:
choices.append((t.text, t.name))
return choices
flight_number = forms.CharField(5, widget=forms.TextInput())
date = forms.DateField(widget=forms.DateInput(attrs={'class':
'datepicker'}))
template = forms.ChoiceField(choices=get_template_choices(),
widget=forms.Select(attrs=
{'id': 'select_box',
'onchange': 'javascript:changer();'}))
text = forms.CharField(widget=forms.Textarea(attrs={'id': 'txt_box',
'class': 'latin',
'maxlength': "160",
'onchange': 'javascript:validateTextArea();'}))`
I'd appreciate any approach, mb I lack knowledge and asking newbie questions, sorry about it.
I just wanna get the python-way solution rather then build some js/jinja walk-arounds.
Thank you for your time!
#
After #Danielius comments, I've made some adjustments:
`class InformForm(forms.Form):
def __init__(self, user=None, *args, **kwargs):
if user:
self.fields['template'] = forms.ChoiceField(choices=tuple([(template.text, template.name) for template in user.template_set.all()]),
widget=forms.Select(attrs={'id': 'select_box', 'onchange': 'javascript:changer();'}))
flight_number = forms.CharField(5, widget=forms.TextInput())
date = forms.DateField(widget=forms.DateInput(attrs={'class': 'datepicker'}))
# template = forms.ChoiceField(choices=templates, widget=forms.Select(attrs={'id': 'select_box',
# 'onchange': 'javascript:changer();'}))
text = forms.CharField(widget=forms.Textarea(attrs={'id': 'txt_box', 'class': 'latin',
'maxlength': "160", 'onchange': 'javascript:validateTextArea();'}))`
Got an error AttributeError: 'InformForm' object has no attribute 'fields'
You can pass request to your form by changing your __init__ method like this :
class InformForm(forms.Form):
...
def __init__(self, user=None,*args, **kwargs):
super(InformForm, self).__init__(**kwargs)
if user:
self.fields['somefield'] = forms.ChoiceField()
self.fields['somefield'].widget = forms.Select()
self.fields['somefield'].queryset = Someobject.objects.filter(User=user)
...
If the User is linked to other object in db by Foreign key, then you will get all the values of other object as select items.
Also , when creating form you could pass user like this :
form= InformForm(user=request.user,data=request.POST)
I have a customer table that has an id, name, telephone and a date where the customer was set to inactive (called 'inactive_date').
In my form, I only want a checkbox to appear. If there is a date, then the checkbox is set. If the date is empty, then the checkbox is not set.
The only way I can set the checkbox so far is by doing the following:
class MyCustomerForm(ModelForm):
inactive_checkbox = forms.BooleanField(initial='checked')
I tried to use a clean or an init method to be able to set the checkbox based on a condition, but these attempts down below have been unsuccessful. My form always return the checkbox as unset regardless of the contents of the inactive_date.
class MyCustomerForm(ModelForm):
inactive_checkbox = forms.BooleanField()
def clean(self, *args, **kwargs):
inactive_date = self.cleaned_data['inactive_date']
if (inactive_date != None):
self.fields['inactive_checkbox'] = 'checked'
return self.cleaned_data
def __init__(self, *args, **kwargs):
super(myCustomerForm, self).__init__(*args, **kwargs)
inactive_date = self.cleaned_data['inactive_date']
if (inactive_date != None):
self.fields['inactive_checkbox'] = 'checked'
class Meta:
model = MyCustomer
fields = ['customer_id','customer_name','customer_phone',
'inactive_checkbox']
Any idea what is wrong with my code or is there perhaps a better way to control my inactive_date through a checkbox?
Try with initial:
https://docs.djangoproject.com/en/1.8/ref/forms/api/#dynamic-initial-values
In your view check the state of inactive_date field and then pass the result to the form via initial argument.
Hope it can help.
I wanted to post my new form based on jorlugaqui's answer as a comment, but comments are very limited in terms of formatting and length.
So here's my new form and view based on jorlugaqui's recommendation:
New form:
class MyCustomerForm(ModelForm):
inactive_checkbox = forms.BooleanField(required=False, label="Inactive")
class Meta:
model = MyCustomer
fields = ['customer_id','customer_name','customer_phone']
New view:
def customer_detail(request):
id = request.GET.get('id')
item = MyCustomer.objects.get(customer_id=id)
if request.method == 'POST':
form = MyCustomerForm(request.POST, instance=item, initial={'inactive_checkbox': item.inactive})
if form.is_valid():
inactive = request.POST.get('inactive_checkbox')
modified_item = form.save(commit=False)
if inactive:
if modified_item.inactive == None:
modified_item.inactive = datetime.datetime.now().strftime("%Y-%m-%d")
else:
modified_item.inactive = None
modified_item.save()
else:
form = MyCustomerForm(initial={'inactive_checkbox': item.inactive}, instance=item)
return render(request, 'basic_detail.html', {'id': id, 'form': form})
Create a new widget with the desired behavior. The default CheckboxInput widget (tested in django 3.2) properly checks or unchecks the checkbox based on if the field is a datetime or None. Then, you just override the response returned from user input:
from django.forms.widgets import CheckboxInput
from django.utils import timezone
class DateTimeCheckboxInput(CheckboxInput):
def value_from_datadict(self, data, files, name):
# cast the boolean returned to a timestamp or None
value = super().value_from_datadict(data, files, name)
return timezone.now() if value else None
Then, you can use this widget in any django model form
class MyCustomerForm(ModelForm):
class Meta:
model = MyCustomer
fields = ['inactive_checkbox']
widgets = {
"inactive_checkbox": widgets.DateTimeCheckboxInput,
}
I'm having a problem accessing a Django Form POST data.
I need to pass request.user to the form, so:
class TradeForForm(forms.Form):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
else:
request = kwargs.pop('request')
super(TradeForForm, self).__init__(*args, **kwargs)
#Obtain items for user
if user:
print user
items = Item.objects.filter(user=user)
choices = []
for i in range(len(items)):
choices.append([i,items[i].name])
self.fields['item_to_be_traded_for'].choices = choices
trade_type = forms.ChoiceField(
widget=RadioSelect(),
choices = [
['0','Item'],
['1','Money offer'],
]
)
item_to_be_traded_for = forms.ChoiceField()
and then call it using:
def trade_for(request, item_id):
item = Item.objects.get(id=item_id)
if request.method == 'POST':
form = TradeForForm(request.POST)
if form.is_valid():
pass
else:
form = TradeForForm(user=request.user)
variables = RequestContext(request, {
'form': form,
'item': item,
})
return render_to_response('trade_for.html', variables)
Now the problem is, when doing GET to access the empty form, it works just fine. But when I post it, I received an error:
KeyError at /trade_for/1/
'user'
Request Method: POST
Request URL: http://localhost:8000/trade_for/1/
Django Version: 1.3.1
Exception Type: KeyError
Exception Value:
'user'
Now how can this be fixed? I assume it's because the user variable is not passed to the form when creating it using the request.POST data, but I want to be able to create the form with the user parameter and without it, both working.
you should probably pass the user to the form creator even with POST data so the choices can be validated properly, so
TradeForForm(request.POST, user=request.user)
if you don't want this, you need to change user = kwargs.pop('user') to something like
user = kwargs.pop('user', None)
# if kwargs has no key 'user', user is assigned None
# make sure your code handles this case gracefully
pop will raise a KeyError unless it has a default value. So you just need to pass it a default value - probably None:
user = kwargs.pop('user', None)
If you want the form to work without the user, change the constructor to:
user = kwargs.pop('user', None)
But then you have to be able to deal with user being None.
The suggestion of user = kwargs.pop('user', None) is part of your missing code. A better approach would be to drop in your own arguments to avoid all the popping!
class TradeForForm(forms.Form):
def __init__(self, user=None, request=None, *args, **kwargs):
super(TradeForForm, self).__init__(*args, **kwargs)
if user:
...code here...