(Django 2.0, Python 3.6, Django Rest Framework 3.8)
I'm trying to fill the calendarydays
field in the model below:
Model
class Bookings(models.Model): booked_trainer = models.ForeignKey(TrainerProfile, on_delete=models.CASCADE) booked_client = models.ForeignKey(ClientProfile, on_delete=models.CASCADE) trainer_availability_only = models.ForeignKey(Availability, on_delete=models.CASCADE) calendarydays = models.CharField(max_length=300, blank=True, null=True) PENDING = 'PENDING' CONFIRMED = 'CONFIRMED' CANCELED = 'CANCELED' STATUS_CHOICES = ( (PENDING, 'Pending'), (CONFIRMED, 'Confirmed'), (CANCELED, 'Canceled') ) booked_status = models.CharField( max_length = 9, choices = STATUS_CHOICES, default = 'Pending' ) def __str__(self): return str(self.trainer_availability_only)
Now, I have a function that takes values from trainer_availability_only
and converts those values to a list of datetime strings, the returned output would look like this:
{'calendarydays': ['2018-07-23 01:00:00', '2018-07-23 02:00:00', '2018-07-23 03:00:00', '2018-07-30 01:00:00', '2018-07-30 02:00:00', '2018-07-30 03:00:00', '2018-08-06 01:00:00', '2018-08-06 02:00:00', '2018-08-06 03:00:00', '2018-08-13 01:00:00', '2018-08-13 02:00:00', '2018-08-13 03:00:00', '2018-08-20 01:00:00', '2018-08-20 02:00:00', '2018-08-20 03:00:00']}
Problem
How can I fill the calendarydays
field with the function output for a user to select from a dropdown, and where should I implement this logic (in my view or the serializer)? My main point of confusion is that, because my function depends on data from trainer_availability_only
, I don't want to create a separate model/table for this information (as that would seem too repetitive). I also don't fully understand where in my serializers or views I can implement some sort of dropdown for a User to choose a single calendarydays
value for (like I would be able to for a ForeignKey
or OneToOneField
for example).
Details for the other models aren't really relevant to the question, except trainer_availability_only
, which basically gives the user a dropdown selection that would look like this:
('Monday','12:00 am - 1:00 am') ('Wednesday','4:00 pm - 5:00 pm') etc.
Any help is greatly appreciated.
2 Answers
Answers 1
Well I don't have the exact answer for your question since I don't understand what you're trying to do, but this is what I can tell you about implementing logic on models:
Assuming you have this model:
class MyModel(models.Model): field1=... field2=... def foo(self): ### Put your logic here ### self.field1 = self.field2 + 5 def save(self, *args, **kwargs): self.foo() return super(MyModel, self).save(*args, **kwargs)
This method runs whenever your data gets saved (or updated I assume). Don't forget to validate your data before saving and don't put your logic on views or serializers, those are not good ideas. You can also create another module for your logic, but that's more complicated!
Answers 2
Unfortunately, with the built-in template engine for Django you cannot dynamically rerender parts of a template based on something from the backend. As I see it, you have two options:
- Request new options via an API in your application and update the HTML using JavaScript; or
- Generate all possible sets of options for the initial request and send them all at once and then just filter in the template
Option 1: in the template
First you would need to make an API endpoint for the Availability
model. It can be as simple as a single url route with a pattern like '/availability/(?P<pk>[0-9]+)$'
(ex: '/availability/5'
). This route should return as a JSON object the Availability
object with pk=pk
(5
in the example above). You can JSON serialize the object yourself if it is fairly simple, just convert it to a list or dictionary that contains simple Python types (e.g. str
, int
, bool
, list
, dict
). If this is going to be a larger application with more involved models or a few more API routes, it's worth checking out Django Rest Framework for building your API.
With that route working, you can call it with JavaScript from your template. You'll likely want to add an eventListener
on change to the <select>
or <input>
for trainer_availability_only
. So if the user changes the value of trainer_availability_only
, your template with make a request to your API route for the Availability
object specified in the form.
With the options returned by your API (calOptions
in the example below), you can populate the <option>
s in the <select>
for calendarydays
. You can do something like
const calSelect = document.querySelector('#select-calendarydays'); calSelect.options = []; // to clear the old options calOptions.forEach(calOption => { const optionElement = document.createElement('option'); optionElement.text = calOption.text; optionElement.value = calOption.value; calSelect.add(optionElement); }
The text
attribute is what displays to the user and the value
attribute is what actually gets submitted. Note that the above supposes that calOptions
is something of the form:
[ {'text': 'Monday 12:00 am - 1:00 am', 'value': '2018-07-23 00:00:00'}, {'text': 'Tuesday 8:00 am - 9:00 am', 'value': '2018-07-24 08:00:00'} ]
And that'll do it for this route.
Option 2: in the view
You can also do this in the view, but it isn't particularly scalable. What you would do is load all of the Availability
objects in the view and then pass a dictionary containing sets of options (presumably one set per Availability
object) and pass that to the template via the context argument.
That would look something like this:
option_sets = {} for availability in Availability.objects.all(): option_sets[availability.pk] = availability.get_calendarydays_options() ... context = {'option_sets': option_sets} return render(request, 'my_app/form-page.html', context)
Where availability.get_calendarydays_options
returns a list of options like calOptions
in the previous option.
Then in the template you need to get the option_set
from option_sets
that is for the selected Availability
object. Do do that see this answer: Django template how to look up a dictionary value with a variable because dictionary access doesn't work the same in the template engine as it does in Python. For populating the <option>
s in the <select>
, you can do something like:
<select id='select-calendarydays>' {% for cal_option in option_set %} <option value="{{ cal_option.value }}">{{ cal_option.text }}</option> {% endfor %} </select>
Why I say this is not very scalable is because it requires sending all of the possible sets of options for calendarydays
in the initial response. I recommend using Option 1, but the choice is yours.
This seems like a complex problem so I doubt this response will answer everything, but hopefully it sets you in the right direction a bit. Let me know if you have questions.
0 comments:
Post a Comment