I am trying to refactor my code to inherit FormView instead of View. The view I'm working with receives values in the GET request. I retrieve the values in the get_context_data method and pass them through different functions to end up with a set of variables that I can pass in the context.
In short:
For the sake of example, the set of variables includes variables FOO and BAR. I need to initialise my form by passing variable FOO in the kwargs and additionally set my form field's initial value to BAR. I understand I should use the get_initial() and get_form_kwargs() methods to do this. I am just struggling with how to get FOO and BAR from the get_context_data method.
I tried adding FOO and BAR to the context dictionary:
context = super().get_context_data(**kwargs)
context["FOO"] = foo
context["BAR"] = bar
return context
And then calling it from the other methods:
def get_initial(self):
""" Get initial value for the form field """
initial = super(NameOfView, self).get_initial()
context = self.get_context_data()
initial_value = context["BAR"]
initial.update({'name': inital_value})
return initial
and the same for get_form_kwargs. But I get a RecursionError:
maximum recursion depth exceeded while calling a Python object
Any help understanding how I can acheive this will be appreciated
UPDATE: My Actual code is a bit more like this:*
class ConfirmTripView(FormView):
"""
Provides the user a set of choice options based on their search input in
the products.TripsView
"""
model = Booking
template_name = "bookings/trips_available.html"
form_class = DateChoiceForm
def __init__(self):
self.searched_date = None
self.passengers = None
self.destination_id = None
self.gte_dates = None
self.lt_dates = None
def convert_to_int(self, type_tuple):
""" Converts tuple value to integer """
type_int = int(''.join(type_tuple))
return type_int
def get_available_trips(self, destination, passengers):
""" Find trips with enough seats for searched no. of passengers """
available_trips = Trip.objects.filter(
destination=destination
).filter(seats_available__gte=passengers)
return available_trips
def get_trips_matched_or_post_date(self, date, destination, passengers):
"""
Returns trips that either match or are post- searched_date
Refine to trips with dates closest to searched_date
limit to 3 results
"""
available_trips = self.get_available_trips(destination, passengers)
gte_dates = available_trips.filter(date__gte=date)[:3]
return gte_dates
def get_trips_preceding_date(self, date, destination, passengers):
"""
Returns trips that are pre- searched_date
Refines to trips with dates closest to searched_date
limits to 3 results
"""
available_trips = self.get_available_trips(destination, passengers)
lt_dates = available_trips.filter(date__lt=date).order_by("-date")[:3]
return lt_dates
def make_timezone_naive(self, obj):
""" Turns date attribute to a time-zone naive date object """
date_attr = obj.date
date_string = date_attr.strftime("%Y-%m-%d")
datetime_naive = datetime.strptime(date_string, "%Y-%m-%d")
return datetime_naive
def get_trips_queryset(self, gte_dates, lt_dates):
""" Creates the queryset that will be used by the ModelChoiceField
in the DateChoiceForm """
# Merge both queries
trips = lt_dates | gte_dates
trips = trips.order_by('date')
return trips
def get_initial(self, **kwargs):
""" Takes values from get request and formulates variables
to be used in the form """
# Values from GET request
self.searched_date = self.request.GET.get('request_date')
self.passengers = self.request.GET.get('passengers')
self.destination_id = self.convert_to_int(
self.request.GET.get("destination")
)
# Return querysets for dates before/beyond searched_date respectively:
self.gte_dates = self.get_trips_matched_or_post_date(
self.searched_date,
self.destination_id,
self.passengers)
self.lt_dates = self.get_trips_preceding_date(
self.searched_date,
self.destination_id,
self.passengers)
naive_searched_date = datetime.strptime(self.searched_date, "%Y-%m-%d")
# Find the trip closest to the searched_date (for form initial value)
if self.gte_dates:
gte_date = self.gte_dates[0]
naive_gte_date = self.make_timezone_naive(gte_date)
if self.lt_dates:
lt_date = self.lt_dates[0]
naive_lt_date = self.make_timezone_naive(lt_date)
if (
naive_gte_date - naive_searched_date
> naive_searched_date - naive_lt_date
):
default_selected = lt_date
else:
default_selected = gte_date
else:
default_selected = gte_date
elif self.lt_dates:
lt_date = self.lt_dates[0]
default_selected = lt_date
else:
messages.error(
self.request,
"Sorry, there are no dates currently available for the"
"selected destination.",
)
# Get initial valuees for the form
initial = super(ConfirmTripView, self).get_initial()
initial.update({'trip': default_selected})
return initial
def get_form_kwargs(self, **kwargs):
""" Provides keyword arguemnt """
kwargs = super(ConfirmTripView, self).get_form_kwargs()
trips = self.get_trips_queryset(self.gte_dates, self.lt_dates)
kwargs.update({'trips': trips})
return kwargs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
destination = Product.objects.filter(id=self.destination_id)
context["passengers"] = self.passengers
context["destination_obj"] = destination
return context
def form_valid(self, form):
"""
Takes the POST data from the DateChoiceForm and creates an
Intitial Booking in the database
"""
booking = form.save(commit=False)
booking.status = "RESERVED"
booking.save()
trip = form.cleaned_data['trip']
destination = trip.destination
booking_line_item = BookingLineItem(
booking=booking,
product=destination,
quantity=self.request.GET.get("passengers")
)
booking_line_item.save()
return redirect('create_passengers', booking.pk)
First of all, bookmark this.
Second, get_initial() and get_context_data() solve 2 different problems:
As you can see in above site, the form is injected into the template variables through
get_context_data()and that's where your recursion problem comes from:Now, how your GET parameters and form should be working together is unclear from your question, but if you need some values from GET for initial form values, then get them inside
get_initial().UPDATE:
get_queryset()with a signature like this, reason is that several views dealing with models also have aget_queryset()method with a different signature.get_trips()in this context makes a lot of sense__rangelookups which probably makes your logic easier. Change the semantics to "find trips between 30 days before and 30 days after search date". This is not the same but a good enough approach in practical terms.If you're still stuck, let us know on what specifically.