How can I called a view within another view django - django

Currently, I have a view that essentially closes a lead, meaning that it simply copies the information from one table (leads) to another (deals), now what I really would like to do is that after clicking close, the user is redirected to another page where the user can update some entries (sales forecast), I have a view that updates the lead, so I thought that I can do something like below:
#login_required
def close_lead(request):
id = request.GET.get('project_id', '')
keys = Leads.objects.select_related().get(project_id=id)
form_dict = {'project_id': keys.project_id,
'agent': keys.agent,
'client': keys.point_of_contact,
'company': keys.company,
'service': keys.services,
'licenses': keys.expected_licenses,
'country_d': keys.country
}
deal_form = NewDealForm(request.POST or None,initial=form_dict)
if request.method == 'POST':
if deal_form.is_valid():
deal_form.save()
obj = Leads.objects.get(project_id=id)
obj.status = "Closed"
obj.save(update_fields=['status'])
## Changing the Forecast Table Entry
forecast = LeadEntry.objects.filter(lead_id=id)
for i in forecast:
m = i
m.stage = "Deal"
m.save(update_fields=['stage'])
messages.success(request, 'You have successfully updated the status from open to Close')
update_forecast(request,id)
else:
messages.error(request, 'Error updating your Form')
return render(request,
"account/close_lead.html",
{'form': deal_form})
This view provides the formset that I want to update after closing the lead
#login_required
def update_forecast(request,lead_id):
# Gets the lead queryset
lead = get_object_or_404(Leads,pk=lead_id)
#Create an inline formset using Leads the parent model and LeadEntry the child model
FormSet = inlineformset_factory(Leads,LeadEntry,form=LeadUpdateForm,extra=0)
if request.method == "POST":
formset = FormSet(request.POST,instance=lead)
if formset.is_valid():
formset.save()
return redirect('forecast_lead_update',lead_id=lead.project_id)
else:
formset = FormSet(instance=lead)
context = {
'formset':formset
}
return render(request,"account/leadentry_update.html",context)
As you can see I’m calling this function update_forecast(request,id) after validating the data in the form, and I would have expected to be somehow redirected to the HTML page specified on that function, however, after clicking submit, the form from the first view is validated but then nothing happens, so I'm the function doesn't render the HTML page
My question how can I leverage existing functions in my views?, obviously, I will imagine that following the DRY principles you can do that in Django, so what am I doing wrong ?, how can I call an existing function within another function in views?

A view returns a response object. In your current code, you're calling a second view but not doing anything with its response. If you just wanted to display static content (not a form that might lead to an action that cares about the current URL) you could return the response object from the second view - return update_forecast(request, id).
But since your second view is displaying a form, you care about what the action for the view from the second form is. The typical Django idiom is to have forms submit to the current page's URL - that wouldn't work if you just call it and return its response object. You could customize the action in the second view, say adding an optional parameter to the view, but the usual idiom for form processing is to redirect to the view you want to show on success. Just as you do in the update_forecast view. Something like this:
messages.success(request, 'You have successfully updated the status from open to Close')
return redirect('update_forecast', lead_id=id)

Related

how to add element in django list

I'm confused with how django adds elements to a list. consider the following:
def add(request):
if request.method == "POST":
form = NewTaskForm(request.POST)
if form.is_valid():
task = form.cleaned_data["task"]
request.session['tasks'].append(task)
# request.session['tasks'] += [task]
return HttpResponseRedirect(reverse("tasks:index"))
else:
return render(request, "tasks/add.html",{
"form": form
})
return render(request, "tasks/add.html",{
"form": NewTaskForm()
})
if we add a print statement after request.session['tasks'].append(task) we get a list:
['check email']
we also get the same list if we comment the append line and use the correct way with +=
However, on the redirect to task/index the first way shows an empty list and the second way shows the list that's expected. Why? Whats going on?
Django only saves the session data and sends to client if it has been assigned or deleted. Like in your second example:
request.session['tasks'] += [task]
If you are updating the information inside your session data, it will not recognize the change and won't update it, like when you append some data to the list that is assigned to the 'tasks' key. In this case you need to explicitly tell Django that you modified the session data using:
request.session.modified = True

What is the difference between using Django form and manually setting date fields?

