django redirect to calling view after processing function - django

I have written a function having the following signature:
def action_handler(request, model):
This action_handler is used from different views and handles the actions for this views. One example is deleting objects. In this example the user selectes some objects, selects the delete action and then the user is presented a page to check whether he/she wants to really delete the selected objects. This is done by the following code:
context = {
'action_name' : selected_action,
'object_list' : object_list,
}
return render_to_response("crm/object_delete_check.html", context,
context_instance=RequestContext(request))
For the case that something goes wrong I want to redirect the user to the view from where the user called the action.
Thus I want to ask here whether it is possible to get the calling view from the request object or somewhere else from.
If the def "def action_handler(request, model):" is called from the view "contacts(request):" then i want to redirect the user to the view "contacts(request):" .
But the clue is I do not want to hard-code it since the def action_handler is called from different views. Using a simple "return" is also not possible, since I want to recall the view completely.

if goback: #goback being whatever criteria means "something went wrong"
default_back_url = "someurl_in_case_the_meta_is_messed_up"
back = request.META.get('HTTP_REFERER',default_back_url) #yeah they spelled referrer wrong
if back:
return HttpResponseRedirect(back)
else:
return HttpResponseRedirect(default_back_url)
while META can be faked, it's harder to fake than GET query strings.

You can pass previous page url through GET parameter:
/object_delete_check/?previous=/contacts/
(see contrib.auth.decorators.login_required for example)

Related

Choosing correct Django delete view approach

I'm working on Django website and I have problem in figuring out correct/good way to handle delete view. From what I found out there are two ways to approach it:
1
class ObjectDeleteView(DeleteView):
model = Object
def get_success_url(self):
objectid = self.kwargs['object_id']
object = Object.objects.get(id = objectid)
container = object.container
containerid = container.id
url = reverse('Containers:showContainerContent', args=[containerid])
return url
def get_object(self):
return get_object_or_404(Object, pk=self.kwargs['object_id'])
2
def objectDelete(request, object_id):
object = Object.objects.get(id = object_id)
container = object.container
containerid = container.id
url = reverse('Containers:showContainerContent', args=[containerid])
return HttpResponseRedirect(url)
From what I can tell both are doing exactly the same thing - once object is deleted present user with page under Containers:showContainerContent.
The big difference I am experiencing is error I am getting when running unit test for this (simple call of the website and check of response code). With option 1 I end up getting error
django.template.exceptions.TemplateDoesNotExist: ContainerApp/object_confirm_delete.html
Which I understand - I don't have this template, this is default template for DeleteView, hence error is correct. The thing is I don't want to have any extra page. Just redirect user and that's it.
Also, I tested changing return url to return HttpResponseRedirect(url) in option 1, but result is the same.
What should I do here? Should I just continue with option 2? What are or might be the drawbacks for this approach?
There is a major difference between two class based delete view and function based view (the way you declared it).
CBV accepts get, post and delete http methods. When you send a get request to class based view, it does not delete the object. Instead it renders template with object to be deleted in context. This is basically used to have confirmation. For example you can send a get request and it will render a template with text "Do you really want to delete?" or "Please confirm blah blah..". And if you send a post or delete request, it will actually delete the object and redirect to next page.
FBV, on the other hand, give you full control over what you want to do. And as you declared it, it will accept any request type and delete the object and redirect to next page because you have not done any request type check in your view which is not a great idea IMHO. You should not allow deletion on get requests. They should be idempotent. There are plenty of otherthings that CBV provides. For example in case the object does not exist your FBV will crash. CBV, on contrary, will return proper 404 response if object does not exist.
So I think there is no bad in using FBV, but make is strong and secure enough that it handles every case (what if object does not exist?, what about confirmation?, GET should be idempotent only allow deletion with post? etc etc). Or simply use CBV.

Django Processing Form to other Form

I have a view that is an input form that people input their information in (name, address, that sort of thing). They will then click an "ok" button.
After people click "ok" I want them to be redirected to a page that has a table with their inputted information.
Any ideas on how to do this?
So far, I have the first view. When clicking OK all the information is stored in a database. I just don't know how to use it from there.
Note: ModelForms are used
If you look carefully the documentation : https://docs.djangoproject.com/en/dev/topics/forms/#processing-the-data-from-a-form
You could understand that, once you have treated (is_valid() - cleaned_data['']), you have to do a call to
render_to_response('template',{ 'name':name, 'last_name':last_name..}, context_instance=RequestContext(request))
And, in the template called, just make the data you need be shown, as usual..
you could try to pass your newly created object to the next view (which shows the new object) like this, the called view just has to accept your object as parameter.
# ... your form processing here
if form.is_valid():
# create and save your object code here
your_object.save()
return redirect('show_new_data_view', your_object=your_object)
Hope this helps.

Django want to use a variable from another view

