Monday, April 30, 2018

Re: Dynamically altering a (ForeignKey) ModelChoiceField’s values

If anyone is interested in a solution I found, it is at www.webforefront.com/django/formprocessing.html. Great documentation there, and you can read why it works, too.

Here are some code snippets used to test my situation.

In forms.py and in a unique form for update (not create) - JimtestUpdateForm:
    def __init__(self, *args, **kwargs):
        super(JimtestUpdateForm, self).__init__(*args, **kwargs) # invoke the default first
        initial_arguments = kwargs.get('initial', None)
        if initial_arguments:
            ci_id = initial_arguments.get('ci_id', None)
            if ci_id:
                assign_qs = Checkin.objects.values('choretime').filter(choretime_id__isnull=False)
                unassign_qs = ChoreTime.objects.exclude(pk__in=assign_qs)
                ci_qs = Checkin.objects.get(id=ci_id)
                ct_qs = ChoreTime.objects.filter(id=ci_qs.choretime_id)
                new_qs = unassign_qs | ct_qs # union unassigned with the client's single assignment
                self.fields['choretime'].queryset = new_qs # update the choices shown
                self.fields['choretime'].widget.choices = self.fields['choretime'].choices # update the widget (may not need this - just a hunch - not tested omitting it yet

In views.py under the CB dispatch method:
        if request.method == 'GET':    
form = JimtestUpdateForm(instance=checkin, initial={'ci_id': checkinid}) # pass in the value(s) needed in __init__()
...

That is pretty much it - simple if you have the proper documentation on how to initialize forms.

Jim

On Apr 27, 2018, at 11:39 AM, Jim Illback <subaru_87@hotmail.com> wrote:

Absolutely agree. The "instance" (your note) is for Checkin and it includes a ForeignKey field to ChoreTime, which is a selection (choice field). But, I want that choice field to be limited for the forms to be only what has been not assigned yet. That way the user on the page will know that any shown pick is available. Otherwise, there are over 100 choices and no guidance as to what is truly available or already filled.

I have to alter the query set for that field. On a CreateView, I have it working perfectly (it's shown below). However, on an UpdateView, the person's choice is one of the "unavailable". So, I'm trying to add just that one choice back into the query set before showing it on the form. Therefore, I need both the URL (to get the update's choice) and the pre-form display method override (to exclude assigned entries) where I can override the field's query set.

Thanks for the inputs, Matthew. I hope I've made it clearer. If you think there's a better overall approach, I'm all ears!
Jim

On Apr 27, 2018, at 11:01 AM, Matthew Pava <Matthew.Pava@iss.com> wrote:

That ID in the URL should be taken care of in the view, not the form.  You pass the object in as keyword instance to instantiate the form.  How are you defining your form in the view?
 
Typically, you would do something like this:
 
instance = CheckIn.objects.get(pk=self.kwargs.get('pk'))
form = CheckInForm(request.POST or None, instance=instance)
 
From: django-users@googlegroups.com [mailto:django-users@googlegroups.com] On Behalf Of Jim Illback
Sent: Friday, April 27, 2018 11:33 AM
To: Django users
Subject: Re: Dynamically altering a (ForeignKey) ModelChoiceField's values
 
I also need to have the specific ID specified in the URL, so that is the other part needed. I've successfully done your suggestion for CreateView. Now, I just can't seem to get UpdateView to work properly.
 
Thanks much!
Jim


On Apr 27, 2018, at 9:20 AM, Matthew Pava <Matthew.Pava@iss.com> wrote:
 
In the form's __init__ method, you'll have to modify the queryset of the field.
 
def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
                self.fields['choretime'].queryset |= ChoiceTime.objects.filter(pk=self.instance.pk)
 
I hope that helps!
 
From: django-users@googlegroups.com [mailto:django-users@googlegroups.com] On Behalf Of Jim Illback
Sent: Friday, April 27, 2018 11:16 AM
To: Django users
Subject: Re: Dynamically altering a (ForeignKey) ModelChoiceField's values
 
To make this easier, here is just a simple question. 
 
Does Django give a method that can be overridden while processing a form which has access to the incoming URL parameters and precedes the display of the form's fields, specifically being able to alter the fields's query set before it is displayed?
 
Thanks for any help to answer this question.
 
Jim Illback



On Apr 25, 2018, at 10:41 AM, Jim Illback <subaru_87@hotmail.com> wrote:
 
I wondered if anyone has had to alter the queryset behind a model form's foreign key field which presents as a model choice field?
Briefly, I have a client attribute table with the foreign key to a chore/time table. For an add of the client attribute table, I want to limit entries to unassigned chore/time combinations only. This works perfectly during my CreateView. Here are some extracts to show specifics:
Models.py:
class ChoreTime(models.Model):
chore = models.ForeignKey(Chore, on_delete=models.CASCADE)
time = models.ForeignKey(TimePeriod, on_delete=models.CASCADE)
priority = models.BooleanField(default=False)
class Checkin(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE)
choretime = models.ForeignKey(ChoreTime, on_delete=models.CASCADE)
Forms.py:
class CheckinForm(forms.ModelForm):
assigned_qs = Checkin.objects.values('choretime').filter(choretime_id__isnull=False)
choretime = forms.ModelChoiceField(queryset=ChoreTime.objects.exclude(pk__in=assigned_qs))
However, I cannot get the any design to work on an UpdateView form. Using the same form as above, the current value does not even show up – it is blank – because, of course, that entry is already assigned so is excluded.
What I'd like is to have the above exclusions BUT be able to also add in the single entry assigned to the client being updated – so the entry will show that specific assignment (as well as limiting any possible change options to unassigned values - just like on the Create).
The problems with various approaches I've tried are:

1.     Anything done before the form is fully assembled won't have the existing form's Checkin ID value (which is part of the URL, just BTW). This is needed to look up and add the existing entry. So, having a database view won't work without being able to communicate the existing person's assignment ID to the view. Similarly, using an override queryset on the form, like done above for the Create, needs that ID, too.

2.     If I do the queries in the class's GET method routine as ORM objects, I must use UNION (a union of the exclusions as above plus the existing update client's assignment). That union option keeps giving an error that one of the ORM querysets is not a queryset. However, they are both using "<class>.objects.filter…". It seems like complex queries don't work with the union command. If I use raw SQL, the query works but the assignment to the choretime field's queryset fails.

Does anyone have experience with this sort of behavior and be willing to give guidance?
 
-- 
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/F0B95720-C669-4CE5-912E-5167E3B89DFE%40hotmail.com.
For more options, visit https://groups.google.com/d/optout.
 
-- 
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/C42DA3B1-C882-4889-995B-49580840F539%40hotmail.com.
For more options, visit https://groups.google.com/d/optout.
 
-- 
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to 
django-users+unsubscribe@googlegroups.com.
To post to this group, send email to 
django-users@googlegroups.com.
Visit this group at 
https://groups.google.com/group/django-users.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-users/b73cf0c772af40969ec471acff9ec667%40ISS1.ISS.LOCAL.
For more options, visit 
https://groups.google.com/d/optout.
 
-- 
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/7B3E1F75-3393-45E1-9E69-2C73FC461F56%40hotmail.com.
For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/a61dfe45f4524aad8e73fcdb6b14b3b4%40ISS1.ISS.LOCAL.
For more options, visit https://groups.google.com/d/optout.


No comments:

Post a Comment