I am getting date/time info from ajax to Django. I am using 2 different views. event_edit is working fine, but event_edit_new does not work. It return an error Enter a valid date/time.
My question is what is making difference. They are getting exactly same information but one is ok while other one is not.
Javascript code making ajax request:
var ajax_test = function(event){
$.ajax({
url: '/scheduler/event/' + event.eventId + '/edit2/',
method: 'POST', // 'POST' or 'PUT'
data: {
'Note': event.title,
'OpNum': event.resourceId,
'StartTime' : event.start.format(),
'StopTime' : event.end.format(),
'ScheduleNum': event.eventId,
}
}).done(function(res) {
console.log("done", res)
}).fail(function(error) {
console.error("error", error)
console.log(event.start.format());
});
}
2 views
def event_edit(request, pk):
schedule = get_object_or_404(Schedule, pk=pk)
schedule.OpNum = request.POST.get('OpNum')
schedule.Note = request.POST.get('Note')
schedule.StartTime = request.POST.get('StartTime')
schedule.StopTime = request.POST.get('StopTime')
schedule.ScheduleNum =request.POST.get('ScheduleNum')
schedule.save()
return HttpResponse('ok')
def event_edit_new(request, pk):
schedule = get_object_or_404(Schedule, pk=pk)
if request.method =='POST':
form = ScheduleForm(request.POST, request.FILES, instance = schedule)
if form.is_valid():
form.save()
return HttpResponse('ok')
else:
return HttpResponse('error')
else:
return HttpResponse('done')
In your first view, there is no validation applied to user data input:
schedule.StartTime = request.POST.get('StartTime')
schedule.StopTime = request.POST.get('StopTime')
request.POST does not validate any data so that will grab user data exactly as submitted and set it on a model. Note that it might work but it is not guaranteed to work if user will ever send datetime format the application does not understand. For example try submitting something like "invalid date" and you should get 500 error.
Your second view on the other hand uses a Django Form to validate user input. By using a form you are validating user input before processing it in any way. You did not paste how your form is structured however if you are getting Enter a valid date/time. this means Django form did not validate one of the datetime fields. There are couple of reasons why that might be:
submitted datetime does is not one of input_formats for a DateTimeField. You can customize the formats with:
class MyForm(forms.Form):
datetime_field = forms.DateTimeField(input_formats=['%Y-%m-%dT%H:%M:%SZ', ...])
submitted datetime is not one of Django's default supported datetime formats.
You can customize them with DATETIME_INPUT_FORMATS setting.

Generate short temporary link to a form

So, I have a form that logged in user can fill, and when that form is submitted, I'd like it to generate a temporary link to another form that non-logged in users could access and fill, that would last 24 hours. That form would then send data to the database. That link would be valid only several times. that number is determined in the first form, by the logged in user.
I would like that URL to be as short as possible and easy to write down manually on a browser, but I don't know the best way to do so, or even how to do so.
The first form is completed, and only logged in user can fill it, but I do not know how to generate that temporary link.
Please keep in mind that I am fairly new to Django.
Views.py
class GenFormPageView(LoginRequiredMixin, TemplateView):
login_url = '/'
def get(selfself, request, **kwargs):
if request.method == 'POST':
formation_form = genForm(data=request.POST)
if formation_form.is_valid():
cdata = formation_form.cleaned_data.get
else:
print('Invalid')
else:
formation_form = genForm()
return render(request, 'genform.html', {'formation_form': formation_form})
Forms.py
class genForm(forms.ModelForm):
liste_formation = []
formations_info = {}
for formation in Formation.objects.all():
if formation.libelle in formations_info:
formations_info[formation.libelle].append(formation.formateur, formation.dateDebut, formation.dateFin,
formation.nbJours, formation.cours)
else:
info = {'formateur':formation.formateur.get_full_name(), 'dateDebut':formation.get_debut(), 'dateFin':formation.get_fin(), 'nbJours':formation.nbJours, 'cours':formation.cours.get_titre()}
formations_info[formation.libelle] = info
liste_formation.append(formations_info)
formations = [str(formation) for formation in Formation.objects.all()]
formation_select = forms.ChoiceField(label='Formation', choices=([(formation, formation) for formation in formations]), required = True)
formateur = forms.CharField(label='Formateur')
cours = forms.CharField(label='Cours')
nombre_eleves = forms.IntegerField(label='Nombre d\'élèves', min_value=1)
formations = json.dumps(formations_info)
class Meta:
model = Formation
fields = ('formation_select', 'formateur', 'dateDebut', 'dateFin', 'nbJours', 'cours', 'nombre_eleves')
urls.py
url(r'^genform/$', views.GenFormPageView.as_view()),
Here is the field tht is going to determine the number of time the new form can be submitted
When I click the button 'Generer', it creates a link to a form. that form can only be submitted a fixed number of time, and the link can only lead to that form if clicked within 24 hours from the generated time.
What I am trying right now, is creating a field in my database containing the time when the form is generated, and then I will check if the time is valid, and display the form only if it is.
That my idea right now, but I still don't konw how to do it.
So, to answer my question, I finally decided to use a field in my database containing the date of the creation of the form, then I would just check that today's date is within 24 hours from the date in my database.