I have a list of items in a view called client_items. I want to be able to use the variable items_list`which is another view called edit_order in client_items. So is there a way to call the variable from a different view? (Import a variable from another view and be able to use this variable in the other) I cannot just write it in client_items view because it needs an order_no augment.
Edit: here is my latest views. I have tried creating another views called items_in_edit_order. At this point I get `order_no not defined.
def items_in_edit_order(order_no):
order = models.Order.objects.get(pk = order_no)
return order
def client_items(request, client_id = 0):
client = models.Client.objects.get(pk = client_id)
items = client.storageitem_set.all()
order = items_in_edit_order(order_no)
return render_to_response('items.html', {'items':items, 'client':client, 'order':order}, context_instance = RequestContext(request))
Just adding, since no one has said this and it seems like you don't understand this yet:
Your client_items view must, somehow, have access to the order_no variable. If for some reason the value is not being passed along via the URL, it must get the value from somehwere. There are only three real locations where it could get this value:
Database: this will work if you are, for example, storing something like a cart which is directly linked to a user. So for example, you might be able to do something like order_no = Order.objects.filter(cart__user=request.user).order_no which would get the order associated with the user's current cart, then return the order_no value.
Session: you store the order_no in the session. This would assume you had an earlier view where the value for order_no was set, at which point you would save it using request.session['order_no']=order_no. Later, when you wanted to retrieve the value, you would simply use order_no=request.session['order_no'] in the view.
Cookie: not really recommended, but an option nonetheless. It's complicated because in the first view you'd have to create the response object (as in resp = render_to_response(template_name, locals(), RequestContext(request)) and then write a cookie to it resp.set_cookie("order_no", order_no). You retrieve it using request.COOKIES['order_no']
There are other, bizarre, places you could store the value: files, cache, other data storage formats, etc. Not at all recommended.
No. Write a function that returns the value you're interested in, and call it from both views.
What the guy said above is correct. You shouldn't attempt to "share" variables to different views.
However in the event you must or have a reason to then you could just store it in the session and then you have access to it in any view that has access to the "request".
Hope that helps.

Django: Passing a request directly (inline) to a second view

I'm trying to call a view directly from another (if this is at all possible). I have a view:
def product_add(request, order_id=None):
# Works. Handles a normal POST check and form submission and redirects
# to another page if the form is properly validated.
Then I have a 2nd view, that queries the DB for the product data and should call the first one.
def product_copy_from_history(request, order_id=None, product_id=None):
product = Product.objects.get(owner=request.user, pk=product_id)
# I need to somehow setup a form with the product data so that the first
# view thinks it gets a post request.
2nd_response = product_add(request, order_id)
return 2nd_response
Since the second one needs to add the product as the first view does it I was wondering if I could just call the first view from the second one.
What I'm aiming for is just passing through the request object to the second view and return the obtained response object in turn back to the client.
Any help greatly appreciated, critism as well if this is a bad way to do it. But then some pointers .. to avoid DRY-ing.
Thanx!
Gerard.
My god, what was I thinking. This would be the cleanest solution ofcourse:
def product_add_from_history(request, order_id=None, product_id=None):
""" Add existing product to current order
"""
order = get_object_or_404(Order, pk=order_id, owner=request.user)
product = Product.objects.get(owner=request.user, pk=product_id)
newproduct = Product(
owner=request.user,
order = order,
name = product.name,
amount = product.amount,
unit_price = product.unit_price,
)
newproduct.save()
return HttpResponseRedirect(reverse('order-detail', args=[order_id]) )
A view is a regular python method, you can of course call one from another giving you pass proper arguments and handle the result correctly (like 404...). Now if it is a good practice I don't know. I would myself to an utiliy method and call it from both views.
If you are fine with the overhead of calling your API through HTTP you can use urllib to post a request to your product_add request handler.
As far as I know this could add some troubles if you develop with the dev server that comes with django, as it only handles one request at a time and will block indefinitely (see trac, google groups).

django admin actions on all the filtered objects

Admin actions can act on the selected objects in the list page.
Is it possible to act on all the filtered objects?
For example if the admin search for Product names that start with "T-shirt" which results with 400 products and want to increase the price of all of them by 10%.
If the admin can only modify a single page of result at a time it will take a lot of effort.
Thanks
The custom actions are supposed to be used on a group of selected objects, so I don't think there is a standard way of doing what you want.
But I think I have a hack that might work for you... (meaning: use at your own risk and it is untested)
In your action function the request.GET will contain the q parameter used in the admin search. So if you type "T-Shirt" in the search, you should see request.GET look something like:
<QueryDict: {u'q': [u'T-Shirt']}>
You could completely disregard the querystring parameter that your custom action function receives and build your own queryset based on that request.GET's q parameter. Something like:
def increase_price_10_percent(modeladmin, request, queryset):
if request.GET['q'] is None:
# Add some error handling
queryset=Product.objects.filter(name__contains=request.GET['q'])
# Your code to increase price in 10%
increase_price_10_percent.short_description = "Increases price 10% for all products in the search result"
I would make sure to forbid any requests where q is empty. And where you read name__contains you should be mimicking whatever filter you created for the admin of your product object (so, if the search is only looking at the name field, name__contains might suffice; if it looks at the name and description, you would have a more complex filter here in the action function too).
I would also, maybe, add an intermediate page stating what models will be affected and have the user click on "I really know what I'm doing" confirmation button. Look at the code for django.contrib.admin.actions for an example of how to list what objects are being deleted. It should point you in the right direction.
NOTE: the users would still have to select something in the admin page, otherwise the action function would never get called.
This is a more generic solution, is not fully tested(and its pretty naive), so it might break with strange filters. For me works with date filters, foreign key filters, boolean filters.
def publish(modeladmin,request,queryset):
kwargs = {}
for filter,arg in request.GET.items():
kwargs.update({filter:arg})
queryset = queryset.filter(**kwargs)
queryset.update(published=True)