Issue
It's my first time asking question here :) I'm a beginner with Django and I've been stuck on my edit_booking method for days, I don't even know what I'm doing wrong anymore...
I'm making a booking system and I'm getting a "TypeError at /edit-booking/6 __init__() got an unexpected keyword argument 'id'"
. I've been trying a lot of things to make editing the exact booking possible, but they only cause more errors...
Views:
class EditBooking(DetailView):
def __init__(self):
template_name = 'edit-profile.html'
initial = {}
@login_required
def edit_booking(request, id):
booking = get_object_or_404(Booking, id=id)
if request.method == 'POST':
form = BookingForm(request.POST, instance=booking)
customer_data = UserProfile.objects.all()
if form.is_valid():
instance = form.save(commit=False)
instance.customer_data = customer_data
instance.save()
messages.success(request, 'Thank you! Your booking has been updated!')
else:
return messages.error(request, form.errors)
else:
form = BookingForm(initial={
'booking_date': booking.booking_date,
'booking_time': booking.booking_time,
'table_size': booking.table_size,
'additional_info': booking.additional_info,
})
return render(request, template_name, {'form': form})
Templates:
profile.html button triggering edit page:
<a href="{% url 'editbooking' id=booking.id %}" aria-label="edit booking">
<button type="button" class="btn btn-secondary btn-brown px-5">Edit</button>
</a>
edit_booking.html:
<form method="POST" action="{% url 'editbooking' id=booking.id %}"
class="col-6">
{% csrf_token %}
{% crispy form %}
</form>
Urls:
path('edit-booking/<int:id>', views.EditBooking, name='editbooking'),
Models:
class Booking(models.Model):
booking_customer = models.ForeignKey(UserProfile, on_delete=models.CASCADE, null=True)
booking_date = models.DateField(default=datetime.date.today)
booking_time = models.CharField(choices=TIME_SLOTS, default='8:00 - 8:30', max_length=50)
table_size = models.CharField(choices=TABLE_SIZE, default='1', max_length=50)
additional_info = models.TextField(max_length=400, null=True, blank=True)
booked_on = models.DateTimeField(auto_now_add=True)
is_confirmed = models.CharField(choices=CONFIRMATION, default='Awaiting confirmation', max_length=50)
slug = AutoSlugField(max_length=70, unique=True, null=True)
class Meta:
ordering = ["booked_on"]
def __str__(self):
return f'Booking for {self.booking_date} at {self.booking_time} was booked on {self.booked_on} and currently has a status: {self.is_confirmed}'
Forms:
class BookingForm(ModelForm):
"""
Provides necessary fields to the booking form for customers
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Crispy form helpers
self.helper = FormHelper()
self.helper.form_method = 'post'
self.helper.add_input(Submit('submit', 'Submit', css_class='btn btn-secondary btn-brown mb-4 mx-auto px-5'))
# Provides a date widget to the form
booking_date = forms.DateField(widget=forms.DateInput(attrs={'class':'form-control', 'type':'date', 'value': datetime.date.today}), required=False)
booking_time = forms.ChoiceField(choices=TIME_SLOTS, required=False)
table_size = forms.ChoiceField(choices=TABLE_SIZE, required=False)
additional_info = forms.CharField(max_length=400, widget=SummernoteWidget(), required=False)
booked_on = forms.DateTimeField(initial=datetime.datetime.now, widget=forms.HiddenInput(), required = False)
slug = AutoSlugField(max_length=70, unique=True, populate_from=lambda instance: instance.title,
unique_with=['booked_on', 'booking_date'],
slugify=lambda value: value.replace(' ','-'))
# Provides a model to pull the fields from
class Meta:
model = Booking
fields = ['booking_date', 'booking_time', 'table_size', 'additional_info']
read_only = ['booked_on', 'slug']
# Prevents booking dates in the past
def save_booking(self, *args, **kwargs):
data = self.cleaned_data
if data.get('booking_date') < datetime.date.today():
raise ValidationError("The date cannot be in the past!")
else:
super(Booking, self).save(*args, **kwargs)
I was trying to use slug, pk and id to get the exact booking, I changed the urls and templates to display the id, used Booking.objects.get() and swapped it in the end with get_object_or_404, getting rid of init etc... I've been searching through similar questions here, but none of the solutions that i've tried worked for me really :(
Solution
There are several problems here. First of all, you register a class-based view with .as_view()
which essentially wraps the class into a function that handles requests, so:
path('edit-booking/<int:id>', views.EditBooking.as_view(), name='editbooking'),
Likely you now get an error about the template_name
and the model not being specified, which makes sense: you didn't define it, and tried to define it in the constructor. Your view also looks a lot like an UpdateView
[Django-doc], not that much a DetailView
. You can see an UpdateView
as a DetailView
, but with a form to allow editing. This will also populate the form already with the instance in the GET request, which probably makes it more convenient.
We can use the LoginRequiredMixin
[Django-doc]
and SuccessMessageMixin
[Django-doc] to check if the user has logged in and also already encode a message when the item was successful:
What makes not much sense is the .customer_data = customer_data
: you don't store that in your model object. You can set the .booking_customer
with the UserProfile
of the logged in user, but that depends on the UserProfile
model, so you probably have to revise the form_valid()
method overload.
Finally a successful POST request normally results in a redirect to some path, you set that by the success_url
:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic import UpdateView
class EditBooking(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
model = Booking
form_class = BookingForm
template_name = 'edit-profile.html'
success_message = 'Thank you! Your booking has been updated!'
success_url = '/path/to/some/view'
# no __init__
def form_valid(self, form):
form.instance.booking_customer = UserProfile.objects.get(
user=self.request.user
)
return super().form_valid(form)
Answered By - willeM_ Van Onsem
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.