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
})
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 the following form:
class Recipe_IngredientForm(forms.ModelForm):
class Meta:
model = Recipe_Ingredient
fields = ('quantity', 'quantityUnit')
def __init__(self, *args, **kwargs):
super(Recipe_IngredientForm, self ).__init__(*args, **kwargs)
self.fields['ingredient_form'] = forms.CharField()
And I'm trying to get the value of this form to search for an object, if it exists, i'll set it to be saved in my model.
def recipe_add_ingredient(request, pk):
recipe = get_object_or_404(Recipe, pk=pk)
if request.method == "POST":
form = Recipe_IngredientForm(request.POST)
if form.is_valid():
recipeIngredient = form.save(commit=False)
recipeIngredient.recipe = recipe
aux = form.fields['ingredient_form']
recipeIngredient.ingredient = Ingredient.objects.get(name=aux)
recipeIngredient.save()
return redirect('recipe_detail', pk=recipe.pk)
else:
form = Recipe_IngredientForm()
return render(request, 'recipe/recipe_add_ingredient.html', {'form': form})
But I get an error when submitting the form: Ingredient matching query does not exist, but it shows that I'm getting a value that exists via GET, and if I query the exact same thing in the shell, it return my object. Any Idea?
You should be accessing cleaned_data, not fields.
aux = form.cleaned_data['ingredient_form']
Also note, you should define that field at class level, then you don't need to define __init__ at all.
class Recipe_IngredientForm(forms.ModelForm):
ingredient_form = forms.CharField()
class Meta:
model = Recipe_Ingredient
fields = ('quantity', 'quantityUnit')
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 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.