Changing Fields in Create View Based on Date

Hi All,

I’m trying to update which fields are editable in my create view based on what the date is that the object is created. I.e. - if it is created between two dates, I want additional items to be accessible. Here’s some of the model definition:

class Ride(models.Model):
    stravaURL = models.URLField("Strava URL",unique=True)
    user = models.ForeignKey(User,on_delete=models.CASCADE,null=True,default=None)
    ridedate = models.DateField("Date of Ride",default=timezone.now())
    titanium_twenty = models.ForeignKey(TitaniumTwenty,on_delete=models.SET_NULL,null=True,default=None)
    TTtrail1 = models.BooleanField("Counts for Trail #1",default=False)

And here’s what I attempted for the create view:

class NewRide(LoginRequiredMixin,generic.CreateView):

    model = Ride
    trailnames = "TTtrail1"
    fields = ["ridedate","stravaURL","titanium_twenty"] # i only include titanium_twenty as a hidden object in HTML
    success_url = "success"
    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs.update({'label_suffix': ''}) # remove the trailing colon from labels
        return kwargs

    def get_context_data(self, **kwargs):
        # here i'm trying to see if there is an active titaniumtwenty object for right now
        tempTT = TitaniumTwenty.objects.filter(start_date__lte=timezone.now()).filter(end_date__gte=timezone.now())

        if (len(tempTT)>0):
            # and here I try to change the fields
            self.fields = self.fields + self.trailnames

        context = super().get_context_data(**kwargs)

        if (len(tempTT)>0):
            context['form']['TTtrail1'].TTname=tempTT[0].trailname1
        return context

I find the code works great if I manage to post a ride, however if I get caught in the form.invalid for any reason (strava URL entry is duplicated for example) I run into a key error, because for some reason the form doesn’t have the TTtrail1 key.

Does anyone know why the code would work great when it is rendered from a get, and even from a successful post, but fail when re-rendering the form from an invalid entry?

Thanks,
Kyle

Hiya, it’s a bit unclear to me what you’re trying to achieve here with the code:

if (len(tempTT)>0):
    context['form']['TTtrail1'].TTname=tempTT[0].trailname1

Do you do something special in the template to use this value that you’re setting?

But in response to the general issue of “I want different fields available depending on X” I’d suggest splitting the form logic into a Django Form rather than having it on the view itself. It’s less brittle than trying to do your stuff in get_context_data. Something like:

class NewRide(forms.ModelForm):
    class Meta:
        model = Ride
        fields = ["ridedate", "stravaURL", "titanium_twenty", "TTtrail1"]

    def clean(self):
        data = super().clean()

        now = timezone.now()
        current_event = TitaniumTwenty.objects.filter(
            start_date__lte=now, end_date__gte=now,
        ).exists()

        if current_event is False:
            data.TTtrail1 = False

        return data

If you want to display different fields at different times, that can then be an issue for your template - I don’t know what you’re doing in your templates because you didn’t show it. Or, if you prefer, you can do this in your form’s __init__:

class NewRide(forms.ModelForm):
    class Meta:
        model = Ride
        fields = ["ridedate", "stravaURL", "titanium_twenty", "TTtrail1"]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        now = timezone.now()
        current_event = TitaniumTwenty.objects.filter(
            start_date__lte=now, end_date__gte=now,
        ).exists()

        if not current_event:
            del self.fields["TTtrail1"]

Hope that helps!

Thanks for the reply!

I ultimately decided to fix the issue on the client side using javascript, which I think makes the user experience better in the end.

Yes, the one

line was made to change a form label based on the date.