The objective is simple: I'm building a car rental platform where customers can place an order for a car. The simple 'order' contains the car, start, and end-dates. The form should automatically save the authenticated user as the creator.
It uses a CreateView with this code:
class BookingCreate(CreateView):
model = Booking
fields = ['car', 'start_date', 'end_date']
permission_classes = [permissions.IsAuthenticated]
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
The form works fine, but when submitted, it raises this error:
ValueError at /rentals/booking/create Cannot assign "<SimpleLazyObject: <CustomUser: Test2 Two>>": "Booking.user" must be a "Customer" instance.
I've looked up previous answers, and the best solution came from this thread, which recommended using this code instead
def form_valid(self, form):
form.instance.user = Booking.objects.get(user=self.request.user)
return super().form_valid(form)
However, this change returns a slightly different error:
ValueError at /rentals/booking/create Cannot query "Test2 Two": Must be "Customer" instance. I have a customUser Model and a "customer" model that inherits from it.
For additional context, I am using a customUser model. Because I have multiple user types (in particular (car) Owners and Customers), I use a special table with boolean fields to mark each type as True based on the registration form they use per this this tutorial.
Here's the relevant code (there's a lot, so I've only added the relevant parts):
models.py
from accounts.models import CustomUser
class User(CustomUser):
is_customer = models.BooleanField(default=False)
is_owner = models.BooleanField(default=False)
class Owner(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
def __str__(self):
return self.user.first_name
class Customer(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
cars = models.ManyToManyField('Car', blank=True)
...
def get_absolute_url(self):
return reverse('rental:customer-detail', args=[str(self.id)])
def __str__(self):
return f'{ self.user.first_name } { self.user.last_name }'
class Booking(models.Model):
"""Stores the bookings, for example when it was made, the booking date, and the car ID."""
# Unique ID for this booking.
id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text="Automatically generated unique ID. Do not change.")
user = models.ForeignKey('Customer', on_delete=models.SET_NULL, null=True)
car = models.ForeignKey(Car, on_delete=models.SET_NULL, null=True)
start_date = models.DateField(default=timezone.now)
end_date = models.DateField(null=True)
...
Solution and extra resources for future explorers
Both answers below helped, but the issue kept coming back. The permanent solution was this line of code:
def form_valid(self, form):
form.instance.created_by = Customer.objects.get(pk=self.request.user.pk)
return super().form_valid(form)
As alluded to in the question above, this is a common problem. So here are three other threads to look at, each with great answers:
Cannot assign "<SimpleLazyObject: <User: XXX>>": "Comment.user" must be a "MyProfile" instance
Cannot assign "<SimpleLazyObject: <User: JohnDoe12>>": "Profile.user" must be a "User" instance
CodePudding user response:
I'd recommend you to use get_object_or_404()
and you need to assign user
not Booking
instance so:
def form_valid(self, form):
form.instance.user=get_object_or_404(Booking,user=self.request.user).user
return super().form_valid(form)
CodePudding user response:
Your self.request.user (or the user you're logged in as) seems to be the CustomUser instance, but you're trying to assign the User instance to the Booking. Though it has the same fields (as it inherits from CustomUser class), it is a different object.
I think you want to make the CustomUser an abstract model? You should add abstract to the CustomUser class. See: https://docs.djangoproject.com/en/4.1/topics/db/models/#abstract-base-classes
class Meta:
abstract = True
Then you would also need to select the correct user model in you'r settings like: AUTH_USER_MODEL = 'users.User' for example
https://docs.djangoproject.com/en/4.1/topics/auth/customizing/#substituting-a-custom-user-model
Also in the second option you're assigning the Booking instead of the related user. Do this instead:
def form_valid(self, form):
form.instance.user = Booking.objects.get(user=self.request.user).user
return super().form_valid(form)