How to save subform in parent form django ModelForm? [closed] - django

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 9 days ago.
Improve this question
Dears, please check this below scenario, I am trying to submit main form with sub form thats is a foreign key to the main form.
class ServiceProviderTransaction(BaseModel):
serial_number = models.IntegerField(unique=True)
service_provider_bill = models.OneToOneField(ServiceProviderBill, on_delete=models.CASCADE)
with the following forms.py
class ServiceProviderTransactionForm(forms.ModelForm):
class Meta:
model = ServiceProviderTransaction
fields = "__all__"
I have subform which is Bill inside Transaction form, upon submission of transaction form, I want my bill form to be saved as well with single submit, please advice
class ServiceProviderFormSubmissionView(View):
def get(self, request):
current_provider = ServiceProvider.objects.get(pk=1)
form = ServiceProviderTransactionForm(
initial={
"service_provider": current_provider,
}
)
context = {
"form": form,
"bill": ServiceProviderBillForm(),
}
return render(request, "provider_moves/sp-form.html", context)
def post(self, request):
transaction = ServiceProviderTransactionForm(request.POST)
bill = ServiceProviderBillForm(request.POST)
ServiceProviderBillForm["service_provider_bill"] = bill
# transaction["service_provider_bill"] = ServiceProviderBillForm(request.POST)
if transaction.is_valid():
# bill = bill.save()
transaction = transaction.save()
return redirect(reverse("spt-view"))
#else
context = {
"form": transaction,
"bill": bill,
}
return render(request, "provider_moves/sp-form.html", context)

Related

How to add a Django form field dynamically depending on if the previous field was filled?

