Django Formtools Add Formset Dynamically - python

I am using Django Formtools to create a multistep form for a Job posting process. In one of the forms, I have Job Questions which I want the user to add dynamically, say a checkbox that generates the question form if they are interested in adding questions. They should have a button to create as many questions as possible. Now my challenge is that when I use normal model forms, I am able to complete the job posting process but if i replace the question form with a model formset and include it in the form_list I get key errors.
Secondly, if I try the various Javascript responses on adding fields dynamically such as this stack overflow response, I get form validation errors. Just to mention, the question Form uses the same model as the other forms (Job Model) thus my expectation is that regardless of how many questions are added they will be save to the Job Model. Does anyone know how to go about this? Adding fields in Django formtools dynamically and saving to the model? My Form tools wizard looks as below:
class JobWizard(SessionWizardView):
form_list=[JobForm7,JobForm1,JobForm2,JobForm3, JobForm4,JobForm5,JobForm6 ]
file_storage= FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'jobs'))
template_name="jobs/jobforms.html"
def get_template_names(self):
return [TEMPLATES[self.steps.current]]
def done(self, form_list, form_dict, **kwargs):
form_dict = self.get_all_cleaned_data()
categories = form_dict.pop('categories')
sub_categories = form_dict.pop('sub_categories')
job_question = form_dict.pop('job_question')
print(job_question)
print("_________________________")
job=Job.objects.create(**form_dict)
job.categories=categories
job.job_question=job_question
for sub_category in sub_categories:
job.sub_categories.add(sub_category)
# for question in job_question:
# job.job_question.add(question)
job.save()
return redirect('job_list')
And my model looks as below:
class Job(models.Model):
...#Other fields
# Form 4
job_question=models.CharField(max_length=20, default="")
# Form 5
job_freelancers_number=models.IntegerField(default=1)

So I was able to handle this by using Django Dynamic Formset library.All I needed was create the jquery.formset.js in my STATIC_URL then reference the same in my template after jquery. Then in my template < /script> section i passed below:
<script type="text/javascript">
$(function() {
$('#job-question').formset();
})

Related

Django:Error upon having more than than 3 models in one view

am trying to create a dashboard template page having a summary of the system model.But when i exceed more than three models in the view,it shows up an error.How do i go about this?
def dashboard(request):
return render(request=request,
template_name = 'main/admin.html',
context = {
"teachers_list": Teacher.objects.all(),
"stream_list": Stream.objects.all(),
"fees_list":Fees.objects.all(),
"books_list":Book.objects.all()
}
)
I just realised the error is from one of the url template a tags

How to route a multi-part form in django

