I am getting the AttributeError 'NoneType' object has no attribute 'model'
First, let me explain I have 2 forms. The first form that the user encounters is the InitialSearchForm. It takes a date input, a selected object (instance of a trip) and an integer (for number of passengers). The POST request data from the InitialSearchForm is sent to the TripSelection view. This data is used to query the database and retrieve a queryset which will be used as a choice set in my 2nd form - DateChoiceForm - rendered in the HttpResponse.
The code below is for the TripSelection view in views.py. At the point that I initialise the DateChoiceForm, I pass the queryset as parameter 'trips':
class TripSelection(View):
""" A view to show results of search """
template = "bookings/trips_available.html"
def post(self, request):
"""
Takes the POST data from the InitialSearchForm and uses it to
initialise the DateChoiceForm before passing to the template
"""
form = InitialSearchForm(request.POST)
if form.is_valid():
destination_choice = request.POST.get("destination")
searched_date = request.POST.get("request_date")
passenger_total = int(request.POST.get("passengers"))
# Find trips with enough seats for requested no. of passengers
available_trips = Trip.objects.filter(
destination=destination_choice
).filter(seats_available__gte=passenger_total)
# Refine to trips with dates closest to searched_date
# limit to 3 results
gte_dates = available_trips.filter(
date__gte=searched_date
).order_by("date")[
:3
] # Returns trips that either match or are post- searched_date
lt_dates = available_trips.filter(date__lt=searched_date).order_by(
"-date"
)[
:3
] # Returns trips that are pre- searched_date
# Merge both queries
trips = gte_dates | lt_dates
naive_searched_date = datetime.strptime(searched_date, "%Y-%m-%d")
# Find trip closest to searched_date and make timezone naive
if gte_dates:
date_gte = gte_dates[0]
naive_closest_gte = self.timezone_naive(date_gte)
if lt_dates:
date_lt = lt_dates[0]
naive_closest_lt = self.timezone_naive(date_lt)
if (
naive_closest_gte - naive_searched_date
> naive_searched_date - naive_closest_lt
):
default_selected = date_lt
else:
default_selected = date_gte
else:
default_selected = date_gte
elif lt_dates:
date_lt = lt_dates[0]
naive_closest_lt = self.timezone_naive(date_lt)
default_selected = date_lt
else:
messages.error(
request,
"Sorry, there are no dates currently available for the"
"selected destination.",
)
form = DateChoiceForm(
trips=trips,
initial={
"trip_date": default_selected,
"num_passengers": passenger_total,
},
)
return render(request, self.template, {"form": form})
def timezone_naive(self, query_object):
""" Turns date attributes to a time-zone naive date """
date_attr = query_object.date
date_string = date_attr.strftime("%Y-%m-%d")
date_obj_naive = datetime.strptime(date_string, "%Y-%m-%d")
return date_obj_naive
In forms.py I set the queryset for the field 'trip' in the form's __init__ method:
class DateChoiceForm(forms.Form):
num_passengers = forms.IntegerField(widget=forms.HiddenInput())
trip = forms.ModelChoiceField(
queryset=None,
widget=forms.RadioSelect()
)
def __init__(self, *args, **kwargs):
trips = kwargs.pop('trips', None)
super(DateChoiceForm, self).__init__(*args, **kwargs)
self.fields['trip'].queryset = trips
The form renders as expected in the template but when I make a selection and click to 'submit' on the form, that is when I get the AttributeError.
This is caused in the view that handles the POST data for DateChoiceForm, specifically at the line form = DateChoiceForm(request.POST):
def trip_confirmation(request):
if request.method == "POST":
form = DateChoiceForm(request.POST)
if form.is_valid():
passengers = request.POST.get("num_passengers")
trip_choice = request.POST.get("trip")
print(trip_choice)
else:
print("Hello")
template = "bookings/confirm/trip.html"
context = {
"passengers": passengers,
"trip_choice": trip_choice
}
return render(request, template, context)
print("Error!").
If I understand correctly, this is because, having passed the queryset from the view and setting the queryset in the __init__ method, now every-time I call the DateChoiceForm it expects a parameter to be sent for the form to be initialised. Since I pass no parameter this time around, it reaches this line: trips = kwargs.pop('trips', None) and takes the fallback value of None.
I don't know how to go about setting the queryset for this form field since it is not a straightforward static filter that I can do from within forms.py. Is there another way of doing this? My only workaround at the moment is to pass the first form's post request data to session storage, retrieve it in the view and create the same queryset to pass as a parameter to DateChoiceForm(request.POST)