How to render an html template with data from view? - python

I am new to django. I made a form. I want that if the form is filled successfully then django should redirect to a success page showing the name entered in the form but no parameters should be present in the url itself.
I searched on the internet and the solution I got was to redirect to url with pk as a get parameter which fetches the data and shows in the view. But I don't want to pass any thing in the url itself. and some websites say that http can't redirect with post data.
Here's my views.py
class UserRegistrationView(CreateView):
model = UserForm
template_name = 'userregistration.html'
form_class = UserForm
success_url = 'success'
def get_success_url(self):
return reverse('success',kwargs = {'name' : self.object.firstName})
and here's the template to which I want to redirect:
<h2>Congratualations for registering {{name}} </h2>
Basically what I want is that if the person fill form mentioning his/her firstName as "xyz" then the redirected success page should say that "Congratulations for registering xyz"

You can use django sessions, which I believe installed by default in 1.8
Look here
# Set a session value:
request.session["fav_color"] = "blue"
# Get a session value -- this could be called in a different view,
# or many requests later (or both):
fav_color = request.session["fav_color"]
# Clear an item from the session:
del request.session["fav_color"]
You can pass your pk via session and extract your object in another view without affecting your url.
Make sure you clean up after yourself.
Let me know if more help needed.

One of the possible ways of passing data between views is via sessions. So, in your UserRegistrationView you need to override the form_valid method as shown below.
class UserRegsitrationView(CreateView):
def form_valid(self,form):
self.request.session['name'] = self.object.firstName
return super(UserRegistrationView,self).form_valid(form)
class SuccessView(TemplateView):
template_name = "success_template.html"
def get_context_data(self,**kwargs):
context = super(SuccessView,self).get_context_data(**kwargs)
context['name'] = self.request.session.get('name')
del self.request.session['name']
return context
One more thing that you can modify in your code is that you need not declare success_url if you are overriding get_success_url

Related

Django CreateView success url with pk from different unrelated model

