0

I'm trying to create reservation create view that allows to break down the reservation process into 3 stages for handling all the database queries and displaying relevant choices accordingly to linkage between models (Personnel Profile has many to many key on Service) 1st stage - allows user to select service 2nd stage - displays available staff members (PersonnelProfile) who are handling this service 3rd stage - displays all the free dates / times based on staff member schedule / existing reservations, POST creates the reservation I got stuck at the 2nd stage as my form doesn't validate. I would appreciate any advise how to overcome this issue or how to handle the idea differently. Sorry for the long post :)

UPDATE

My goal is following:

1. User selects a service

2. Backend checks which personnel profiles are offering this service and returns from to select relevant user

3. Based on selected user backend checks availability (Schedule) of user, his existing reservations and returns form for date / hour selection

4. Reservation is created after user submits from with selected hour and time

I tried to do it with additional forms to split the process in stages, as I have no idea (unfortunately I'm still a newbie) how to handle such behaviour with one form and if it's possible or not. I would appreciate any tips / guidance on how to handle this.

Reservation

class Reservation(models.Model):
user = models.ForeignKey(PersonnelProfile, on_delete=models.PROTECT)
client = models.ForeignKey(ClientProfile, on_delete=models.PROTECT)
service = models.ForeignKey(Service, on_delete=models.PROTECT)
date = models.DateField()
start_time = models.TimeField()

Service

class Service(models.Model):
name = models.CharField(max_length=100)
description = models.TextField()
price = models.IntegerField()
duration = models.IntegerField()

def __str__(self):
    return self.name

PersonnelProfile

class PersonnelProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
birth_date = models.DateField(default=None, null=True)
address = models.CharField(max_length=200)
services = models.ManyToManyField(Service)
image = models.ImageField(default='profile_pics/default.jpg', upload_to='profile_pics')

def __str__(self):
    return f'{self.user.first_name} {self.user.last_name}'

Schedule Model

class Schedule(models.Model):
user = models.OneToOneField(PersonnelProfile, on_delete=models.CASCADE)
availability_days = models.ManyToManyField(WorkDay)
start_hour = models.TimeField()
end_hour = models.TimeField()

def __str__(self):
    return f'Schedule of {self.user.user.first_name} {self.user.user.last_name}'

WorkDay Model

class WorkDay(models.Model):
day_choices = [(str(i), calendar.day_name[i]) for i in range(7)]
name = models.CharField(max_length=9, choices=day_choices, default='1', unique=True)

def __str__(self):
    return self.get_name_display()

Reservation CreateView

class ReservationCreate(LoginRequiredMixin, UserPassesTestMixin, ModelFormWidgetMixin, CreateView):
model = Reservation
success_url = '/reservations'

@staticmethod
def get(request):
    form = ServiceSelection()
    return render(request, 'reservations/reservation_service_selection.html', context={'form': form})

@staticmethod
def post(request):
    if 'service_selection' in request.POST:
        form = ServiceSelection(request.POST)
        if form.is_valid():
            service = form.cleaned_data['select_service']
            available_personnel = PersonnelProfile.objects.filter(services=service)
            personnel_names = [prs.user for prs in available_personnel]
            form = PersonSelection(personnel_names)
            context = {
                'form': form,
                'service': service
            }
            """
            Selected service data should be saved for the 3rd stage
            """
            return render(request, 'reservations/reservation_person_selection.html', context)
        return HttpResponse('Service incorrect', 404)

    if 'person_selection' in request.POST:
        form = PersonSelection(request.POST)
        if form.is_valid():

            """
            Check the schedule and existing reservations
            Return last form with available dates and times for service selected in stage 1 
            and staff member selected in 3rd stage.
            3rd stage will create Reservation with all the selected details in all 3 stages
            """

            return HttpResponse('Good choice', 200) #response just for tests
        return render(request, 'reservations/reservation_person_selection.html', {'form': form})

def test_func(self):
    if self.request.user.is_staff:
        return True
    return False

Forms

class ServiceSelection(forms.ModelForm):
select_service = forms.ModelChoiceField(
    queryset=Service.objects.all(),
    widget=forms.Select(),
    empty_label='Please select a service',
    to_field_name='price'


)

class Meta:
    model = Service
    fields = ('select_service',)


class PersonSelection(forms.ModelForm):

    def __init__(self, personnel_names, *args, **kwargs):
        super(PersonSelection, self).__init__(*args, **kwargs)
        self.fields['user'] = forms.ChoiceField(
            choices=tuple([(name, name) for name in personnel_names]),
            label='Select a person'
        )

    class Meta:
        model = PersonnelProfile
        fields = 'user',

Templates : reservation_service_selection

{% extends 'website/home.html' %}
{% load crispy_forms_tags %}
{% block content %}
    <div class="content-section">
        <div class="media">
            <img class="rounded-circle account-img" src="/media/profile_pics/default.jpg">
            <div class="media-body">
                <h2 class="account-heading">Select a service</h2>
            </div>
        </div>
    </div>
    <div class="content-section">
        <form method="POST" enctype="multipart/form-data">
            {% csrf_token %}
            <fieldset class="form-group">
                {{ form|crispy }}
            </fieldset>
            <div class="form-group">
                <button class="btn btn-outline-info" type="submit" name="service_selection">Update</button>
                <input type="button" value="Go back" class="btn btn-outline-info" onclick="history.back()">
            </div>
        </form>
    </div>

{% endblock %}

reservation_person_selection

{% extends 'website/home.html' %}
{% load crispy_forms_tags %}
{% block content %}
    <div class="content-section">
        <div class="media">
            <img class="rounded-circle account-img" src="/media/profile_pics/default.jpg">
            <div class="media-body">
                <h2 class="account-heading">Select a person</h2>
            </div>
        </div>
    </div>
    <div class="content-section">
        <form method="POST" enctype="multipart/form-data">
            {% csrf_token %}
            {% if form.errors %}
                {% for field in form %}
                    {% for error in field.errors %}
                        <div class="alert alert-danger">
                            <strong>{{ error|escape }}</strong>
                        </div>
                    {% endfor %}
                {% endfor %}
                {% for error in form.non_field_errors %}
                    <div class="alert alert-danger">
                        <strong>{{ error|escape }}</strong>
                    </div>
                {% endfor %}
            {% endif %}
                <fieldset disabled>
                    <fieldset class="form-group">
                        <div class="form-group">
                            <label for="disabledSelect">Select a service*</label>
                             <select id="disabledSelect" class="form-control">
                                 <option>{{ service }}</option>
                            </select>
                        </div>
                    </fieldset>
                </fieldset>

                {{ person_form|crispy }}

                <div class="form-group">
                <button class="btn btn-outline-info" type="submit" name="person_selection">Update</button>
                <input type="button" value="Go back" class="btn btn-outline-info" onclick="history.back()">
                </div>
        </form>
    </div>
Mateusz
  • 1
  • 2
  • You have 3 stages with 3 different forms right ? why don't you use [formsets](https://docs.djangoproject.com/en/3.0/topics/forms/formsets/#formsets) specially [BaseInlineFormSet](https://docs.djangoproject.com/en/3.0/topics/forms/modelforms/#django.forms.models.BaseInlineFormSet) formsets will be great choice for handling such scenarios. You can refer my previous [answer](https://stackoverflow.com/a/60708034/8601641) for some hint. – k33da_the_bug Mar 23 '20 at 04:33
  • @k33da_lets_debug thank you for your reply. I was getting myself familiar with formsets and I still can't figure out how to use them in this scenario. Could you please help me a bit more ? I would also explain myself better in terms of the goal. (please see updated model of Reservation and goal of my code :) ) – Mateusz Mar 28 '20 at 12:20

0 Answers0