So, I have the following form:
class DesignItemForm (forms.ModelForm):
def __init__(self, *args, **kwargs):
super(DesignItemForm, self).__init__(*args, **kwargs)
CHOICES=[(i,i) for i in range(MAX_DESIGN_ITEM_QUANTITY)]
self.fields['quantity'] = forms.ChoiceField(choices=CHOICES)
class Meta:
model = DesignItem
fields = ('quantity','trackable',)
My view:
d = Design.object.get(slug=fromInput)
....
DesignItemInlineFormSet = inlineformset_factory(Design, DesignItem, fk_name="design", form=DesignItemForm,)
if request.method == "POST":
formset = DesignItemInlineFormSet(request.POST, request.FILES, instance=d)
if formset.is_valid():
formset.save()
DesignItemInlineFormSet(instance=d)
As you can tell, in my form, I overwrote the quantity field to be a drop down instead of an integer field.
For some reason, when I submit the form, the data is not updated in the database. However, if I change the form to the following, it works (of course, it doesn't have the dropdowns I want, but it posts to the db). Why is this, and how do I fix it?
class DesignItemForm (forms.ModelForm):
def __init__(self, *args, **kwargs):
super(DesignItemForm, self).__init__(*args, **kwargs)
# CHOICES=[(i,i) for i in range(MAX_DESIGN_ITEM_QUANTITY)]
# self.fields['quantity'] = forms.ChoiceField(choices=CHOICES)
class Meta:
model = DesignItem
fields = ('quantity','trackable',)
EDIT: Here is the DesignItem model:
class DesignItem(models.Model):
"""Specifies how many of an item are in a design."""
design = models.ForeignKey(Design, related_name="items")
quantity = models.PositiveIntegerField(default=1)
trackable = models.ForeignKey(Trackable, related_name="used")
have you tried just overriding the widget instead of the whole field?
i guess you want a select widget
def __init__(self, *args, **kwargs):
super(DesignItemForm, self).__init__(*args, **kwargs)
CHOICES=[(i,i) for i in range(MAX_DESIGN_ITEM_QUANTITY)]
self.fields['quantity'].widget = forms.Select(choices=CHOICES)
Related
Whenever I have to add a value to the instance of a form obtained from the context or from the URL I do it in the following way, using form.instance.
class PreguntaForm(forms.ModelForm):
class Meta:
model = Pregunta
fields = ('etiqueta', 'grupo', 'tipo_pregunta', 'opciones', 'mostrar_tabla', 'activo')
def __init__(self, *args, **kwargs):
cuestionario = kwargs.pop('cuestionario', False)
super(PreguntaForm, self).__init__(*args, **kwargs)
self.fields['grupo'].queryset = Grupo.objects.filter(cuestionario=cuestionario)
class PreguntaNueva(InfoPregunta, CreateView):
form_class = PreguntaForm
encabezado = 'Nueva Pregunta'
model = Pregunta
def get_form_kwargs(self):
kwargs = super(PreguntaNueva, self).get_form_kwargs()
kwargs['cuestionario'] = self.dame_cuestionario()
return kwargs
def form_valid(self, form):
form.instance.cuestionario = self.dame_cuestionario()
return super(PreguntaNueva, self).form_valid(form)
The problem that arises now is that I want to perform a check CreateView and EditView. To DRY, I want to do it in the clean method of the model, but the value that I assign to form.instance.cuestionario, is not available within the clean method. How could I do it? This value must not be edited by the user in any case.
Yes it is, you pass it in via get_form_kwargs; you just need to assign it to an instance variable in the form's __init__.
def __init__(self, *args, **kwargs):
self.cuestionario = kwargs.pop('cuestionario', False)
super(PreguntaForm, self).__init__(*args, **kwargs)
self.fields['grupo'].queryset = Grupo.objects.filter(cuestionario=self.cuestionario)
def clean(self):
# do something with self.cuestionario
I'm trying to pre-fill some inlines in Django Admin with data passed as query params (in case of adding a new object in DB).
class TestCaseInlineFormSet(BaseInlineFormSet):
class Meta:
model = TestCase
fields = '__all__'
def __init__(self, *args, **kwargs):
super(TestCaseInlineFormSet, self).__init__(*args, **kwargs)
ids_string = self.request.GET.get('ids')
if ids_string:
ids = [int(x) for x in ids_string.split(',')]
self.initial = [{'test_case': id} for id in ids]
class TestCaseInline(admin.TabularInline):
model = TestCase
raw_id_fields = ('test_case',)
extra = 1
formset = TestCaseInlineFormSet
def get_formset(self, request, obj=None, **kwargs):
formset = super(TestCaseInline, self).get_formset(request, obj, **kwargs)
formset.request = request
return formset
def get_extra(self, request, obj=None, **kwargs):
extra = super(TestCaseInline, self).get_extra(request, obj, **kwargs)
requested_extras = len(request.GET.get('ids', '').split(','))
return max(extra, requested_extras)
The data is pre-filled fine with this solution, however there's an issue when trying to submit: the pre-filled inlines are not marked as changed, so they're not saved.
I've tried overriding has_changed() on the TestCaseInlineFormSet however it doesn't solve the problem - it seems has_changed() for the formset is never called?
Any idea how to fix this?
I have the following form:
class PlayerAchievementForm(forms.ModelForm):
class Meta:
model = PlayerAchievement
fields = ('achievement',)
def __init__(self, *args, **kwargs):
super(PlayerAchievementForm, self).__init__(**kwargs)
self.fields['achievement'].queryset = Achievement.objects.filter(input_type=0)
I have the following implementation in a view:
def points(request, action, target=None):
if request.method == 'POST':
if target == 'player':
form = PlayerAchievementForm(request.POST)
print form.errors
if form.is_valid():
print 'valid'
elif:
print 'invalid'
On submit, this prints invalid.
If I take out this line in the form:
def __init__(self, *args, **kwargs):
super(PlayerAchievementForm, self).__init__(**kwargs)
self.fields['achievement'].queryset = Achievement.objects.filter(input_type=0)
Then it saves without issue. What is wrong with the init?
I found the answer here: DJango form with custom __init__ not validating
I was missing:
super(PlayerAchievementForm, self).__init__(*args, **kwargs)
I had the same problem and after a lot of tries, the code that work for me is something like that:
(Notice the first instantiation of the the field at class level.)
class PlayerAchievementForm(forms.ModelForm):
achievement = Achievement.objects.filter(input_type=0)
class Meta:
model = PlayerAchievement
fields = ('achievement',)
def __init__(self, *args, **kwargs):
super(PlayerAchievementForm, self).__init__(**kwargs)
self.fields['achievement'].queryset = Achievement.objects.filter(input_type=0)
I'm trying to override concept queryset in my child form, to get a custom list concepts based on the area got from request.POST, here is my list of concepts, which i need to filter based on the POST request, this lists is a fk of my child form (InvoiceDetail). is it possible to have these filters?
after doing some test when I pass the initial data as the documentation says initial=['concept'=queryset_as_dict], it always returns all the concepts, but i print the same in the view and its ok the filter, but is not ok when i render in template, so I was reading that I need to use some BaseInlineFormset. so when I test I obtained different errors:
django.core.exceptions.ValidationError: ['ManagementForm data is missing or has been tampered with']
'InvoiceDetailFormFormSet' object has no attribute 'fields'
so here is my code:
models.py
class ConceptDetail(CreateUpdateMixin): # here, is custom list if area='default' only returns 10 rows.
name = models.CharField(max_length=150)
area = models.ForeignKey('procedure.Area')
class Invoice(ClusterableModel, CreateUpdateMixin): # parentForm
invoice = models.SlugField(max_length=15)
class InvoiceDetail(CreateUpdateMixin): # childForm
tax = models.FloatField()
concept = models.ForeignKey(ConceptDetail, null=True, blank=True) # fk to override using custom queryset
invoice = models.ForeignKey('Invoice', null=True, blank=True)
views.py
class CreateInvoiceProcedureView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
template_name = 'invoice/invoice_form.html'
model = Invoice
permission_required = 'invoice.can_check_invoice'
def post(self, request, *args, **kwargs):
self.object = None
form = InvoiceForm(request=request)
# initial initial=[{'tax': 16, }] removed
invoice_detail_form = InvoiceDetailFormSet(request.POST, instance=Invoice,
request=request)
return self.render_to_response(
self.get_context_data(
form=form,
invoice_detail_form=invoice_detail_form
)
)
forms.py
class BaseFormSetInvoice(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
# call first to retrieve kwargs values, when the class is instantiated
self.request = kwargs.pop("request")
super(BaseFormSetInvoice, self).__init__(*args, **kwargs)
self.queryset.concept = ConceptDetail.objects.filter(
Q(area__name=self.request.POST.get('area')) | Q(area__name='default')
)
class InvoiceForm(forms.ModelForm):
class Meta:
model = Invoice
fields = ('invoice',)
class InvoiceDetailForm(forms.ModelForm):
class Meta:
model = InvoiceDetail
fields = ('concept',)
InvoiceDetailFormSet = inlineformset_factory(Invoice, InvoiceDetail,
formset=BaseFormSetInvoice,
form=InvoiceDetailForm,
extra=1)
How can i fix it?, what do i need to read to solve this problem, I tried to debug the process, i didn't find answers.
i try to do this:
def FooForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(FooForm, self).__init__(*args, **kwargs)
self.fields['concept'].queryset = ConceptDetail.objects.filter(area__name='default')
In a inlineformset_factory how can do it?.
After a lot of tests, my solution is override the formset before to rendering, using get_context_data.
def get_context_data(self, **kwargs):
context = super(CreateInvoiceProcedureView, self).get_context_data(**kwargs)
for form in context['invoice_detail_form']:
form.fields['concept'].queryset = ConceptDetail.objects.filter(area__name=self.request.POST.get('area'))
return context
I want for filter a ModelChoiceField with the current user. I found a solution very close that I want to do, but I dont understand
Django: How to get current user in admin forms
The answer accepted says
"I can now access the current user in my forms.ModelForm by accessing self.current_user"
--admin.py
class Customer(BaseAdmin):
form = CustomerForm
def get_form(self, request,obj=None,**kwargs):
form = super(Customer, self).get_form(request, **kwargs)
form.current_user = request.user
return form
--forms.py
class CustomerForm(forms.ModelForm):
default_tax = forms.ModelChoiceField(queryset=fa_tax_rates.objects.filter(tenant=????))
class Meta:
model = fa_customers
How do I get the current user on modelchoice queryset(tenant=????)
How do I call the self.current_user in the modelform(forms.py)
Override __init__ constructor of the CustomerForm:
class CustomerForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
super(CustomerForm, self).__init__(*args, **kwargs)
self.fields['default_tax'].queryset =
fa_tax_rates.objects.filter(tenant=self.current_user))
Queryset in the form field definition can be safely set to all() or none():
class CustomerForm(forms.ModelForm):
default_tax = forms.ModelChoiceField(queryset=fa_tax_rates.objects.none())
Just to sum up the solution because it was very hard for me to make this work and understand the accepted answer
In admin.py
class MyModelForm (forms.ModelForm):
def __init__(self, *args,**kwargs):
super (MyModelForm ,self).__init__(*args,**kwargs)
#retrieve current_user from MyModelAdmin
self.fields['my_model_field'].queryset = Staff.objects.all().filter(person_name = self.current_user)
#The person name in the database must be the same as in Django User, otherwise use something like person_name__contains
class MyModelAdmin(admin.ModelAdmin):
form = MyModelForm
def get_form(self, request, *args, **kwargs):
form = super(MyModelAdmin, self).get_form(request, *args, **kwargs)
form.current_user = request.user #get current user only accessible in MyModelAdminand pass it to MyModelForm
return form