I have a django CreateView where users can create new words. I also have an unrelated Song model, where users can choose to add words from the lyrics of the songs. So I've created a separate CreateView for this, so that I can have a different success url. The success url should go back to the song where the user was browsing. But I am struggling to figure out how to pass the pk of this particular object to the CreateView of a different model.
This is my special CreateView:
class CreateWordFromSong(LoginRequiredMixin, generic.CreateView):
template_name = 'vocab/add_custom_initial.html'
fields = ("target_word","source_word", etc.)
model = models.Word
from videos.models import Song
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return super(CreateWordFromSong, self).form_valid(form)
success_url = reverse_lazy('videos:song-vocab', kwargs={'pk': song_pk) #how to get song_pk?
Everything works when I replace song_pk with the actual pk.
I overwrite form_valid so that the user can be saved with the new object. Perhaps there is a way I can alter this so that I can also get the song_pk? I've played around with it a bit, but without luck.
I get the song_pk from my url:
path('song/<int:song_pk>/', views.CreateWordFromSong.as_view(), name='create-song'),
So I should have access to it. But when I try adding it to my view:
class CreateWordFromSong(LoginRequiredMixin, generic.CreateView, song_pk)
I get the error: name 'song_pk' is not defined.
You could add in class CreateWordFromSong instead success_url method get_success_url
def get_success_url(self):
return reverse_lazy('videos:song-vocab', kwargs={'pk': self.kwargs.get('song_pk')})
It will be work, if action in template with form be like this
<form action="{% url 'create-song' song_pk=song.pk %}" method="post">
(Surely a bit lately ...)
I faced a similar case but not sur this matches with your.
I have a list of Mapping objects (children) related to Flow object (parent and ForeignKey).
This list uses the Flow PK.
urls.py
path('flow/<int:pk>/mapping',
mappingColumnOneFlowListView.as_view(), name='mapping-details'),
On each line of Mapping objects, I got a "delete" button.
After deletion, I want to lead back to the list of the Flow Mapping but couldn't because the PK used in the view lead to the (deleted) Mapping.
Another solution would have consisted in leading to another URL without pk (eg. absolute_url in Model) but this was not what I wanted.
It's surely not the best way to achieve the goal but I found this:
in my DeleteView i added :
def get_success_url(self):
pk = self.kwargs['pk'] # Mapping's PK
flow_pk = MappingField.objects.filter(
pk=pk).first().fl_id.pk # fl_id.pk = Flow's PK (FK)
return reverse('mapping-details', kwargs={'pk': flow_pk})

DRF , route a retrieve function to a post function

I have a Django rest framework API that gets a POST request and has a retrive method in the view.
I want that when the user presses the post button it will route the URL to the render created in the retrieve method of the view class.
code:
#views.py
class LocationInfoViewSet(ModelViewSet):
# Order all objects by id, reversed.
queryset = LocationInfo.objects.all().order_by('-id')
serializer_class = LocationInfoSerializer
def retrieve(self, request, *args, **kwargs):
"""
This method is used to get the last object created by the user and render a map associated with the
mission's name.
"""
data = self.queryset[0]
serialized_data = LocationInfoSerializer(data, many=False)
points = list(serialized_data.data.values())
assign_gdt1 = GeoPoint(lat=points[2], long=points[3])
assign_gdt2 = GeoPoint(lat=points[4], long=points[5])
assign_uav = GeoPoint(lat=points[6], long=points[7], elevation=points[-3])
# Geo locations from the POST request.
gdt1 = [assign_gdt1.get_lat(), assign_gdt1.get_long()]
gdt2 = [assign_gdt2.get_lat(), assign_gdt2.get_long()]
uav = [assign_uav.get_lat(), assign_uav.get_long(), assign_uav.get_elevation()]
mission_name = points[1]
try:
# Check if a file already exists in the DB.
HTMLFileInteractionWithDB.table = THREE_POINTS_TRINAGULATION
openfile = HTMLFileInteractionWithDB.return_file_from_db(mission_name=mission_name)
return render(request, openfile)
except:
# Create a new file if one does not exists.
# The main function Creates an HTML File to be rendered.
return render(request, main(gdt1, gdt2, uav,
gdt1_elev=assign_gdt1.get_elevation(),
gdt2_elev=assign_gdt2.get_elevation(),
mission_name=mission_name
)
)
mission name is a primary key, So to access to the retrieve method the user need to go to the URL line and write the mission name for the method to work.
So, how and where in my project (urls,view...) do I create this route.
Exmpale:
I'm a little confused as to the purpose of this view.
The retrieve method is correctly used when it is retrieving a specific object from the queryset list using the pk. IE, one of your LocationInfo objects. It's always a get request.
Your retrieve method is also missing the pk parameter, which is sometimes defaulted as None.
retrieve(self, request, pk=None)
If I were you, I'd create a completely separate view or viewset method/action for this.
Instead of using retrieve for this, we can create a completely new method:
from rest_framework.decorators import action
class LocationInfoViewSet(ModelViewSet):
# Order all objects by id, reversed.
queryset = LocationInfo.objects.all().order_by('-id')
serializer_class = LocationInfoSerializer
# {The rest of your methods, etc...}
#action(detail=False, methods=["post"])
def last_object_by_user(self, request, *args, **kwargs):
# your query to get the last object by your user
queryset = LocationInfo.objects.filter(created_by=request.user).latest()
# The rest of your code
This will create a new endpoint called /{name}/last_object_by_user/ that you can make post requests to.
You'd also notice that I've modified your queryset a bit. Your current queryset gives us the last LocationInfo created by any user. Did you create a field in LocationInfo that tracks who created that LocationInfo?
Here's the documentation for this: Marking extra actions for routing
Note: I did not test this code so if you copy-paste this, it might not work, but the idea is what's important.

Add querystring parameters to from class based detail view with Django

How can I modify a class based view to add querystring parameters to the view? (So that when the view response is returned, the querystring is in the address bar?
class MyDetailView(DetailView):
"""
A detail view for retrieving a model object.
"""
model = MyModel
def some_function_to_modify_qs(self):
# do something and return modified qs for response
Not entirely sure why you’d want to do this, you could pass the values into the template instead but to answer your question the following should work.
from django.shortcuts import redirect
#inside your get method
return redirect(request.get_full_path() + “?query=string”)

Access POST data outside of post() in Django 1.5 TemplateView

I am new to Django (1.5) and I am trying to do a basic POST form. I have a TemplateView that implements the form (passed to the template using get_context_data).
When the form fails for some reason (e.g. validation error), I want to show the form again, containing the data that the user has filled. When it succeeds, I want to redirect to a success page (e.g. the just-created item).
Here's what I've done so far:
class WriteForm(forms.Form):
subject = forms.CharField()
text = forms.CharField(widget=forms.Textarea)
# some other stuff
class WriteView(MailboxView):
# MailboxView extends TemplateView and defines some context
template_name = 'messages/write.html'
form_data = None
def post(self, request, *args, **kwargs):
# treat form data...
# lets make things simple and just assume the form fails
# I want to do something like that:
self.form_data = request.POST
# should I return something?
def get_context_data(self, **kwargs):
context = super(WriteView, self).get_context_data(**kwargs)
if self.form_data is None:
context['form'] = WriteForm()
else:
context['form'] = WriteForm(self.form_data)
return context
Thanks in advance!
Django already has a FormView that you might be able to use. If you want to see how it works, here's the code on GitHub.
If you want to write your own view instead of using the built in form view, you might also find it useful to look at the FormView in Django Vanilla Views, which has a simpler implementation.

Passing form data from my view to thank you page

Is there a way I can pass data from a form submission over to the 'thank you' page. The reason i'd like to do this is because I have a form on the website, where the user will select multiple fields which all contains different PDF's.
So once the user has submitted the form the idea is to re-direct them to a thankyou page, where they can view the list of pdf/files they have selected on the form.
I hope this is enough info to go on. Here are my views / models.
def document_request(request, *args):
# template = kwargs['name'] + ".html"
if request.method == 'POST':
form = forms.ReportEnquiryForm(request.POST)
print(request.POST)
if form.is_valid():
docrequest = form.save()
return HttpResponseRedirect(reverse('thank_you', kwargs={'id': docrequest.id}))
else:
form = forms.ReportEnquiryForm()
return render_to_response('test.html',{'form':form})
def thank_you(request):
docrequest = DocumentRequest.objects.get(pk=id)
return render_to_response('thankyou.html',
{'docrequest' : docrequest },
context_instance=RequestContext(request))
My initial idea was to pass the data to a new view called thank_you. But not this is possible.
class DocumentUpload(models.Model):
name = models.CharField(max_length="200")
document_upload = models.FileField(upload_to="uploads/documents")
def __unicode__(self):
return "%s" % self.name
class DocumentRequest(models.Model):
name = models.CharField(max_length="200")
company = models.CharField(max_length="200")
job_title = models.CharField(max_length="200")
email = models.EmailField(max_length="200")
report = models.ManyToManyField(DocumentUpload)
def __unicode__(self):
return "%s" % self.name
form.py
class ReportEnquiryForm(forms.ModelForm):
class Meta:
model = models.DocumentRequest
fields = ('name', 'company', 'job_title', 'email', 'report')
If you need anymore info, please ask :)
You've saved the user's submission in a DocumentRequest object. So you can pass the ID of that object in the URL when you redirect, and in the thank_you view you can get the DocumentRequest and render the list.
Edit The idea is to make the thank_you page like any other view that accepts a parameter from the URL:
url(r'thanks/(?P<id>\d+)/$, 'thank_you', name='thank_you')
and so the POST part of the form view becomes:
if form.is_valid():
docrequest = form.save()
return HttpResponseRedirect(reverse('thank_you', kwargs={'id': docrequest.id}))
and thank_you is:
def thank_you(request, id):
docrequest = DocumentRequest.objects.get(pk=id)
return render_to_response('thankyou.html',
{'docrequest' : docrequest },
context_instance=RequestContext(request))
Second edit
As others have suggested, this makes it possible for anyone to see the request. So a better solution is to put it in the session:
docrequest = form.save()
request.session['docrequest_id'] = docrequest.id
and in thank_you:
def thank_you(request):
if not 'docrequest_id' in request.session:
return HttpResponseForbidden
docrequest = DocumentRequest.objects.get(request.session['docrequest_id'])
You can do as Daniel Roseman said but in this case the thank you pages can be accessed by anyone with the Ids.
Some ways to pass data between views are the following(the list is not mine.):
GET request - First request hits view1->send data to browser -> browser redirects to view2
POST request - (as you suggested) Same flow as above but is suitable when more data is involved
Using django session variables - This is the simplest to implement
Using client-side cookies - Can be used but there is limitations of how much data can be stored.
Maybe using some shared memory at web server level- Tricky but can be done.
Write data into a file & then the next view can read from that file.
If you can have a stand-alone server, then that server can REST API's to invoke views.
Again if a stand-alone server is possible maybe even message queues would work.
Maybe a cache like memcached can act as mediator. But then if one is going this route, its better to use Django sessions as it hides a whole lot of implementation details.
Lastly, as an extension to point 6, instead of files store data in some persistent storage mechanism like mysql.
The simplest way is to use sessions. Just add the id to the session and redirect to the thank you view, you read the id value and query the db with that id.

Categories

Resources