The beginning is simple:
class Question(models.Model):
question_string = models.CharField(max_length=255)
answers = models.CharField(max_length=255)
answers are json of list of strings e.g ['Yes', 'No']. Number of answers is dynamic.
The challenge for me now is to write a form for this model.
Current state is:
class NewQuestionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(NewQuestionForm, self).__init__(*args, **kwargs)
if self.instance:
self.fields['answers'] = AnswerField(num_widgets=len(json.loads(self.instance.answers)))
class Meta:
model = Question
fields = ['question']
widgets = {
'question': forms.TextInput(attrs={'class': "form-control"})
}
class AnswerField(forms.MultiValueField):
def __init__(self, num_widgets, *args, **kwargs):
list_fields = []
list_widgets = []
for garb in range(0, num_widgets):
field = forms.CharField()
list_fields.append(field)
list_widgets.append(field.widget)
self.widget = AnswerWidget(widgets=list_widgets)
super(AnswerField, self).__init__(fields=list_fields, *args, **kwargs)
def compress(self, data_list):
return json.dumps(data_list)
class AnswerWidget(forms.MultiWidget):
def decompress(self, value):
return json.loads(value)
The problem is: i get 'the JSON object must be str, not 'NoneType'' in template with '{{ field }}'
What is wrong?
I found the problem. I forgot to add 'answers' to class Meta 'fields'.
So my example of dynamic Multiwidget created from Model is:
class NewQuestionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
# need this to create right number of fields from POST
edit_mode = False
if len(args) > 0:
edit_mode = True
answer_fields = 0
for counter in range(0, 20):
answer_key = "answers_" + str(counter)
if args[0].get(answer_key, None) is not None:
answer_fields = counter + 1
else:
break
super(NewQuestionForm, self).__init__(*args, **kwargs)
if edit_mode:
self.fields['answers'] = AnswerField(num_widgets=answer_fields, required=False)
# get number of fields from DB
elif 'instance' in kwargs:
self.fields['answers'] = AnswerField(num_widgets=len(json.loads(self.instance.answers)), required=False)
else:
self.fields['answers'] = AnswerField(num_widgets=1, required=False)
class Meta:
model = Question
fields = ['question', 'answers']
widgets = {
'question': forms.TextInput(attrs={'class': "form-control"})
}
def clean_answers(self):
temp_data = []
for tdata in json.loads(self.cleaned_data['answers']):
if tdata != '':
temp_data.append(tdata)
if not temp_data:
raise forms.ValidationError('Please provide at least 1 answer.')
return json.dumps(temp_data)
'clean_answers' has 2 porposes: 1. Remove empty answers. 2. I failed to set required attribute on first widget. So i check here at least 1 answer exists
class AnswerWidget(forms.MultiWidget):
def decompress(self, value):
if value:
return json.loads(value)
else:
return ['']
class AnswerField(forms.MultiValueField):
def __init__(self, num_widgets, *args, **kwargs):
list_fields = []
list_widgets = []
for loop_counter in range(0, num_widgets):
list_fields.append(forms.CharField())
list_widgets.append(forms.TextInput(attrs={'class': "form-control"}))
self.widget = AnswerWidget(widgets=list_widgets)
super(AnswerField, self).__init__(fields=list_fields, *args, **kwargs)
def compress(self, data_list):
return json.dumps(data_list)
Related
i want that a form is prepoluate with data
my model:
TYPE = (("S",'Swing'),
("R","Rapide"))
class valuation(models.Model):
stock = models.ForeignKey("stock",on_delete=models.CASCADE,related_name='valuation',)
date = models.DateField(auto_created=True)
val_type = models.CharField(choices=TYPE, max_length=1,default='R')
user = models.ForeignKey("users.User", on_delete=models.CASCADE)
def __str__(self):
return f"{self.stock} - {self.date} - {self.val_type}"
my view:
class valuationCreateviewSwing(CreateView):
template_name = "evaluation/evaluation_create.html"
form_class = valuationModeform
def get_form_kwargs(self): # prepopulate form
kwargs = super(valuationCreateviewSwing, self).get_form_kwargs()
stck = get_object_or_404(stock, pk=self.kwargs['pk'])
kwargs['user'] = self.request.user
kwargs['val_type'] = "S"
kwargs['stock'] = stck
return kwargs
def get_context_data(self, **kwargs):
# we need to overwrite get_context_data
# to make sure that our formset is rendered
data = super().get_context_data(**kwargs)
if self.request.POST:
data["val_detail"] = ChildFormset1(self.request.POST)
else:
data["val_detail"] = ChildFormset1()
data.update({
"typeVal": "Swing",})
return data
def form_valid(self, form):
context = self.get_context_data()
val_detail_Swing = context["val_detail_Swing"]
self.object = form.save(commit=False)
# add data info neede about valuation model
self.object = form.save()
if val_detail_Swing.is_valid():
val_detail_Swing.instance = self.object
val_detail_Swing.save()
return super().form_valid(form)
def get_success_url(self):
return reverse("stock:stock-list")
I've a child form in my view (this part works ok):
ChildFormset1 = inlineformset_factory(
valuation, val_detail_Swing, form=valuationSwingModelform, can_delete=False)
I tried to use ge_for_kwargs but it seems not working as I've an error message :
init() got an unexpected keyword argument 'user'
You can use get_initial() method:
class valuationCreateviewSwing(CreateView):
template_name = "evaluation/evaluation_create.html"
form_class = valuationModeform
def get_initial(self):
query = self.request.GET
return {
'user': self.request.user.pk
'val_type': "S",
'stock': self.kwargs.get('pk')
}
...
Or you should override __init__() method and stay to use get_form_kwargs()
class valuationModeform(ModelForm):
class Meta:
model = Valuation
fields = '__all__'
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
val_type = kwargs('val_type', None)
stock = kwargs.pop('stock', None)
super().__init__(*args, **kwargs)
# assign initial values
self.fields['user'].initial = user
self.fields['val_type'].initial = val_type
self.fields['stock'].initial = stock
I have a form like this,
class UniqueUrlForm(forms.ModelForm):
cc_number = cc_form.CardNumberField(label='Card Number')
cc_expiry = cc_form.CardExpiryField(label='Expiration Date')
cc_code = cc_form.SecurityCodeField(label='CVV/CVC')
class Meta:
model = Transactions
fields = ['customer_name', 'customer_phone', 'customer_email', 'total_amount', 'cc_number', 'cc_expiry',
'cc_code']
def __init__(self, *args, **kwargs):
super().__init__()
store_id = kwargs.get("store_id", "1")
payment_page = get_object_or_404(
PaymentPageDisplayDetails.objects.filter(store_id=store_id).values("payment_fields_visible"))
with urllib.request.urlopen(payment_page['payment_fields_visible']) as url:
display_fields = json.loads(url.read().decode())
for field_name in display_fields:
self.fields[field_name] = forms.CharField(required=False)
and a view like this,
def getpaymentpage(request, store_identifier):
uniqueurl_form = UniqueUrlForm(request.POST or None, request.FILES or None, {"store_id": 1})
if uniqueurl_form.is_valid():
trx_details = {
"amount": uniqueurl_form.cleaned_data['amount'],
"customer_email": uniqueurl_form.cleaned_data['customer_email'],
"customer_phone": uniqueurl_form.cleaned_data['customer_phone'],
"cc_number": uniqueurl_form.cleaned_data['cc_number'],
"cc_name": uniqueurl_form.cleaned_data['customer_name'],
"cc_month": uniqueurl_form.cleaned_data['cc_month'],
"cc_year": uniqueurl_form.cleaned_data['cc_year'],
"cvv": uniqueurl_form.cleaned_data['cvv'],
}
return HttpResponse(trx_details)
context = {
'form': {
uniqueurl_form,
},
"page": store_display,
}
return render(request, 'unique_url.html', context)
I have tried print(uniqueurl_form.errors) it always returns empty and uniqueurl_form.is_valid() as false.
Is it because I'm adding dynamic fields to the form.
I have referred the following,
dynamically add field to a form
What am I doing wrong here?
Thank you for your suggestions.
weirdly it started working when i made following changes,
class UniqueUrlForm(forms.ModelForm):
cc_number = cc_form.CardNumberField(label='Card Number')
cc_expiry = cc_form.CardExpiryField(label='Expiration Date')
cc_code = cc_form.SecurityCodeField(label='CVV/CVC')
class Meta:
model = Transactions
fields = ['customer_name', 'customer_phone', 'customer_email', 'total_amount', 'cc_number', 'cc_expiry',
'cc_code']
def __init__(self, *args, **kwargs):
store_id = kwargs.get("store_id", "1")
super(UniqueUrlForm, self).__init__(*args, **kwargs)
payment_page = get_object_or_404(
PaymentPageDisplayDetails.objects.filter(store_id=store_id).values("payment_fields_visible"))
with urllib.request.urlopen(payment_page['payment_fields_visible']) as url:
display_fields = json.loads(url.read().decode())
for field_name in display_fields:
self.fields[field_name] = forms.CharField()
my guess is because I did not specify class name in my .super() event though it was appending the fields it was not sure what validations to put on those fields.
I have these models:
class Purchase(models.Model):
company = models.ForeignKey(Company,on_delete=models.CASCADE,null=True,blank=True)
party_ac = models.ForeignKey(Ledger1,on_delete=models.CASCADE,related_name='partyledger')
purchase = models.ForeignKey(Ledger1,on_delete=models.CASCADE,related_name='purchaseledger')
total = models.DecimalField(max_digits=10,decimal_places=2,null=True,blank=True)
class Stock_total(models.Model):
company = models.ForeignKey(Company,on_delete=models.CASCADE,null=True,blank=True)
purchases = models.ForeignKey(Purchase,on_delete=models.CASCADE,null=True,blank=False,related_name='purchasetotal')
stockitem = models.ForeignKey(Stockdata,on_delete=models.CASCADE,null=True,blank=True,related_name='purchasestock')
total = models.DecimalField(max_digits=10,decimal_places=2,default=0.00,null=True,blank=True)
This are my forms:
class Purchase_form(forms.ModelForm):
class Meta:
model = Purchase
fields = ('Company','Party_ac', 'purchase', 'sub_total')
def __init__(self, *args, **kwargs):
self.User = kwargs.pop('User', None)
self.Company = kwargs.pop('Company', None)
super(Purchase_form, self).__init__(*args, **kwargs)
self.fields['party_ac'].queryset = Ledger1.objects.filter(Q(Company = self.Company) , Q(group1_Name__group_Name__icontains='Sundry Creditors') | Q(group1_Name__group_Name__icontains='Bank Accounts') | Q(group1_Name__group_Name__icontains='Cash-in-hand') | Q(group1_Name__Master__group_Name__icontains='Sundry Creditors') | Q(group1_Name__Master__group_Name__icontains='Bank Accounts') | Q(group1_Name__Master__group_Name__icontains='Cash-in-hand'))
self.fields['party_ac'].widget.attrs = {'class': 'select2_demo_2 form-control',}
self.fields['purchase'].queryset = Ledger1.objects.filter(Q(Company = self.Company) ,Q(group1_Name__group_Name__icontains='Purchase Accounts') | Q(group1_Name__Master__group_Name__icontains='Purchase Accounts'))
self.fields['purchase'].widget.attrs = {'class': 'select2_demo_2 form-control',}
self.fields['total'].widget.attrs = {'class': 'form-control',}
class Stock_Totalform(forms.ModelForm):
class Meta:
model = Stock_Total
fields = ('stockitem', 'Total_p')
def __init__(self, *args, **kwargs):
super(Stock_Totalform, self).__init__(*args, **kwargs)
self.fields['stockitem'].widget.attrs = {'class': 'select2_demo_2 form-control',}
self.fields['total'].widget.attrs = {'class': 'form-control',}
Purchase_formSet = inlineformset_factory(Purchase, Stock_Total,
form=Stock_Totalform, extra=3)
The views:
class Purchase_createview(ProductExistsRequiredMixin,Company_only_accounts_mixins,LoginRequiredMixin,CreateView):
form_class = Purchase_form
template_name = 'stockkeeping/purchase/purchase_form.html'
def get_context_data(self, **kwargs):
context = super(Purchase_createview, self).get_context_data(**kwargs)
company_details = get_object_or_404(Company, pk=self.kwargs['pk'])
context['company_details'] = company_details
selectdatefield_details = get_object_or_404(Selectdatefield, pk=self.kwargs['pk3'])
context['selectdatefield_details'] = selectdatefield_details
if self.request.POST:
context['stocks'] = Purchase_formSet(self.request.POST)
else:
context['stocks'] = Purchase_formSet()
return context
def form_valid(self, form):
c = Company.objects.get(pk=self.kwargs['pk'])
form.instance.Company = c
context = self.get_context_data()
stocks = context['stocks']
with transaction.atomic():
self.object = form.save()
if stocks.is_valid():
stocks.instance = self.object
stocks.save()
return super(Purchase_createview, self).form_valid(form)
def get_form_kwargs(self):
data = super(Purchase_createview, self).get_form_kwargs()
data.update(
Company=Company.objects.get(pk=self.kwargs['pk'])
)
return data
I want to perform a queryset method for stockitem field in Stock_Totalform which I will make specific to Company like I have done for Party_ac and purchase in Purchase_form above..
When I try to do the same for Stock_Totalform objects it gives me no result..
I tried like above doing: self.Company = kwargs.pop('Company', None) using the get_form_kwargs function in views.
But it looks like it doesnot works for the ForeignKey field which is inlined with a parent model.
Can anyone help to to solve this issue.
Thank you
These are my forms:
class MultijournaltotalForm(forms.ModelForm):
class Meta:
model = Multijournaltotal
fields = ('Date', 'Total_Debit', 'Total_Credit')
widgets = {
'Date': DateInput(),
}
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('User', None)
self.company = kwargs.pop('Company', None)
super(MultijournaltotalForm, self).__init__(*args, **kwargs)
self.fields['Date'].widget.attrs = {'class': 'form-control',}
self.fields['Total_Debit'].widget.attrs = {'class': 'form-control',}
self.fields['Total_Credit'].widget.attrs = {'class': 'form-control',}
class MultijournalForm(forms.ModelForm):
class Meta:
model = Multijournal
fields = ('By','To','Debit','Credit','narration')
def __init__(self, *args, **kwargs):
self.User = kwargs.pop('User', None)
self.Company = kwargs.pop('Company', None)
super(MultijournalForm, self).__init__(*args, **kwargs)
self.fields['Debit'].widget.attrs = {'class': 'form-control',}
self.fields['Credit'].widget.attrs = {'class': 'form-control',}
self.fields['To'].widget.attrs = {'class': 'form-control select2',}
self.fields['By'].widget.attrs = {'class': 'form-control select2',}
self.fields['narration'].widget.attrs = {'class': 'form-control',}
Multijournal_formSet = inlineformset_factory(
Multijournaltotal,
Multijournal,
form=MultijournalForm,
extra=6,
)
I am facing a problem in updating my inline formset from quite a long time. The create view works perfectly fine but when I try to update the instances of Multijournal in Multijournaltotal then the objects are not shown in the formset...
I tried this previously:
class Multijournal_updateview(LoginRequiredMixin,UpdateView):
model = Multijournaltotal
form_class = MultijournaltotalForm
template_name = 'Multijournal/multi_journal_form.html'
def get_success_url(self,**kwargs):
company_details = get_object_or_404(Company, pk=self.kwargs['pk'])
multijournal_details = get_object_or_404(Multijournaltotal, pk=pk2)
selectdatefield_details = get_object_or_404(Selectdatefield, pk=self.kwargs['pk3'])
return reverse(
'accounting_double_entry:multijournaldetail',
kwargs={
'pk1':company_details.pk,
'pk2':multijournal_details.pk,
'pk3':selectdatefield_details.pk
},
)
def get_context_data(self, **kwargs):
context = super(Multijournal_updateview, self).get_context_data(**kwargs)
company_details = get_object_or_404(Company, pk=self.kwargs['pk'])
context['company_details'] = company_details
selectdatefield_details = get_object_or_404(Selectdatefield, pk=self.kwargs['pk3'])
context['selectdatefield_details'] = selectdatefield_details
multijournal_details = get_object_or_404(Multijournaltotal, pk=self.kwargs['pk2'])
queryset = Multijournal.objects.filter(total= multijournal_details.id)
multijournalformset = Multijournal_formSet(self.request.POST or None, queryset=queryset)
context['multijournalformset'] = multijournalformset
return context
Can anyone tell me if I am going in right direction?
Thank you
I'm unable to figure out how to only call a queryset of items that belong to a specific User in the django forms.
dropoffs/models.py
class DropoffItem(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True)
dropoff = models.ForeignKey('Dropoff', null=True, blank=True)
product = models.ForeignKey(Product)
location = models.CharField(max_length=120, choices=LOCATION_CHOICES, default="Customer")
def __str__(self):
return str('%s' + " " + "(" + '%s' + ")") %(self.product.title, self.product.sku)
def sku(self):
return self.product.sku
def title(self):
return self.product.title
def dropoff_id(self):
return str(self.dropoff.id)
forms.py
class AddPickupItemForm(forms.ModelForm):
dropoffitem = forms.ModelChoiceField(queryset=DropoffItem.objects.none())
class Meta:
model = PickupItem
# fields = ["product", "quantity"]
fields = ['dropoffitem']
def __init__(self, user, *args, **kwargs):
# self.request = kwargs.pop("request")
the_user = kwargs.pop('user', None)
super(AddPickupItemForm, self).__init__(*args, **kwargs)
if the_user is not None:
self.fields["dropoffitem"].queryset = DropoffItem.objects.filter(user=the_user)
views.py
def add_item_to_pickup_order(request):
request.session.set_expiry(120000)
try:
user = request.user
the_id = request.session['pickup_id']
pickup = Pickup.objects.get(id=the_id)
except:
user = request.user
new_pickup_order = Pickup(user=user)
new_pickup_order.save()
request.session['pickup_id'] = new_pickup_order.id
the_id = new_pickup_order.id
pickup = Pickup.objects.get(id=the_id)
try:
dropoffitem = DropoffItem.objects.filter(user=user)
except DropoffItem.DoesNotExist:
pass
except:
pass
form = AddPickupItemForm(request.POST, user=request.user)
if request.method == "POST":
dropoffitem_id = int(request.POST['dropoffitem'])
pickup_item = PickupItem.objects.create(pickup=pickup, dropoffitem_id=dropoffitem_id)
pickup_item.save()
return HttpResponseRedirect('%s'%(reverse('add_item_to_pickup_order')))
context = {
"pickup": pickup,
"form": form,
}
return render(request, 'pickups/create_pickup_order.html', context)
With the modifications to init, I'm getting a TypeError of: init() got multiple values for keyword argument 'user'.
Could that be because of how I'm requesting a 'session'?
class AddPickupItemForm(ModelForm):
def __init__(self,*args,**kwargs)
the_user = kwargs.pop('user',None)
super(AddPickupItemForm, self).__init__(*args,**kwargs)
if the_user is not None:
self.fields['dropoffitem'].queryset = DropOffItem.objects.filter(user=the_user)
In other words, pass your user to the form when instantiating, if you need to.