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,
}
Related
i'm trying to overwrite save method in my forms.py ,i have to prevent from creating duplicated objects , and if the object exists only update some fields
class Item(models.Model):
item = models.ForeignKey(Product,on_delete=models.CASCADE)
quantity = models.IntegerField()
for example if i entered this data before : item = XYZ , quantity = 100 i want to prevent from creating another XYZ item , i want to just update the quantity , for example i'll enter this data item = XYZ , quantity = 200 i try to prevent from creating this duplicate data , i just try to update the quantity previous quantity + new quantity 100 + 200 = 300 i must update the quantity to 300 for that purpose i overwrite save() in my forms.py
class ItemForm(forms.ModelForm):
class Meta:
model = Item
fields = ['item','quantity']
def save(self,*args,**kwargs):
if self.instance.item is None: #i also tried this if not self.instance.item
return super().save(*args,**kwargs)
else:
Item.objects.filter(item__name=self.instance.item).update(
quantity=F('quantity') + self.instance.quantity)
my views.py
def createNewProduct(request):
form = ItemForm()
if request.method == 'POST':
form = ItemForm(request.POST)
if form.is_valid():
form.save()
return render(request,'temp/add_item.html',{'form':form})
but it only update if it exists if not exists it doesn't create any new object , iexpect to create new object if it didn't exists , isn't there any way to achieve it please ? or i didn't something wrong ?
This is How I usually overwrite save method in model form:
def save(self, commit=True):
# your logic or Save your object for example:
obj = Model.objects.create(...)
return obj
Or you can also do this:
def save(self, commit=True):
obj = super().save(commit=False)
# do you logic here for example:
obj.field = something
if commit:
# Saving your obj
obj.save()
return obj
According to the documentation for ModelForm.save():
A subclass of ModelForm can accept an existing model instance as the
keyword argument instance; if this is supplied, save() will update
that instance. If it’s not supplied, save() will create a new instance
of the specified model.
This means that in your createNewProduct view, when handling POST requests, you need to check whether an Item already exists in the database and if so pass it to the Form constructor for editing, otherwise instantiate the ModelForm as per usual to create a new Item. So actually there's no need to override the ModelForm's save method
Since you want to add the old and new quantities instead of overwriting them you need to take care of that before the form is saved. This should typically happen in the form's clean method.
The resulting ItemForm and createNewProduct view would then look like this:
class ItemForm(forms.ModelForm):
class Meta:
model = Item
fields = ['item','quantity']
def clean(self):
cleaned_data = super().clean()
# if this Item already exists
if self.instance:
# add the old quantity to the new quantity
cleaned_data['quantity'] += self.instance.quantity
return cleaned_data
def createNewProduct(request):
if request.method == 'POST':
try:
dbItem = Item.objects.get(item=request.POST['item'])
except Item.DoesNotExist:
# get form for new Item
form = ItemForm(request.POST)
else:
# get form for existing Item
form = ItemForm(request.POST,instance=dbItem)
finally:
if form.is_valid():
form.save()
return redirect('success') # redirect on success
return redirect('failure') #redirect on failure
else:
form = ItemForm()
return render(request,'temp/add_item.html',{'form':form})
I want to set the item in dropdown using the query in the form. I want to add employee and the select company which using filter Is_Del= 0. I do not know how to set values for the drop down and where to write this query.
I tried to put in Forms.py, but it is not working.
This is form.py
class EmployeeCreateForm(forms.ModelForm):
class Meta:
model = Employee
fields = ('Emp_Name','Emp_company','Emp_Dept','Emp_Join_Date', 'Emp_End_Date')
def clean(self):
cleaned_data = super(EmployeeCreateForm, self).clean()
Emp_Name = cleaned_data.get('Emp_Name')
Emp_company = cleaned_data.get('Emp_company')
Emp_Dept = cleaned_data.get('Emp_Dept')
Emp_Join_Date = cleaned_data.get('Emp_Join_Date')
Emp_End_Date = cleaned_data.get('Emp_End_Date')
return cleaned_data
def __init__(self, *args, **kwargs):
super(EmployeeCreateForm,self).__init__(*args, **kwargs)
self.fields['Emp_company'].queryset = Company.objects.filter(Is_Del=0)
and below is my view.py
class EmployeeCraeteView(LoginRequiredMixin,SuccessMessageMixin,CreateView):
model=Employee
form = EmployeeCreateForm
success_message = " Employee Craeted successfully!"
success_url="../../company/all-companies"
template_name = 'employee_form.html'
fields =[
'Emp_Name','Emp_company','Emp_Dept','Emp_Join_Date',
'Emp_End_Date'
]
companies= Company.objects.filter(Is_Del=0)
def form_valid(self,form):
form.instance.Emp_Crt_By = self.request.user
if form.cleaned_data['Emp_Join_Date'] >= form.cleaned_data['Emp_End_Date']:
form.add_error('Emp_End_Date', 'Joining date should be less than Ending date')
return self.form_invalid(form)
return super(EmployeeCraeteView, self).form_valid(form)
I want to show only this companies in the form which are filtered by Is_Del =0
Your EmployeeCreateView is wrong:
Remove the attributes form, fields and companies
Add form_class = EmployeeCreateForm.
The reason is that form doesn't do anything in a CreateView (see here). To use a custom form class, you need to pass it to form_class.
Your CreateView was dynamically creating the form using a modelform_factory with the fields you defined (if you hadn't added those you'd have seen your mistake immediately) and so your EmployeeCreateForm is never instantiated.
I have a multiple choice field with a foreign key. I want to save which keeper was attending a training session and I want to list all keepers as a multiple choice field.
class AddAttendance(forms.ModelForm):
attendanceKeeper = Attendance.objects.only("keeper","present").all()
keeperValues = Attendance.objects.values_list("keeper__id", flat=True).distinct()
keeper = forms.ModelMultipleChoiceField(widget=forms.widgets.CheckboxSelectMultiple, queryset=Keeper.objects.filter(id__in=keeperValues, status=1))
class Meta:
model = Attendance
fields = ('keeper',)
def __init__(self, *args, **kwargs):
super(AddAttendance, self).__init__(*args, **kwargs)
self.initial["keeper"] = Keeper.objects.all()
However my problem is, I am not familiar how to handle a queryset in the view and how to loop through it and to save every instance with the value True or False.
I always get the value error that a queryset cannot be assigned
"Attendance.keeper" must be a "Keeper" instance
Can you help me how I access the queryset values and save them
def new_attendance(request, team_pk, package_pk):
if request.method == "POST":
form = AddAttendance(request.POST)
if form.is_valid():
for item in form:
attendance = item.save(commit=False)
attendance.keeper = get_object_or_404(AddAttendance.keeper)
attendance.team = get_object_or_404(Team, pk=team_pk)
attendance.created_date = timezone.now()
attendance.save()
return redirect(reverse('select_package', args=[package_pk, team_pk]))
else:
form = AddAttendance()
return render(request, 'attendance/new_attendance.html', {'form': form})
In the end I want to match keeper from the queryset and save True/False into the field present in my model
class Attendance(models.Model):
session = models.ForeignKey(Session)
keeper = models.ForeignKey(Keeper)
team = models.ForeignKey(Team)
present = models.BooleanField()
created_date = models.DateTimeField(default=timezone.now)
edited_date = models.DateTimeField(default=timezone.now)
You don't want a multiple choice field; you want a single choice. Only one keeper can be associated with each Attendance object.
You are doing a bunch of strange and unnecessary things here. You should remove most of this code, and use the ModelChoiceField which is the default for a ForeignKey. You don't want a checkbox widget either, since again that is for multiple choices; perhaps a radiobutton would be suitable.
class AddAttendance(forms.ModelForm):
class Meta:
model = Attendance
fields = ('keeper',)
widgets = {'keeper': forms.RadioSelect}
# remove the __init__ and the field definitions, you don't need them
...
form = AddAttendance(request.POST)
if form.is_valid():
attendance = item.save(commit=False)
attendance.team = get_object_or_404(Team, pk=team_pk)
attendance.created_date = timezone.now()
attendance.save()
return redirect(reverse('select_package', args=[package_pk, team_pk]))
There's no need to set the keeper explicitly in the view, since that's what the form is doing.
I'm using django form to add custom field in my form, but I want to exclude this custom field in some condition(when user_id is set on url query param), how can I handle it?
Something interesting happens in my try and error, I put my custom field name in Mata.exclude of my form, but this custom field still generates in my form !!!!
Here is my code:
class PushTokenForm(forms.ModelForm):
push_scenario = forms.ChoiceField(
label=_('Push Scenario'),
choices=get_scenarios(),
)
class Meta:
model = PushToken
exclude = ['failed', 'push_scenario']
UPDATE
def get_form(self, request, obj=None, **kwargs):
self.exclude = []
user_id = request.GET['user_id'] if 'user_id' in request.GET else None
if user_id:
self.exclude.append('push_scenario')
form = super(PushTokenAdmin, self).get_form(request, obj, **kwargs)
return form
I have a Django ModelForm with some initial data passed to it. Which is working fine so far.
But, if the user doesn't fill in all data, or makes another mistake the initial value will not be looked up again on redisplaying the form.
Here's a piece of code:
class TrainingAddForm(forms.ModelForm):
class Meta:
model = TrainingTasks
fields = ('task','ac_reg','date','wo_no')
def __init__(self, *args, **kwargs):
super(TrainingAddForm, self).__init__(*args, **kwargs)
self.fields['task'].required = False
self.fields['task'].widget.attrs['disabled'] = 'disabled'
self.fields['date'].widget = widgets.AdminDateWidget()
def clean_task(self):
return
An in forms.py:
def add_trainingtask(request, task_id):
if request.POST:
form = TrainingAddForm(request.POST)
if form.is_valid():
tt = TrainingTasks(
trainee = request.user,
task = Tasks.objects.get(pk=task_id),
date = form.cleaned_data['date'],
ac_reg = form.cleaned_data['ac_reg'],
wo_no = form.cleaned_data['wo_no'],
)
tt.save()
return HttpResponseRedirect('/admin/tot/tasks/')
else:
form = TrainingAddForm(initial = {"task": task_id})
return render_to_response('admin/tot/trainingtasks/add.html', {
'form': form,
'task_id': task_id
},
context_instance = RequestContext(request)
)
If a user misses to fill in i.e. the date (which is mandatory) the form will be redisplayed showing an error (field required), but the underlying record of task_id is not shown anymore.
The ID is still there and it's also possible to save the record (after correcting the error), so that's almost an irritating error for the user.
I guess I missed some piece of code, but I can't figure it out.
I'm not sure I understand the logic of your form or your view.
You've included the task field, yet disabled the field. With the task field disabled, the value isn't going to be in the request.POST collection.
In your view, you're passing the form the task_id parameter as the initial data, and if the request.method is a POST, you're retrieving the Task object from the database.
It seems like the Task is something that you want to assign to TrainingTask, but not it's not necessarily something you want to include in the form. Given that, I would:
#forms.py
class TrainingAddForm(forms.ModelForm):
class Meta:
model = TrainingTasks
fields = ('ac_reg','date','wo_no',)
#not include the task
def __init__(self, *args, **kwargs):
super(TrainingAddForm, self).__init__(*args, **kwargs)
self.fields['date'].widget = widgets.AdminDateWidget()
#views.py
from django.core.urlresolvers import reverse
from django.shortcuts import get_object_or_404, render
from your_app.forms import TrainingAddForm
from your_app.models import Task, TrainingTasks
def add_trainingtask(request, task_id):
#make sure we have a valid Task object, or redirect.
#you could also use a try/except Task.DoesNotExist and show an error
task = get_object_or_404(Task, pk=task_id)
form = TrainingAddForm(request.POST or None)
if request.POST:
if form.is_valid():
tt = TrainingTasks(
trainee = request.user,
task = task,
date = form.cleaned_data['date'],
ac_reg = form.cleaned_data['ac_reg'],
wo_no = form.cleaned_data['wo_no'],
)
tt.save()
#dont hard-code the url here
return HttpResponseRedirect(reverse('admin_tot_tasks'))
return render(request, 'admin/tot/trainingtasks/add.html', {'form': form,
'task' : task})
Hope that helps you out.