Django not displaying correct URL after reverse

There's lots of documentation about Django and the reverse() method. I can't seem to locate my exact problem. Suppose I have two urlconfs like this:
url(r'ParentLists/$', main_page, name = "main_page"),
url(r'ParentLists/(?P<grade_level>.+)/$', foo, name = "foo")
and the two corresponding views like this:
def main_page(request):
if request.method == 'POST':
grade_level = request.POST['grade_picked']
return HttpResponseRedirect(reverse('foo', args = (grade_level,)))
else:
return render(request, 'index.html', context = {'grade_list' : grade_list})
def foo(request, grade_level):
grade_level = request.POST['grade_picked']
parent_list = # get stuff from database
emails = # get stuff from database
return render(request, 'list.html', context = {'grade_list' : grade_list, 'parent_list' : parent_list})
Here, list.html just extends my base template index.html, which contains a drop down box with grade levels. When the user goes to /ParentLists, the main_page view renders index.html with the drop down box as it should.
When the user picks a grade level from the drop down box (say 5th Grade), the template does a form submit, and main_page once again executes - but this time the POST branch runs and the HttpResponseRedirect takes the user to /ParentLists/05. This simply results in an HTML table pertaining to grade 5 being displayed below the drop down box.
The problem is, when the user now selects say 10th Grade, the table updates to show the grade 10 content, but the URL displayed is still /ParentLists/05. I want it to be /ParentLists/10.
Clearly, after the first selection, the main_page view never executes again. Only foo does...and so the HttpResponseRedirect never gets called. How should I reorganize this to get what I'm looking for? Thanks in advance!
As you correctly mentioned you will never redirect to foo() from foo().
So the simple way to fix this is just add similar code as in main_page() view:
def foo(request, grade_level):
if request.method == 'POST':
grade_level = request.POST['grade_picked']
return HttpResponseRedirect(reverse('foo', args = (grade_level,)))
else:
parent_list = # get stuff from database
emails = # get stuff from database
return render(request, 'list.html', context = {'grade_list' : grade_list, 'parent_list' : parent_list})
Please note that I remove grade_level = request.POST['grade_picked'] because as Nagkumar Arkalgud correctly said it is excessively.
Also instead of combination of HttpResponseRedirect and reverse you can use shortcut redirect which probably little easy to code:
from django.shortcuts redirect
...
return redirect('foo', grade_level=grade_level)
I would suggest you to use kwargs instead of args.
The right way to use the view is:
your_url = reverse("<view_name>", kwargs={"<key>": "<value>"})
Ex:
return HttpResponseRedirect(reverse('foo', kwargs={"grade_level": grade_level}))
Also, you are sending "grade_level" to your view foo using the URL and not a POST value. I would remove the line:
grade_level = request.POST['grade_picked']
as you will override the grade_level sent to the method from the url.

Need clarification on using Django 1.4 Form Wizards, specifically pre-filling and saving