I'm new to django.
I'd like my users to be able to place orders. For this kind of order, the user uploads a csv. The app parses, the CSV, serializes the data, and then needs to show the user a new view with the data and a "confirm order" button. I'm not sure how to do this in django.
class UploadSampleSheetView(LoginRequiredMixin, FormView):
form_class = UploadSampleSheetForm
template_name = 'pages/upload.html'
def form_valid(self, form):
uploaded_sample_sheet = self.request.FILES['uploaded_sample_sheet']
sample = _parse_sample_sheet_to_sample_model(uploaded_sample_sheet)
sample.save()
return super().form_valid(form)
def get_success_url(self):
return reverse("orders:create") # how do I return another view populated with data here?
class CreateOrderView(LoginRequiredMixin, CreateView):
model = Order
form_class = NewOrderForm
template_name = 'pages/complete_order.html'
What I'm looking for is some way that, on success, the UploadSampleSheetView can return a CreateOrderView with sample data.
In general, I'd love to be pointed to a reference about how to build user flows like this one. How does one view defer to another? I'm seeing a lot of return HttpResponseRedirect('url') which seems a little messy. How do I pass data around views?
FormViews are supposed to return HttpResponseRedirects. They take in the data, process it, and re-route you to a place that makes sense. I think your question is, how can I get routed to the appropriate place.
The documentation isn't very clear on this, so it's a very valid question. But, you can override get_success_url to take in an argument. So, you would add this:
def get_success_url(self, sample_id):
return reverse('create_order', kwargs={"pk" : sample_id})
and also change form_valid to
def form_valid(self, form):
uploaded_sample_sheet = self.request.FILES['uploaded_sample_sheet']
sample = _parse_sample_sheet_to_sample_model(uploaded_sample_sheet)
sample.save()
return HttpResponseRedirect(self.get_success_url(sample.id))
Don't worry, you're not missing out on anything by skipping super().form_valid() -- the docs (scroll just a little down) say that all form_valid does by default is redirect to success_url.
You can use Form Wizard
Django comes with an optional “form wizard” application that splits forms across multiple Web pages. It maintains state in one of the backends so that the full server-side processing can be delayed until the submission of the final form.
You might want to use this if you have a lengthy form that would be too unwieldy for display on a single page. The first page might ask the user for core information, the second page might ask for less important information, etc.
Harder to use, but very useful and flexible.
# views.py
from formtools.wizard.views import SessionWizardView
class MyView(SessionWizardView):
template_name = 'pages/wizard_steps.html'
template_name_done = 'pages/wizard_done.html'
form_list = (
('upload', UploadSampleSheetForm),
('confirm', NewOrderForm)
)
def done(self, form_list, **kwargs):
# you can get all forms data here and process it as you want
return render(self.request, self.template_name_done, {
'form_data': [form.cleaned_data for form in form_list],
})
def process_step(self, form):
if isinstance(form, UploadSampleSheetForm):
# Example. You can put here some additional manipulations
pass
return super().process_step(form)
And template
<!-- wizard_done.html -->
{% if wizard.steps.current == 'upload' %}
{% include 'pages/wizard_upload.html' %}
{% elif wizard.steps.current == 'complete' %}
{% include 'pages/wizard_complete.html' %}
{% endif %}
I think you are looking for Django Session. This is an extensive document if you want to dive deep.
It includes the examples that you are looking for as well.

Suit form include on list display in admin

Im trying to use Django Suit's form includes to add a button to the list display of all my subscribers in the admin. In the documentation it says to add this to your admin.py file in the right app.
class SubscriberAdmin(admin.ModelAdmin):
list_display = ('email', 'date')
readonly_fields = ('email', 'date')
def has_add_permission(self, request):
return False
suit_form_includes = (
('admin/suit_includes/suit_csv.html', 'top'),
)
However this only appears when clicking into an instance of an object, and doesn't show up on the admin page that shows the entire list of objects in the database. Is there a way to do this with Django Suit ? I had trouble finding anything on google.
suit form include template:
<button class="btn btn-info">Export to File</button>
Admin instance display (Where its appearing):
Admin list display (Where I want it to appear):
What's doing django-suit, here, is that it is including your HTML snippet in the change_form that is displayed for your model, the change_form being where you can modify your model and save the changes into the database.
Where you want it to appear is the "change_list", aka the place where you can see all of your instances of that model in your database.
To add it your html snippet, you should extend your change_list.html with your own snippet : More info on expanding templates in the official documentation
Good luck !

Multiple Instances of a django app, does django support this

