my view is like this
class RecordView(View):
record_form = RecordForm
record_files = {}
templare = 'acquisition.html'
def get(self, request, *args, **kwargs):
fil = urllib.urlopen('/home/student/wwww.jpg')
self.record_files = {'small_cover': SimpleUploadedFile('hehe.jpg', fil.read())}
rr_form = self.record_form()
return render(request, self.template_name, {'rr_form': rr_form,
})
def post(self, request, *args, **kwargs):
record = RecordForm(request.POST, self.record_files)
record.save()
HttpResponseRedirect('/')
Here i have populated self.record_files in get method.. but after i post data i see self.record_files as empty dictionary. I get confused here. What can i do to do so.
The state of your view instance is not maintained between a get and a post, so setting record_files on the instance will not keep it for the next request. You would need to put that logic in the dispatch method, or store information in the user's session.
class RecordView(View):
record_form = RecordForm
record_files = {}
templare = 'acquisition.html'
# dispatch is called before deciding whether to use get() or post()
# so any instance-level properties that require the request can go here.
# This could even go in __init__().
def dispatch(self, request, *args, **kwargs):
fil = urllib.urlopen('/home/student/wwww.jpg')
self.record_files = {'small_cover': SimpleUploadedFile('hehe.jpg', fil.read())}
return super(RecordView, self).dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
rr_form = self.record_form()
return render(request, self.template_name, {'rr_form': rr_form,
})
# self.record_files will be available in a get, or a post (or any valid
# method for that matter).
def post(self, request, *args, **kwargs):
record = RecordForm(request.POST, self.record_files)
record.save()
return HttpResponseRedirect('/')
That is exactly how it suppose to work. Whenever you have a GET request, it will call method get, and if it is a POST request, naturally it will call post method, but not the get method. So here is how you can solve your problem:
from django.views.generic import TemplateView
class RecordView(TemplateView):
record_form = RecordForm
record_files = {}
template_name = 'acquisition.html'
def get_context_data(self, **context):
fil = urllib.urlopen('/home/student/wwww.jpg')
self.record_files = {
'small_cover': SimpleUploadedFile('hehe.jpg', fil.read())
}
context.update({
'record_files': self.record_files,
'rr_form': self.record_form()
})
fil.close()
return super(RecordView, self).get_context_data(**context)
def post(self, request, *args, **kwargs):
context = self.get_context_data()
record_form = self.record_form(request.POST, self.record_files)
if record_form.is_valid():
record_form.save()
## or do a redirect instead, like you had before:
# return HttpResponseRedirect('/')
context['rr_form'] = record_form
return self.render_to_response(context)
Related
get the form and process it in post. It is necessary to save the unique uuid of the record to the model, I do it like this: formOne.save(related_uuid=related_uuid)
but doesn't work, the error is - save() got an unexpected keyword argument 'related_uuid'
models
class Orders(models.Model):
device = models.CharField(max_length=150)
uuid = models.CharField(max_length=22, blank=True)
views
class OrderAddView(TemplateView):
template_name = 'orders/order_add.html'
def get(self, request, *args, **kwargs):
context = super().get_context_data(**kwargs)
... some work code
return self.render_to_response(context)
def post(self, request, *args, **kwargs):
formOne = SimpleOrderAddForm(self.request.POST, prefix='one_form')
if formOne.is_valid():
related_uuid = shortuuid.uuid()
formOne.save(related_uuid=related_uuid)
return HttpResponseRedirect('orders_home')
else:
print('NotValid')
return self.form_invalid(formOne, **kwargs)
def form_invalid(self, formOne,, **kwargs):
context = self.get_context_data()
... some work code
return self.render_to_response(context)
assuming that "shortuuid.uuid()" return correct uuid; try
def post(self, request, *args, **kwargs):
formOne = SimpleOrderAddForm(self.request.POST, prefix='one_form')
if formOne.is_valid():
a=formOne.save(commit=False)
a.related_uuid=shortuuid.uuid()
a.save()
return HttpResponseRedirect('orders_home')
else:
print('NotValid')
return self.form_invalid(formOne, **kwargs)
I have a class based view which i am checking some conditions and redirecting to another page, i see the GET request to that page in the terminal and it returns 200, but it doesn't redirect to the page:
class CheckoutFinalView(CartOrderMixin, View):
def post(self, request, *args, **kwargs):
cart_obj, new_obj = Cart.objects.new_or_get(request)
order_obj = None
if cart_obj.items.count() == 0:
return redirect("carts:cart")
billing_profile, billing_profile_created = BillingProfile.objects.new_or_get(request)
has_card = False
if billing_profile is not None:
order_obj, order_obj_created = Order.objects.new_or_get(billing_profile, cart_obj)
order_obj.save()
has_card = billing_profile.has_card
is_prepared = order_obj.check_done()
user = User.objects.get(id=request.user.id)
if is_prepared:
print(is_prepared)
did_charge, crg_msg = billing_profile.charge(order_obj)
print(did_charge)
if did_charge:
order_obj.mark_paid() # sort a signal for us
request.session['cart_items'] = 0
del request.session['cart_id']
del request.session["order_id"]
if not billing_profile.user:
print("not billing profile user: ", billing_profile.user)
billing_profile.set_cards_inactive()
return redirect("checkout_final")
else:
print(crg_msg)
return redirect("checkout_final")
def get(self, request, *args, **kwargs):
return redirect("carts:success")
I tried return HttpResponseRedirect(reverse('carts:success')) , too. But it doesn't work, too.
I see the GET request to that page in the terminal and it returns 200.
That is perfectly normal, you only specify to redirect for a POST request, since you override def post.
You thus should implement it for a GET request with:
class CheckoutFinalView(CartOrderMixin, View):
def get(self, request, *args, **kwargs):
return redirect('carts:success')
def post(self, request, *args, **kwargs):
return redirect('carts:success')
Note that this view however does not do much, it simply redirects. In that case, you can make use of a RedirectView:
from django.views.generic import RedirectView
from django.urls import reverse_lazy
class CheckoutFinalView(RedirectView):
url = reverse_lazy('carts:success')
Can't figure out why CreateView doesn't return HttpResponse. For now, I use this view just for posting (no GET). I thought that set self.success_url should be enough (as you can see in def post).
class TripCreationView(CreateView):
form_class = TripCreationForm
template_name = 'frontend/homepage.html'
def post(self, request, *args, **kwargs):
self.success_url = request.POST.get('success_url') or reverse('frontend:homepage')
super(TripCreationView, self).post(self, request, *args, **kwargs)
#
# def form_valid(self, form):
# trip = form.save(self.request)
# return HttpResponseRedirect(self.success_url)
def get_form_kwargs(self):
kwargs = super(TripCreationView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
Do you know what to do?
You forgot a return statement.
def post(self, request, *args, **kwargs):
self.success_url = request.POST.get('success_url') or reverse('frontend:homepage')
return super(TripCreationView, self).post(self, request, *args, **kwargs)
Why would one use a Base view in Django when this
from django.http import HttpResponse
from django.views import View
class MyView(View):
def get(self, request, *args, **kwargs):
return HttpResponse('Hello, World!')
can be written as
def get(request):
return HttpResponse('Hello, World!')
What is the advantage of the Base view vs the function view?
All sorts of reasons.
You want to make use of a specialized view, like the TemplateView mentioned by #pythonista that makes it a lot easier for you to write your view. e.g.,
class MyTemplateView(TemplateView):
template_name = 'template.html'
You want to have some isolation when you have similar behavior. For example, you want a class-based View to handle both the form rendering and the form post:
class MyFormView(TemplateView):
template_name = 'form.html'
def get(self, request, *args, **kwargs):
return super(MyFormView, self).get(request, *args, **kwargs)
def post(self, request, *args, **kwargs:
value1 = request.POST.get('value1')
value2 = request.POST.get('value2')
# handle the post values
return super(MyFormView, self).get(request, *args, **kwargs)
You have REST endpoint and you’d like to isolate all of the code for GET/POST/PUT/DELETE in a single class-based view.
class RestEndpoint(View):
def __init__(self):
super(RestEndpoint, self).__init__()
self.model = MyModel
def get(request, n_id, *args, **kwargs):
x = self.model.objects.get(id=n_id)
return JsonResponse(x.to_json())
def put(self, request, *args, **kwargs):
data = json.loads(request.body)
x = self.model(**data)
x.save()
return JsonResponse(x.to_json())
def post(self, request, n_id, *args, **kwargs):
data = json.loads(request.body)
x = self.model.objects.get(id=n_id)
for key, value in data.items():
setattr(x, key, value)
x.save()
return JsonResponse(x.to_json())
def delete(self, request, n_id, *args, **kwargs):
self.model.objects.filter(id=n_id).delete()
return JsonResponse({})
You just prefer using classes instead of functions, e.g., so that you can create your own fun base views and reuse code with inheritance.
I have a FormView that generates a review of an object (which it is generically related to) and then links it to the object and saves it when the form is completed.
The issue I'm having is that I have no way to hold onto the data of the object I want to connect to. This means that I need to 'look it up' for context (template rendering) for valid processing (to do the linking) and for the success (to generate an appropriately reversed url.
Is there a better way to be binding the review to the object? Or better yet, is there a way to persist form data that I'm missing?
EDIT: Sorry the login decorator was on dispatch. I removed that method because SO was complaining about too much code and I didn't think it was relevant... I must have missed the decoratot
class ReviewCreate(FormView):
template_name = 'food/item_add_review.html'
form_class = ReviewForm
review_item = None
def get_context_data(self, **kwargs):
context = super(ReviewCreate, self).get_context_data(**kwargs)
item_modelname = self.kwargs.get('model')
item_model = apps.get_model('food',item_modelname)
review_item = get_object_or_404(item_model,pk=self.kwargs.get('pk'))
context['item'] = review_item
return context
def form_valid(self, form):
item_modelname = self.kwargs.get('model')
item_model = apps.get_model('food',item_modelname)
review_item = get_object_or_404(item_model,pk=self.kwargs.get('pk'))
r = form.save(commit=False)
r.content_object=review_item
r.save()
return super(ReviewCreate, self).form_valid(form)
def get_success_url(self):
item_modelname = self.kwargs.get('model')
item_model = apps.get_model('food',item_modelname)
review_item = get_object_or_404(item_model,pk=self.kwargs.get('pk'))
return reverse( 'pkitem', kwargs = {'pk': review_item.id, 'model':item_modelname},)
The view is an object right, so you just assign your values to instance variables, i e "to self" (this is thread-safe). Like this:
class ReviewCreate(FormView):
template_name = 'food/item_add_review.html'
form_class = ReviewForm
#method_decorator(login_required) # Use a class level mixin instead
def get_context_data(self, **kwargs):
return super(
ReviewCreate,
self
).get_context_data(
item=self.review_item,
**kwargs
)
def lookup_review_item(self):
self.item_modelname = self.kwargs.get('model')
item_model = apps.get_model('food', self.item_modelname)
self.review_item = get_object_or_404(
item_model,
pk=self.kwargs.get('pk')
)
def dispatch(self, request, *args, **kwargs):
# lookup performed here to be set for both GET and POST
self.lookup_review_item()
return super(ReviewCreate, self).dispatch(request, *args, **kwargs)
def form_valid(self, form):
r = form.save(commit=False)
r.content_object=self.review_item
r.save()
return super(ReviewCreate, self).form_valid(form)
def get_success_url(self):
return reverse(
'pkitem',
kwargs = {
'pk': self.review_item.id,
'model': self.item_modelname
},
)
The default form_valid() method for FormView redirects to the success url and reinitializes the form. You can make the form data persist by overriding form_valid():
def form_valid(self, form):
return super(YourFormView, self).get(form)
This will redirect to your success url with a (bounded) form having the posted data. The form is added to the context so you can use the data in your template or in your view as you wish.
(Django version 1.11.7)
The get_context_data should always return the context dictionary. It doesn't make sense to use the login_required decorator with it, because that means it might return a redirect response instead.
It would be better decorate the dispatch method instead. In your dispatch, you can set attributes on the instance.
class ReviewCreate(FormView):
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
self.item_modelname = self.kwargs.get('model')
self.item_model = apps.get_model('food',item_modelname)
self.review_item = get_object_or_404(item_model,pk=self.kwargs.get('pk'))
return super(ReviewCreate, self).dispatch(request, *args, **kwargs)
Then, in your other methods, you can access the attributes, for example:
def get_context_data(self, **kwargs):
context = super(ReviewCreate, self).get_context_data(**kwargs)
context['item'] = self.review_item
return context