首页 > 解决方案 > 'NoneType' 对象在检索 POST 请求数据时没有属性 'model' 错误

问题描述

我得到 AttributeError 'NoneType' 对象没有属性 'model'

首先,让我解释一下我有两种形式。用户遇到的第一个表单是 InitialSearchForm。它需要一个日期输入、一个选定对象(旅行实例)和一个整数(乘客人数)。InitialSearchForm 中的 POST 请求数据被发送到 TripSelection 视图。此数据用于查询数据库并检索查询集,该查询集将用作我的第二种形式 - DateChoiceForm - 在 HttpResponse 中呈现的选择集。

下面的代码适用于views.py中的 TripSelection 视图。在初始化 DateChoiceForm 时,我将查询集作为参数“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

forms.py 中,我在表单的 __init__ 方法中为字段“trip”设置了查询集:

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

表单在模板中按预期呈现,但是当我做出选择并单击表单上的“提交”时,我得到了 AttributeError。这是在处理 DateChoiceForm 的 POST 数据的视图中引起的,特别是在以下行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!").   

如果我理解正确,这是因为从视图传递了查询集并在 __init__ 方法中设置了查询集,现在每次我调用 DateChoiceForm 时,它都希望发送一个参数来初始化表单。由于这次我没有传递任何参数,所以它到达这一行:trips = kwargs.pop('trips', None)并采用 None 的后备值。

我不知道如何为这个表单字段设置查询集,因为它不是一个简单的静态过滤器,我可以在 forms.py 中完成。还有另一种方法吗?我目前唯一的解决方法是将第一个表单的发布请求数据传递给会话存储,在视图中检索它并创建相同的查询集作为参数传递给 DateChoiceForm(request.POST)

标签: pythondjangoattributeerror

解决方案


You can't use the except clause like that except (ValueError, TypeError, self.queryset.model.DoesNotExist). Use the actual model DoesNotExist instead: except (ValueError, TypeError, <modelname_here>.DoesNotExist).

Also, you can't set the queryset=None. That's not possible. You must use the <model>.objects.none() queryset instead.

If you want dynamic model selection for your view I recommend using class-based views instead. The inheritance makes it way easier to handle multiple model types.


Edit: after reading your question again I noticed that trip_dates = kwargs.pop('trips', None) actually retrieves 'trips' and not 'trip' is this intentional? Given your post variables trips will be equal to None.

Second: You don't actually set a queryset. self.fields['trip'].queryset = trip_dates will be either None or list of strings but never an actual queryset. So regardless of whether setting the queryset for the ModelChoiceField in the __init__ it's never a queryset.


推荐阅读