I have two models connected with the foreign key with each other as below :
class Service(models.Model):
title = models.CharField(max_length=100)
counter = models.PositiveIntegerField()
def __str__(self) -> str:
return f'{self.title}'
class Reservation(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
service = models.ForeignKey(Service, on_delete=models.CASCADE)
turn = models.GeneratedField(expression='',
output_field=models.CharField(max_length=1),
db_persist=True,)
def __str__(self) -> str:
return f'{self.user}'
What I want to do is that turn
field in Reservation
get option choices
and choice values are from 1 to service.counter
. service
is the foreign key here. for example if counter
in Service
model is 5 the turn
field in Reservation
would be something like this :
turn = models.PositiveIntegerField(choices=[1,2,3,4,5])
. A for loop won't work obviously :) .
Should I create a property
field or Meta class? How can I change my field like this?
I am seeking a better approach to solve this. I've changed turn
field into a Generated field so we make a query and assert it to the database. How can I do that?
Option 1: Only Service
exists already and you want to create a complete new Reservation
and let the user choose connected Service
. The answer to this Option is most likely not found within django, the backend, but has to be handled within the frontend. Reason being that it requires to switch possible choices for turn
on the fly when the user selects a different connected Service
.
Option 2: An initial Reservation
-Object with connected Service
already exists when the user is asked to select turn
. This can be done by django. Not on the models.py level though. This logic has to go to forms.py. We need a model form, that only needs the turn
field populated.
class ReservationForm(forms.ModelForm):
class Meta:
model = Reservation
fields = ['turn']
widgets = {
'turn': forms.Select(), # Set the widget for the 'turn' field to Select
}
def __init__(self, *args, **kwargs):
service_instance = kwargs.pop('service_instance', None)
super(ReservationForm, self).__init__(*args, **kwargs)
if service_instance:
# Set the choices for the 'turn' field based on the service instance's counter
self.fields['turn'].choices = [(i, i) for i in range(service_instance.counter + 1)]
else:
# If no service instance is provided, default to an empty list
self.fields['turn'].choices = []
# views.py
service_instance = Service.objects.get(id=service_id)
form = ReservationForm(service_instance=service_instance)
Above shows how to do dynamic forms. Basically you are selecting up front in the view what you want to pass to the form. Then within the __init__
method you capture that and set it accordingly.
Since you said that this form is always already bound to an object and therefore is connected to a service already you can also go for this option:
class ReservationForm(forms.ModelForm):
class Meta:
model = Reservation
fields = ['turn']
widgets = {
'turn': forms.Select(), # Set the widget for the 'turn' field to Select
}
def __init__(self, *args, **kwargs):
super(ReservationForm, self).__init__(*args, **kwargs)
# next line checks if an object is already connected to the form and a service connected
service_id = self.data.get('service') if self.is_bound else None
if service_id:
try:
service_instance = Service.objects.get(id=service_id)
# Set the choices for the 'turn' field based on the service instance's counter
self.fields['turn'].choices = [(i, i) for i in range(service_instance.counter + 1)]
except Service.DoesNotExist:
# If the service does not exist, default to an empty list
self.fields['turn'].choices = []
else:
# If no service ID is provided, default to an empty list
self.fields['turn'].choices = []
# views.py
# here you need to pass an instance to the form
form = ReservationForm(request.POST, instance=Reservation.objects.get(id=1)