Success message not displayed - python

Don't see any SuccessMessage. Anyone can tell me why I don't get any success message once I successfully created the database entry?
class TicketCreate(AdminPermissionRequiredMixin, SuccessMessageMixin, FormValidationMixin, BaseTicketView, TemplateView):
template_name = 'tickets/admin/create.html'
success_message = _("Ticket has been successfully created.")
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['ticket_form'] = self.ticket_form
context['tax_form'] = self.tax_form
return context
#cached_property
def ticket_form(self):
return TicketForm(data=self.request.POST or None, event=self.request.event)
#cached_property
def tax_form(self):
return TicketTaxForm(prefix='tax', data=self.request.POST or None)
#transaction.atomic
def post(self, request, *args, **kwargs):
if self.ticket_form.is_valid() and self.tax_form.is_valid():
tax_instance = self.tax_form.save(commit=False)
tax_choice = self.tax_form.cleaned_data.get('tax_choice')
new_tax = (tax_choice == TicketTaxChoice.NEW_TAX and
tax_instance.name and tax_instance.percentage)
# save the tax instance
if new_tax:
tax_instance.event = self.request.event
tax_instance.save()
# save the ticket instance
ticket_instance = self.ticket_form.save(commit=False)
ticket_instance.event = self.request.event
if new_tax:
ticket_instance.tax = tax_instance
self.ticket_form.save()
return redirect(
'tickets:admin:detail',
self.request.organizer.slug,
self.request.event.slug,
ticket_instance.pk
)
return super().get(request, *args, **kwargs)

From the official docs:
Adds a success message attribute to FormView based classes
I can't see your other classes, but from what I see your class is based on TemplateView, which is not derived from FormView.
Looking the source you can see that message is shown when form_valid is called, so your class should at least specify form_class or model field. But you are calling is_valid() manually, so form_valid is not called and message is not shown.

Related

Saving in django forms using FormView

I'm creating a form that will change the state of reserve book, I have this
class LibraryReserveForm(CrispyFormMixin, forms.Form):
def __init__(self, *args, **kwargs):
self.manager = kwargs.pop('manager')
super(LibraryReserveForm, self).__init__(*args, **kwargs)
def save(self):
self.instance.reserve_status = 'approved'
self.instance.save()
return self.manager
models.py
class ReservedBooks(TimeStampedModel):
BOOK_RESERVE_STATUS = Choices(
('for_approval', "For Approval"),
('approve', "Approved"),
('cancelled', "Cancelled"),
('rejected', "Rejected")
)
reserve_status = models.CharField(
_('Status'),
max_length=32,
choices=BOOK_RESERVE_STATUS,
default='for_approval'
)
...
view
class LibraryReserveView(
ProfileTypeRequiredMixin,
MultiplePermissionsRequiredMixin,
FormView,
):
model = ReservedBooks
template_name = 'library/reserved_list.html'
form_class = LibraryReserveForm
def get_form_kwargs(self):
kwargs = super(LibraryReserveView, self).get_form_kwargs()
kwargs.update({
'manager': self.request.user.manager,
})
return kwargs
urls
url(
r'^reserve/(?P<pk>\d+)/$',
views.LibraryReserveView.as_view(),
name='reserved'
),
everytime I submit the button I print something in the save() method of the forms but its not printing something therefore that method is not called. How do you called the save method ? Thanks
A FormView does not handle saving the object. It simply calls form_valid that will redirect to the success_url. But an UpdateView adds boilerplate code to pass the instance to the form, and will save the form.
You work with a Form, but a Form has no .instance attribute. A ModelForm has, so it might be better to use a ModelForm here:
class LibraryReserveForm(CrispyFormMixin, forms.ModelForm):
def __init__(self, *args, **kwargs):
self.manager = kwargs.pop('manager')
super(LibraryReserveForm, self).__init__(*args, **kwargs)
def save(self, *args, **kwargs):
self.instance.reserve_status = 'approved'
return super().save(*args, **kwargs)
Then we can make use of an UpdateView:
from django.views.generic import UpdateView
class LibraryReserveView(
ProfileTypeRequiredMixin,
MultiplePermissionsRequiredMixin,
UpdateView
):
model = ReservedBooks
template_name = 'library/reserved_list.html'
form_class = LibraryReserveForm
success_url = …
def get_form_kwargs(self):
kwargs = super(LibraryReserveView, self).get_form_kwargs()
kwargs.update(
manager=self.request.user.manager
)
return kwargs
You still need to specify the sucess_url here: the URL to which a successful POST request will redirect to implement the Post/Redirect/Get pattern [wiki].