We are building a wizard using Django 1.4's new form wizard functionality.
The docs on this are very terse and we can't find any advanced examples. We are using a named step wizard (needed to support a listview/datagrid we use) and a session backend.
The wizard is meant to edit roles and linked rights and is built to provide both add and edit functionality. We do this by asking the user in the first step if he/she wants to add or edit.
The next step depends on that choice;
If the user wants to edit, there is a search screen, followed by a listview/datagrid that displays results. The user can then select one of the results and goes to a details-screen, followed by a FilteredSelectMultiple page, allowing him/her to link rights to this role.
If the user wants to add a new role, the search and results screens are skipped and the user goes directly to the details screen, followed by the link-screen.
It all works pretty well, using a condition_dict in urls.py, but we are wondering a couple of things about the general functionality:
When a specific pre-existing role is selected, how can we fill the details and the link-screen with the corresponding data?
Do we instantiate a roles-object and pass it somehow to the two forms, if so, where do we instantiate it and do we need to do that for every form separately (which seems a bit over the top)?
When saving, is it common practice to create another instance of a role object, add the form data to it and save, or can we re-use the object used in the forms somehow?
We have tried overloading get_form_instance to return instances of roles, and we have looked at instance_dict in the docs, but it feels like the wrong approach and there are no examples to be found online, and we're not even sure these are used to pre-fill data or even if we're on the right track.
Logically, I would say in the step that selects an existing role, I need to fill the wizard-variables using an instance of the chosen object, and these get displayed in the forms. At the end of the wizard we reverse the process and get all data from the wizard-variables and add them to a newly instantiated roles-object and save it. Ideally this instance will determine itself if it needs to perform an INSERT or an UPDATE, depending on whether or not the promary key is filled.
If anyone can provide an example, or a nudge in the right direction, it would be very much appreciated.
The code of the wizardview class in views.py is below:
class RolesWizard(NamedUrlSessionWizardView):
def get_template_names(self):
# get template for each step...
if self.steps.current == 'choice':
return 'clubassistant/wizard_neworeditrole.html'
if self.steps.current == 'search':
return 'clubassistant/wizard_searchrole.html'
if self.steps.current == 'results':
return 'clubassistant/wizard_pickrole.html'
if self.steps.current == 'details':
return 'clubassistant/wizard_detailsrole.html'
elif self.steps.current == 'rights':
return 'clubassistant/wizard_roles.html'
def get_context_data(self, form, **kwargs):
# get context data to be passed to the respective templates
context = super(RolesWizard, self).get_context_data(form=form, **kwargs)
# add the listview in the results screen
if self.steps.current == 'results':
# get search text from previous step
cleaned_data = self.get_cleaned_data_for_step('search')
table = RolesTable(Roles.objects.filter(
role_name__contains=cleaned_data['searchrole'])
)
RequestConfig(self.request, paginate={
"per_page": 4,
}).configure(table)
# add the listview with results
context.update({'table': table})
# add a role instance based on the chosen primary key
if self.steps.current == 'rights':
cleaned_data = self.get_cleaned_data_for_step('results')
role_id = cleaned_data['role_uuid']
role = get_object_or_404(Roles, pk=role_id)
context.update({'role': role})
return context
def done(self, form_list, **kwargs):
# this code is executed when the wizard needs to be completed
# combine all forms into a single dictionary
wizard = self.get_all_cleaned_data()
if wizard.get("neworeditrole")=="add":
role = Roles()
else:
role = get_object_or_404(Roles, pk=wizard.get("role_uuid"))
# many-to-many rights/roles
role.role_rights_new_style.clear()
for each_right in wizard.get('role_rights_new_style'):
RightsRoles.objects.create(role=role, right=each_right,)
# other properties
for field, value in self.get_cleaned_data_for_step('details'):
setattr(role, field, value)
role.save()
# return to first page of wizard...
return HttpResponseRedirect('/login/maintenance/roles/wizard/choice/')
For future googlers:
I had some success with using get_form() because it is called before a form is rendered. Start with a couple of ModelForms:
class Wizard1(models.ModelForm):
class Meta:
model = MyModel
fields = ('field0', 'model0')
class Wizard2(models.ModelForm):
class Meta:
model = MyModel
excludes = ('field0', 'model0')
Then, in your SessionWizardView:
class MyWizard(SessionWizardView):
def get_form(self, step=None, data=None, files=None):
form = super(ExtensionCreationWizard, self).get_form(step, data, files)
if step is not None and data is not None:
# get_form is called for validation by get_cleaned_data_for_step()
return form
if step == "0":
# you can set initial values or tweak fields here
elif step == "1":
data = self.get_cleaned_data_for_step('0')
if data is not None:
form.fields['field1'].initial = data.get('field0')
form.fields['field2'].widget.attrs['readonly'] = True
form.fields['field3'].widget.attrs['disabled'] = True
form.fields['model1'].queryset = Model1.objects.filter(name="foo")
return form
The action is all in step 1. You request validated data from step 0 (which triggers another call to get_form() for step 0, so be careful) and then you can access any values that were set in step 0.
I threw in a couple of examples of settings you can change on the fields. You can update a queryset to limit the values in a ChoiceField, or re-display a value again but make it read-only. One caveat I noticed... readonly does not work on ChoiceField. You can make it disabled, but then the value is not propagated when you submit the form.
Let's see if I can help. I did a form wizard that adds steps depending on the answers. At each step I save all forms in a session variable, like so:
def process_step(self, request, form, step):
request.session['form_list'] = self.form_list
request.session['initial'] = self.initial
Then, each time that view is rendered, I instantiate a new form wizard with all the previous data:
def dynamic_wizard(request):
if not request.session.get('form_list'):
form = Wizard([Form1])
else:
form = Wizard(request.session.get('form_list'), initial = request.session['initial'])
return form(context=RequestContext(request), request=request)