I have a Form (Formset) for users to update their profiles. This is a standard User model form, and custom Participants model form. Now, in cases when a participant provide his phone number, I need to refresh the whole Form with a new 'Code' filed dynamically. And the participant will type the code he received my SMS.
Here is how I am trying to do it:
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
form.save()
seller_form = SellerForm(self.request.POST, instance=self.object.seller)
if seller_form.is_valid():
seller = self.request.user.seller
seller.inn = seller_form.cleaned_data.get('inn')
if seller_form.cleaned_data.get('phone_number'):
seller_form.fields['code'] = models.CharField(max_length=4)
return render(request, self.template_name, {'form': form, 'seller_form': seller_form})
seller.save()
return HttpResponse('Seller updated')
return render(request, self.template_name, {'form': form, 'seller_form': seller_form})
Well I am not sure if this is the way I can add additional field. What would you suggest to handle this situation?
A technique I have used is to have an initially hidden field on the form. When the form otherwise becomes valid, I cause it to become visible, and then send the form around again. In class-based views and outline:
class SomeThingForm( forms.Modelform):
class Meta:
model=Thing
fields = [ ...
confirm = forms.BooleanField(
initial=False, required=False, widget=forms.widgets.HiddenInput,
label='Confirm your inputs?' )
class SomeView( CreateView): # or UpdateView
form_class = SomeThingForm
template_name = 'whatever'
def form_valid( self, form):
_warnings = []
# have a look at cleaned_data
# generate _warnings (a list of 2-tuples) about things which
# aren't automatically bad data but merit another look
if not form.cleaned_data['confirm'] and _warnings:
form.add_error('confirm', 'Correct possible typos and override if sure')
for field,error_text in _warnings:
form.add_error( field, error_text) # 'foo', 'Check me'
# make the confirm field visible
form.fields['confirm'].widget = forms.widgets.Select(
choices=((0, 'No'), (1, 'Yes')) )
# treat the form as invalid (which it now is!)
return self.form_invalid( form)
# OK it's come back with confirm=True
form.save() # save the model
return redirect( ...)
For this question, I think you would replace confirm with sms_challenge, a Charfield or IntegerField, initially hidden, with a default value that will never be a correct answer. When the rest of the form validates, form_valid() gets invoked, and then the same program flow, except you also emit the SMS to the phone number in cleaned_data.
_warnings = []
# retrieve sms_challenge that was sent
if form.cleaned_data['sms_challenge'] != sms_challenge:
_warnings.append( ['sms_challenge', 'Sorry, that's not right'] )
if _warnings:
...
form.fields['sms_challenge'].widget = forms.widgets.TextInput
return self.form_invalid( form)
I think that ought to work.

How to clone model instances in Django?

I want a user to be able to copy a blog post by some other user and save it as their own post. Basically, when a user copies a blog post I want to create a new blog post by copying all of the fields from the original blog post but without any changes in an original blog post. Following is the code which I tried. The problem with this code is that when I try to copy blog post it is changing the primary key of the original blog post. I want the original blog post to remain untouched. Any suggestions would be appreciated. Thank you!
class CopyView(LoginRequiredMixin, UpdateView):
model = Entry
template_name = 'entries/create_entry.html'
fields = ['entry_title','entry_text', 'entry_input', 'entry_output']
def form_valid(self, form):
form.instance.entry_author = self.request.user
post = self.get_object()
post.save()
post.pk=None
post.save()
return super().form_valid(form)
Since you are copying an existing entry, you are creating a new Entry, not updating. Therefore you'll want to subclass CreateView and use get_initial() to copy the values from the existing Entry. This will give you a filled in form that you can edit and save. Then, on submit, you only have to override form_valid() to assign the entry_author like you were doing above. You can pass in the existing entry pk with a url like this:
path('entries/<int:entry_id>/copy/', CopyView.as_view(), name='entry_copy'),
Then in the view:
class CopyView(LoginRequiredMixin, CreateView):
model = Entry
template_name = 'entries/create_entry.html'
fields = ['entry_title','entry_text', 'entry_input', 'entry_output']
def get_initial(self):
initial = super().get_initial()
existing_entry = Entry.objects.get(pk=self.kwargs['entry_id'])
initial['entry_title'] = existing_entry.entry_title
initial['entry_text'] = existing_entry.entry_text
initial['entry_input'] = existing_entry.entry_input
initial['entry_output'] = existing_entry.entry_output
return initial
def form_valid(self, form):
form.instance.entry_author = self.request.user
response = super().form_valid(form)
return response

Custom UpdateView in Python

I am trying to get a custom UpdateView to work in Python/Django. I believe that the code that I've writtten is mostly correct, as it seems to be returning the proper Primary Key ID in the URL when I click on the associated dropdown. The problem is that I am not seeing any of the data associated with this record on the screen in update mode. The screen appears in edit mode, but there is no data. I suspect the problem is perhaps the django template in the html form? However, I have played with the form and used {{ form }} and it too returns a blank form. I've played with this all afternoon and I'm out of guesses. Here is my view:
def updating_document(request, pk):
doc = get_object_or_404(Doc, pk=pk)
form = Update_Doc_Form(request.user, request.POST)
if request.method == 'GET':
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('App:main_procedure_menu'))
else:
print("Form is invalid!")
return render(request,'Doc/update_doc.html',{'form':form })
I also have an associated form...
Form.py
class Update_Doc_Form(forms.ModelForm):
class Meta:
model = Doc
exclude = ['user']
doc_name = forms.CharField(widget=forms.TextInput)
description = forms.CharField(required=True,widget=forms.Textarea)
team = forms.CharField(widget=forms.Select)
document = forms.CharField(required=True,widget=forms.Textarea)
def __init__(self, *args, **kwargs):
super(Update_Doc_Form, self).__init__(*args, **kwargs)
self.fields['doc_name'].widget.attrs['class'] = 'name'
self.fields['description'].widget.attrs['class'] = 'description'
self.fields['team'].widget.attrs['class'] = 'choices'
self.fields['team'].empty_label = ''
I'm a newbie, but I do want to use a custom UpdateView so that I can alter some of the fields and pass user information. I feel like the code is close, just need to figure out why it's not actually populating the form with data. Thanks in advance for your help!
What a difference a day makes. Found an answer on SO this morning. Not sure how to credit the person or issue number....
The answer was to add the following line of code to my form:
user = kwargs.pop('object_user')
I also needed to add the following function to my View:
def get_form_kwargs(self):
kwargs = super(ViewName,self).get_form_kwargs()
kwargs.update({'object_user':self.request.user})
return kwargs
This question was answered originally in 2013 by Ivan ViraByan. Thanks Ivan!
I ultimately went with a standard class based UpdateView and scrapped my plans for the custom UpdateView once I was able to figure out how to use the Class Based View(UpdateView) and "pop" off the user information when passing it to the form based on Ivan ViraByan's answer in 2013.
The code above allows you to get the user but not pass it to the ModelForm so that you don't get the unexpected user error.

how does the "Favourate" button in StackOverFlow works?

i am creating a forum for my website. i would like to know how does the "This is your favourate question" button in StackOverFlow webpage(ie this website) works?
i am using django.
i created models such as:
models.py
class Question(models.Model):
user=models.ForeignKey(User)
created=models.DateTimeField(auto_now_add=True)
topic=models.ForeignKey(Topic)
question=models.TextField()
tags=models.CharField(max_length=50,blank=True)
class Favourites(models.Model):
'''
Does this your Favourite Question'''
user=models.ForeignKey(User)
favourite=models.BooleanField(default=False)
question=models.ForeignKey(Question)
also i would like to know how to create view to get total favourites for a particular question and also any other modified models.
You probably don't need the 'favourite' field. You can assume that, if there is entry in your table for a given question, related to a given user, that question is that users favorite.
You'll need to create a view which will take a question id, and mark it as the currently logged in user's favorite:
def mark_as_favorite(request, question_id, *args, **kwargs):
if request.user.is_authenticated():
question = get_object_or_404(Question, id=question_id)
favorite = Favourite.objects.create(user=request.user, question=question)
return HttpResponse("Marked")
else:
return HttpResponseForbidden("User is not logged in")
Finding the total favorites for a given question is easy:
def question(request, quesiton_id, *args, **kwargs):
question = get_object_or_404(Question, id=question_id)
favorites = Favourites.objects.filter(question=question)
favorites_count = favorites.count()
return render_to_response('template', {'question':questions, 'favorites_count':favorites_count}, context_instance=RequestContext(request))
i just edited jack shedd's answer
def mark_as_favorite(request, question_id, *args, **kwargs):
if request.user.is_authenticated():
fav, created = Favourite.objects.get_or_create(user=request.user, question=question)
if created:
return HttpResponse("Marked")
else:
fav.delete()
return HttpResponse("Un Marked")
else:
return HttpResponseForbidden("User is not logged in")

Dynamic View not displayed

I am trying to take a quiz and if you answer a question wrong you see that question get added to a list of wrong questions.
Questions can be used in multiple quizes so I can't hard code ask question_1 then question_2 etc.
When I load the page I see the first question but then if I submit or refresh I see the right.html template. It doesn't ask all of the questions.
It should ask all of the questions before rendering the right.html page
for question in quiz.questions.all():
if question not in asked_questions:
asked_questions.append(question)
return answer_question(request, quiz_id, question.id, module_list)
models.py
class Choice(models.Model):
choice = models.CharField(max_length=64)
def __unicode__(self):
return self.choice
#create a multiple choice quiz to start
class Question(models.Model):
question = models.CharField(max_length=64)
answer = models.CharField(max_length=64)
choices = models.ManyToManyField(Choice)
module = models.CharField(max_length=64)
def __unicode__(self):
return self.question
class Quiz(models.Model):
name = models.CharField(max_length=64)
questions = models.ManyToManyField(Question)
def __unicode__(self):
return self.name
views.py
asked_questions = []
module_list = []
module_list.append('test')
def take_quiz(request, quiz_id):
quiz = Quiz.objects.get(id=str(quiz_id))
for question in quiz.questions.all():
if question not in asked_questions:
asked_questions.append(question)
return answer_question(request, quiz_id, question.id, module_list)
#quiz is over
return render (request, 'right.html')
def answer_question(request, quiz_id, question_id, module_list):
question = Question.objects.get(id=question_id)
#module_list = []
if request.method == 'POST':
form = QuestionForm(request.POST)
choices = [(i, i) for i in question.choices.all()]
form.fields['selection'].choices = choices
if form.is_valid():
if form.cleaned_data['selection'] != str(question.answer):
module_list.append(question.module)
return take_quiz(request, quiz_id)
else:
form = QuestionForm()
choices = [(i, i) for i in question.choices.all()]
form.fields['selection'].choices = choices
return render(request, 'answer_question.html', {'question':question, 'form':form, 'module_list':module_list})
If you plan on hosting this and making it available on the web, you should really start over. You shouldn't use lists like that to store stuff, you should use sessions or the cache instead. Also, you would be better off using a FormWizard for the quiz since it is designed to do exactly what you are trying to do: handle multiple forms and process them.
Maybe you should try
return HttpResponseRedirect(reverse('answer_question', args=[quiz_id, question_id, module_list]))
to redirect your view to another view.
p.s.: reverse should contain the name of your 'answer_question' view