I have written a simple feedback application in django. It's not particulairly complex, basically it allows authenticated users to write a shot message with a subject line and submit that message via a form. I then allows who are in a selected group to view user submitted feedback. In the future I may add more functionality but for now it does what I want.
Here comes my question, the site I'm building has multiple places where I would like to use the feedback app, for example I have a "what do you think of the site?" kind of page at /dev/feedback/ I also have one for customer support feedback at "/support/feedback/" Currently I have just copied the code from my mysite.apps.dev.feedback over to mysite.apps.support.feedback.
The problem is that this has now created two separate copies of the same code. Despite having just written the app the two versions are already starting to diverge which is annoying. My question is simply how do I create multiple instances of the same app in a django site with distinct database models?
Some resources I've found related but not helpful are https://docs.djangoproject.com/en/dev/topics/http/urls/ and Reversing namespaced URLs in Django: multiple instances of the same app The first page does not offer much on the issue and the second page provides somewhat cludgey and impractical solutions that seem to be both unrelated and more work than their worth. Is there a proper way to implement multiple instances of the same django app?
Single model approach
I'd personally try to keep this as one app and have a view that can handle being posted from multiple locations / tag them appropriately.
As S.Lott says, this is the way to go. I am providing alternatives if you're curious about methods to keep your code in one place in other situations.
For example, you could add a category field to your model, set up a single url conf which accepts an argument in the URL such as /(?P<category>\w+/feedback/$ and have the view simply tag the feedback with the appropriate category.
class MyForm(forms.ModelForm):
class Meta:
model = Feedback
def my_view(request, category):
form = MyForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
feedback = form.save(commit=False)
feedback.category = category
feedback.save()
return http.HttpResponse("Thanks for posting!")
return render(request, "mytemplate.html", {'form': form})
# urls.py
(r'^(?P<category>\w+)/feedback/$', 'my_view')
# user can visit dev/feedback or support/feedback and the feedback will be tagged appropriately
Abstract base class
Another solution is to build an abstract base class, then create subclasses for your distinct tables. That should solve the issue with your code getting out of sync.
You'd have a single abstract model (which has no tables) from which your "real" models in your separate apps would be based on.
Dynamically generated views
If you must have separate models, you could potentially write a dynamically constructed view.
def view_generator(model_class):
class MyForm(forms.ModelForm):
class Meta:
model = model_class
def my_view(request):
form = MyForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
form.save()
return http.HttpResponse("Thanks for posting!")
return render(request, "mytemplate.html", {'form': form})
return my_view
# urls.py
from foo import view_generator
(r'^my_first_feedback_form', view_generator(Model1))
(r'^my_second_feedback_form', view_generator(Model2l))
how do I create multiple instances of the same app in a django site with distinct database models?
You shouldn't.
You simply use the feedback app model in the other two apps with a simple from feedback.models import Feedback.
Then your support app can create, retrieve, update and delete Feedback objects.
Your dev app, similarly, can create, retrieve, update and delete Feedback objects because it imported the model.
That's all that's required: import.
Thanks Yuji Tomita for a very thorough answer, my final solution is derived very closely from his suggestion, but is different enough that I thought I would post it as another option if someone else runs into the same situation that I am in.
Firstly in my mysite.apps.feedback.models file I put
class Feedback( models.Model ):
subject = models.TextField( max_length=100 )
body = models.TextField( max_length=100 )
# Some other stuff here...
# Finally I used the suggestion above and created a field which I
# use to label each entry as belonging to a specific instance of the app.
instance_name = models.TextField( max_length=20 )
In my mysite.apps.feedback.views file I put
def save_message( request, instance_name ):
if request.method == 'POST':
form = FeedbackFrom( request.POST )
if form.is_valid():
form.instance.instance_name = instance_name
form.save()
return render("feedback/thanks.html")
else:
return render("feedback/submit.html", {'form':form })
else:
return render("feedback/submit.html",{'form':FeedbackForm()})
#user_passes_test( is_staff )
def all_messages( request, instance_name ):
messages = Feedback.objects.filter( instance_name = instance_name )
return render("feedback/view_all.html",{'feedback':messages} )
In my mysite.apps.dev.urls file I put
url(r'^feedback/', include('mysite.apps.feedback.urls'),
{'instance_name':'dev'}),
In my mysite.apps.support.urls file I put
url(r'^feedback/', include('mysite.apps.feedback.urls'),
{'instance_name':'support'}),
This will separate feedback messages by app instance. Note that my actual code is more complex but this should be good enough for anyone with a similar problem to get a solution up and running pretty quickly. Hope this is useful to anyone in a similar situation. Thanks again to Yuji Tomita for the suggestions upon which this solution is based.

How do I update an instance of a Django Model with request.POST if POST is a nested array?

I have a form that submits the following data:
question[priority] = "3"
question[effort] = "5"
question[question] = "A question"
That data is submitted to the URL /questions/1/save where 1 is the question.id. What I'd love to do is get question #1 and update it based on the POST data. I've got some of it working, but I don't know how to push the POST into the instance.
question = get_object_or_404(Question, pk=id)
question <<< request.POST['question'] # This obviously doesn't work, but is what I'm trying to achieve.
question.save()
So, is there anyway to push the QueryDict into the model instance and update each of the fields with my form data?
Of course, I could loop over the POST and set each value individually, but that seems overly complex for such a beautiful language.
You can use a ModelForm to accomplish this. First define the ModelForm:
from django import forms
class QuestionForm(forms.ModelForm):
class Meta:
model = Question
Then, in your view:
question = Question.objects.get(pk=id)
if request.method == 'POST':
form = QuestionForm(request.POST, instance=question)
form.save()

Categories