How to send cookies in CreateView? - django

I am trying to implement signed cookie sending in CreateView, but I have encountered with the trouble. Following code works in UpdateView but in CreateView we dont have self.object in render_to_response method and basically we cant get a pk there or at least I dont know how to do it.
Question is how to get pk or id of a freshly created object or maybe alternatively in which method I could move my code to get access to pk from there?
Thanks.
def render_to_response(self, context, **response_kwargs):
response = CreateView.render_to_response(self, context, **response_kwargs)
existing_allowed_comments = self.request.get_signed_cookie('allowed_comments', default=None)
if not self.request.user.is_authenticated:
if existing_allowed_comments and str(self.object.pk) not in \
existing_allowed_comments:
response.set_signed_cookie('allowed_comments',
", ".join([existing_allowed_comments, str(self.object.pk)])
elif not existing_allowed_comments:
response.set_signed_cookie('allowed_comments', self.object.pk
return response
method should add pk of created objects to signed cookies in case user is not authenticated.
self.get_object() doesn't work as well – 404

You may be better to override the form_valid() method for this. This method creates an object from the validated form data so you will have access to self.object after calling the method in the base class:
def form_valid(self, form):
response = super().form_valid(form)
existing_allowed_comments = self.request.get_signed_cookie('allowed_comments', default=None)
if not self.request.user.is_authenticated:
if existing_allowed_comments and str(self.object.pk) not in \
existing_allowed_comments:
response.set_signed_cookie('allowed_comments',
", ".join([existing_allowed_comments, str(self.object.pk)])
elif not existing_allowed_comments:
response.set_signed_cookie('allowed_comments', self.object.pk
return response
Note that if the form is not valid because of bad data, no object will be created and this method will not get called.

Related

How to update foreignkey field manually in UpdateView post method?

I am trying to manually change a foreign key field (Supplier) of a model (Expenditure). I override the UpdateView post method of Expenditure and handle forms for other models in this method too. A new SupplierForm is also rendered in this view and I am tracking if this form is changed via has_changed() method of the form. If this form has changed, what I ask is overriding the related_supplier field of ExpenditureForm and picking newly created Supplier by this statement:
if supplier_form_changed:
new_supplier = related_supplier_form.save(commit=False)
new_supplier.save()
....
# This statement seems to have no effect
self.object.related_supplier = new_supplier
I override the post method with super(), so even though I explicitly state save() method for all related forms, however I don't call the save method of main model (Expenditure) since it is already handled after super(). This is what start and end of my method looks like;
def post(self, request, *args, **kwargs):
context = request.POST
related_receipt_form = self.receipt_form_class(context, request.FILES)
related_supplier_form = self.supplier_form_class(context, request.FILES)
self.object = self.get_object()
related_receipt = self.object.receipt
related_supplier_form = self.supplier_form_class(context)
expenditure_form = self.form_class(context)
inlines = self.construct_inlines()
....
return super().post(self, request, *args, **kwargs)
You may find the full code of my entire view here:
https://paste.ubuntu.com/p/ZtCfMHSBZN/
So my problem is self.object.related_supplier = new_supplier statement does not have any effect. After the update, old related_supplier object is still there, new one is saved but not attached to the updated Expenditure. Strange thing is I am doing a similar thing in the same view (also in CreateView) with receipt and no problem whatsoever.
I debugged the code via PyCharm, before the execution of super(), I can confirm that self.object.related_supplier is the newly created one, but when the super() executed, it returns back to the original supplier object.
you can override the form valid method to add things manually, an example shown below
def form_valid(self, form):
related_supplier_form.instance.related_supplier = new_supplier
valid_data = super(UpdateView, self).form_valid(form)
return valid_data

automatically saved value in a UpdateView Django

i'm new in Django and i'm learning about the views and the methods and how they work, especially with this problem. The thing is that I would like to know how to automatically save a value of a field in my model after updating an object in a UpdateView, for example when I update an object, in this case a report where I can assign a person to do it, I would like to save a model value that shows the "status" and save the value of "assigned" or something like that, to know if the report was already assigned or not. I know there are methods and that maybe one of them could be done by overwriting the class, but I do not know how to apply it or which one to use.
For help this is a simple class of a UpdateViews that i'm using:
class reporteupdate(UpdateView):
model = reporte_fallo
form_class = ReporteAsignar
template_name = 'formulario/jefe_asignar.html'
success_url = reverse_lazy('formulario:reporte_listar_jefe')
and the field of the model that I would like to assign a value to is called status.
i'm waiting for your help, since I'm stuck with that doubt. Thanks!!!
the query dict will be changable after you create a copy of it in post method so you can do this:-
class SomeUpdateView(UpdateView):
model=your model
form_class=you form
def post(self, request, **kwargs):
request.POST = request.POST.copy()
request.POST['status'] = 'Assigned'
return super(SomeUpdateView, self).post(request, **kwargs)
You could perhaps set the status flag after the form has been successfully validated, by overriding the form_valid() method in your reporteupdate view:
class reporteupdate(UpdateView):
...
def form_valid(self, form):
# Call super() to save the model and return the success url
resp = super().form_valid(form)
# Set your status flag
self.object.status = 'assigned'
self.object.save()
return resp

Django Detailview Error Handling

I have a scenario where I am trying to send different messages to the user in the event that they are not authorized to see a record or if the record does not exist. I have been able to get the interface to send a message to the user if a 404 occurs, but can't quite figure out the logic based on criteria of the record. I've been playing with get, get_object, or get_object_or_404 and nothing quite works.
Here is my DetailView..
class BookSearchDetailView(LoginRequiredMixin,DetailView):
model = Book
context_object_name = 'book_detail'
template_name = 'book/book_search_detail.html'
def get_object(self, queryset=None):
return get_object_or_404(Book, book_number=self.request.GET.get("q"))
def get(self, request, *args, **kwargs):
book_number=self.request.GET.get("q")
try:
self.object = self.get_object()
except Http404:
messages.add_message(self.request, messages.INFO, 'Book Number %s Not Found' % book_number )
return HttpResponseRedirect(reverse('Book:book_number_search'))
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
except Not a Preference:
Book_number exists but is not self.request.user.preferences.all()
I realize the except Not a Preference doesn't work, but I am illustrating what I am trying to do.
Here is my model.
class Book(models.Model):
book_name = models.CharField(max_length=80,null=True,unique=False)
book_number = models.IntegerField(editable=True,null=True)
Note I do not have a slug defined to my model. In the current code above, this doesn't cause any problems. When I have played with different code variations, one of the error messages I get is that DetailView must be called with a pk or a slug.
I am ultimately trying to filter the book so that if the user doesn't have attributes to allow them to see the book it would essentially return none.
I have also tried to override get_queryset, but when I use the code below,
def get_queryset(self):
queryset = super(BookSearchDetailView, self).get_queryset()
return queryset.filter(book_number__in=self.request.user.preferences.all())
However, when I define the code above, my DetailView does not seem to honor it or have any effect.
What I've read is that the get_object either gets the object or 404. I've got that piece work working fine. Just can't figure out how to incorporate a third variation of error message.
Thanks in advance for any thoughts.

Django FormView success_url parameter advice

I am converting an old project with Class Views and I want to know if the pattern I am using is "safe".
In brief I have a class View like this (code simplified ):
class FileAddHashedView(FormView):
"""This view hashes the file contents using md5"""
form_class = FileUploadForm
success_url = reverse_lazy('vault-show')
template_name = "vault/add.html"
filebox_random= 0
def get_success_url(self):
return reverse('vault-show', kwargs={'random': self.filebox_random})
def form_valid(self, form):
instance = form.save(commit=False)
#generate a random number
rng = random.SystemRandom()
#TODO: must catch exception here
instance.random=rng.randint(0, sys.maxint)
instance.save()
#TODO: check what is the proper way to generate the parametric URL
self.filebox_random=instance.random
messages.success(
self.request, 'File hashed and uploaded!', fail_silently=True)
return super(FileAddHashedView, self).form_valid(form)
def form_invalid(self, form, **kwargs):
messages.error(self.request, 'Upload failed ...', fail_silently=True)
return super(FileAddView, self).form_invalid(form)
And the corresponding views that gets activated:
class VaultStatus(TemplateView):
template_name = 'vault/vault.html'
def get(self, request,random):
# retrieve object info
data = {
'filebox': FileBox.objects.get(random=random)
}
return render(request,self.template_name,data)
#return render_to_response('vault/vault.html', {'random': random});
Now my question is: is there a better way in get_success_url to get the self.filebox_random without relying on a class attribute?
Is this thread-safe?
You're not using a class attribute here. Even though you've defined filebox_random at class level, by setting a value to that name within an instance method in Python you're actually creating an instance attribute with the same name, which hides the class-level one. (Although I'm not actually sure why you're defining the class attribute in the first place).
Django does quite a lot of work behind the scenes to ensure that class-based views are thread-safe, so there is no danger in setting instance attributes.

how to alter/change the post data in django UpdateView

I am using django update view for my model/records editing stuff like below
class EditProductView(LoginRequiredMixin, UpdateView):
model = Product
def get_template_names(self):
return ['website/product/edit_product.html']
def get_success_url(self):
return reverse('product_details', args=[self.kwargs['pk']])
def get_context_data(self, **kwargs):
publisher = Publisher.objects.get(product__id=self.kwargs['pk'])
context = super(EditProductView, self).get_context_data(**kwargs)
context.update(
{
'publisher':publisher,
}
)
return context
edit_product = EditProductView.as_view()
So what all i want/trying to do is alter(add some data, edit already submitted data according to website functionality) the POST data before submitting to form,
So i know that UpdateView has some method def def post(self, request, *args, **kwargs): , but i dont know exactly how to use it
Suppose below is the request.POST data i am getting
<QueryDict: {u'product_name': [u'Biscuit'], u'product_price': [u'1000'], u'product_tag': [u'']}>
So now i want to alter the above QueryDict and if the value of product_tag was empty i need assign some default one and submit with latest querdict
Also i know that Querydict is mutable, but because of sure i need to edit the POST data, before submitting/saving to database, i need to make that querydict as dict, then edit it, and convert back to querdict
So after all whats my question is
How can we alter the POST data in UpdateView before submitting/saving to database
Is the post method heplful?
The QueryDict is mutable after you create its .copy(). See the docs.
Update Example:
class SomeUpdateView(UpdateView):
def post(self, request, **kwargs):
request.POST = request.POST.copy()
request.POST['some_key'] = 'some_value'
return super(SomeUpdateView, self).post(request, **kwargs)
Here is much broader discussion about the topic.
Furthermore, shouldn't this be done in ModelForm subclass? You're certainly aware you can set custom form as a form_class in UpdateView. Such a logic usually needs unit tests and it's much easier to unit test logic which sits in the form.