Warm tip: This article is reproduced from serverfault.com, please click

How to pass active user to Django ModelForms

发布于 2020-11-28 02:06:57

In my form, I must filter the "tenant" choice field so that it displays only tenants associated to the active user's organization. So I'm simply passing an extra argument to the form constructor. This works as expected when I load (GET) the form.

But when I submit (POST) the form, I keep getting the following error:

AttributeError: 'NoneType' object has no attribute 'organization_profile'

Any idea what's provoking this?

Views.py

def create_booking(request):
    if request.method == "POST":
        form = BookingCreateForm(data=request.POST)
        if form.is_valid():
            data = form.cleaned_data
            booking = Booking.objects.create(
                status=data['status'],
                tenant = data['tenant'],
                apartment = data['apartment'],
                check_in = data['check_in'],
                check_out = data['check_out'],
                rent = data['rent'],
            )
            booking.save()

            return redirect('dashboard:dashboard')

    else:
        form = BookingCreateForm(user=request.user)
    return render(request, 'accounts/booking_form.html', {'form': form})

Forms.py

class BookingCreateForm(forms.ModelForm):
    class Meta():
        model = Booking
        fields = '__all__'
    
    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user',None)
        super(BookingCreateForm, self).__init__(*args, **kwargs)
        organization = self.user.organization_profile
        self.fields['tenant'] = forms.ChoiceField(choices=[
            (tenant.id, tenant.name)) for tenant in TenantProfile.objects.filter(organization=organization)
        ])

Models.py

class Booking(models.Model):
    STATUS_OPTIONS = [
        (0, 'Pending'),
        (1, 'Accepted'),
        (2, 'In-tenancy'),
        (3, 'Moved-out'),
        (4, 'Cancelled'),
    ]
    status = models.PositiveSmallIntegerField(choices=STATUS_OPTIONS, default=0)
    tenant = models.ForeignKey('TenantProfile', on_delete=models.CASCADE)
    apartment = models.ForeignKey('Apartment', on_delete=models.CASCADE)
    check_in = models.DateField(blank=False)
    check_out = models.DateField(blank=False)
    rent = models.DecimalField(max_digits=6, decimal_places=2, blank=False)
Questioner
Maxim Vallee
Viewed
0
Willem Van Onsem 2020-11-28 18:58:17

You need to pass the user in both the case of the GET request and the POST request, so:

def create_booking(request):
    if request.method == 'POST':
        form = BookingCreateForm(data=request.POST, user=self.request.user)
        # …

Unless howver the tenant of your Booking is a CharField, this will not work, you need to use a ModelChoiceField [Django-doc]. For example:

class BookingCreateForm(forms.ModelForm):
    class Meta:
        model = Booking
        fields = '__all__'
    
    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user',None)
        super().__init__(*args, **kwargs)
        organization = self.user.organization_profile
        self.fields['tenant'].queryset = TenantProfile.objects.filter(
            organization=organization
        )

Note: You can limit views to a view to authenticated users with the @login_required decorator [Django-doc].