Django: Different views for POST and GET - Form validation errors

I have a DemandDetailView(DetailView) and BidCreationView(CreateView).
On DemandDetailView page, there is a form (for creating Bids) which posts data to BidCreationView.
I can't figure out what to do in case form is invalid. I would like to render DemandDetailView again with form errors and preserve corresponding URL.
class DemandDetailView(DetailView):
model = Demand
template_name = 'demands/detail.html'
def dispatch(self, request, *args, **kwargs):
self.bid_creation_form = BidCreationForm(request.POST or None, request.FILES or None,request=request)
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['bid_creation_form']=self.bid_creation_form
return context
class BidCreationView(CreateView):
http_method_names = ['post']
model = Bid
form_class = BidCreationForm
def get_success_url(self):
return reverse_lazy("demands:demands")
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({'request': self.request})
return kwargs
def form_valid(self, form):
form.instance.demand_id = self.kwargs.pop('demand_id')
return super().form_valid(form)
Do you have any ideas? My only idea is to use session which isn't probably the best way.
You could use is_valid() method from Form Objects. Something like:
class DemandDetailView(DetailView):
model = Demand
template_name = 'demands/detail.html'
def dispatch(self, request, *args, **kwargs):
form = BidCreationForm(request.POST or None, request.FILES or None,request=request)
if form.is_valid():
self.bid_creation_form = form
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['bid_creation_form']=self.bid_creation_form
return context
Option #2 (Personal Choice):
forms.py
from django import forms
from .models import Bid
class BidCreationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(BidCreationForm, self).__init__(*args, **kwargs)
for field in self.fields.values():
field.widget.attrs = {"class": "form-control"}
class Meta:
model = Bid
fields = ('user', 'demands', 'amount', 'transaction')
Take a look at the Meta class within the Form. Its explicitly calling the Bid Model and the fields attribute is referring Bid fields from the Model Instance. Now you could call this form in any view without calling another view. If you want to add logic to this form, like calculating total amount or something like that then should do it within the form also. Write code once, Dont Repeat yourself.

django render and validate formset in class based-view (ListView)

