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)
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].
Absolutely; I forgot to pass the request.user in form constructor for POST. Also, you were right about the second case scenario; the "tenant" field is a foreigkey field so I should have used self.fields['tenant'] = forms.ModelChoiceField(queryset=TenantProfile.objects.filter(organization=organization)) instead. I've updated my question to include my Models.py to make this clearer. If you want to include the forms.ModelChoiceField in your answer for people to get the full solution. Thanks!