I have the following class based view that I want to use to render a formset and validate it when it gets submitted through a post method:
The formset renders perfectly. When I submit the form I can read the formset and check it for errors. in the post method of this class -> errors = backorder_formset.errors
If I find any errors in the formset, I would like to render the view, but this time with the formset instance, that I read from POST.
When I call ctx = self.get_context_data() form within the post method of the class the following error gets raised from the call super(MissingProductsListView, self).get_context_data(*args, **kwargs):
'MissingProductsListView' object has no attribute 'object_list'
It seems like the superclass of Listview performs this call:queryset = kwargs.pop('object_list', self.object_list)
My question is why am I running in this error? and how could I render this formset with its errors messages to display it in the template after it was posted? I am using Django 1.9.9
class MissingProductsListView(generic.ListView):
template_name = 'dashboard/purchaseorder/missing_products.html'
context_object_name = 'backorders'
model = BackOrder
def post(self, request, *args, **kwargs):
backorder_formset = BackOrderFormset(request.POST)
errors = backorder_formset.errors
if backorder_formset.is_valid():
# <process form cleaned data>
return HttpResponseRedirect('/success/')
else:
ctx = self.get_context_data()
return self.render_to_response(ctx)
def accumulate_identical_products_from_backorders(self, back_order_list):
... some code
return sorted_accumulated_dict.values()
def get_context_data(self, *args, **kwargs):
ctx = super(MissingProductsListView, self).get_context_data(*args, **kwargs)
ctx['title'] = _("Missing Products")
if self.request.POST:
ctx['back_order_formset'] = BackOrderFormset(self.request.POST)
else:
accumulated_backorders_per_product = self.accumulate_identical_products_from_backorders(BackOrder.objects.all())
back_orders = BackOrderFormset(initial=[{'product_id': backorder_dict['product_id'],
'product': backorder_dict['title'],
'quantity': backorder_dict['quantity']} for backorder_dict in
accumulated_backorders_per_product])
ctx['back_order_formset'] = back_orders
return ctx
def get_queryset(self):
.. some code
return backorder_list
Look here:
class BaseListView(MultipleObjectMixin, View):
"""
A base view for displaying a list of objects.
"""
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
allow_empty = self.get_allow_empty()
if not allow_empty:
# When pagination is enabled and object_list is a queryset,
# it's better to do a cheap query than to load the unpaginated
# queryset in memory.
if self.get_paginate_by(self.object_list) is not None and hasattr(self.object_list, 'exists'):
is_empty = not self.object_list.exists()
else:
is_empty = len(self.object_list) == 0
if is_empty:
raise Http404(_("Empty list and '%(class_name)s.allow_empty' is False.") % {
'class_name': self.__class__.__name__,
})
context = self.get_context_data()
return self.render_to_response(context)
Basically - you missed this part in the POST handler:
self.object_list = self.get_queryset()
And to be honest - I am not quite sure if this is a good idea to add post to the generic ListView in django. It looks more like FormView - but I can be wrong here.

Python edit form data prior to django validation

I'm fairly new to python and have been searching for awhile to find how I can edit form data BEFORE all the standard python form/field validators do their magic.
I have a model form with an IntegerField which I'd like to remove the "$" and commas from (using some sort of custom validation), then let the normal to_python() validate() etc do their thing.
My code is below - any help would be much appreciated!
forms.py
class BuyerSettingsForm(forms.ModelForm):
total_offer_limit = forms.IntegerField(required=False, max_value=10000000, min_value=0)
def __init__(self, request, *args, **kwargs):
super(BuyerSettingsForm, self).__init__(*args, **kwargs)
class Meta:
model = Buyer
fields = ['total_offer_limit']
def save(self, commit=True):
profile = super(BuyerSettingsForm, self).save(commit=commit)
profile.total_offer_limit = self.cleaned_data['total_offer_limit']
profile.save()
return profile
views.py
class SettingsPreferences(LoginRequiredMixin, BuyerAccessRequiredMixin, BuyerAdminAccessRequiredMixin, UpdateView):
template_name = 'invoicely/buyer/settings/buyer_settings.html'
form_class = BuyerSettingsForm
success_url = reverse_lazy('settings_preferences')
def get_object(self, *args, **kwargs):
return self.request.user.profile.buyer
def get_initial(self):
ctx = super(SettingsPreferences, self).get_initial()
ctx.update({
'total_offer_limit': self.object.total_offer_limit,
})
return ctx
def get_form_kwargs(self):
kwargs = super(SettingsPreferences, self).get_form_kwargs()
kwargs['request'] = self.request
return kwargs
def form_valid(self, form):
self.object = form.save()
messages.add_message(self.request, messages.SUCCESS, "Settings successfully updated")
return super(SettingsPreferences, self).form_valid(form)
If you are already overloaded get_form_kwargs you can do this. This is data which your form will be initialized with. So we can edit it before its initialization.
class SettingsPreferences(LoginRequiredMixin, BuyerAccessRequiredMixin, BuyerAdminAccessRequiredMixin, UpdateView):
...
def get_form_kwargs(self):
kwargs = super(SettingsPreferences, self).get_form_kwargs()
kwargs = copy.deepcopy(kwargs)
kwargs['request'] = self.request
if self.request.method in ('POST', 'PUT'):
# here put your data editing code
kwargs['data']['total_offer_limit'] = int(kwargs['data']['total_offer_limit'].strip().replace('$', ''))
return kwargs

Persist Django FormView Data

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

Categories